/* flaneth - flash and ethernet
   Copyright (C) 2007-2012 Stefan Schuermans <stefan@blinkenarea.org>
   Copyleft: GNU public license V2 - http://www.gnu.org/copyleft/gpl.html
   a BlinkenArea project - http://www.blinkenarea.org/ */

#include <stdint.h>

#include "xtea.h"

#define delta 0x9E3779B9

// XTEA encrypt a block of 64 bits
// - this is not block XTEA, it's the basic XTEA algorithm
static void xtea_enc_block(uint32_t key[4], uint8_t rounds, uint32_t in[2],
                           uint32_t out[2])
{
  uint32_t y, z, sum;

  y = in[0];
  z = in[1];
  sum = 0;
  for (; rounds > 0; rounds--) {
    y += ((z << 4 ^ z >> 5) + z) ^ (sum + key[sum & 3]);
    sum += delta;
    z += ((y << 4 ^ y >> 5) + y) ^ (sum + key[sum >> 11 & 3]);
  }
  out[0] = y;
  out[1] = z;
}

// XTEA decrypt a block of 64 bits
// - this is not block XTEA, it's the basic XTEA algorithm
static void xtea_dec_block(uint32_t key[4], uint8_t rounds, uint32_t in[2],
                           uint32_t out[2])
{
  uint32_t y, z, sum;

  y = in[0];
  z = in[1];
  sum = (uint32_t) delta *rounds;
  for (; rounds > 0; rounds--) {
    z -= ((y << 4 ^ y >> 5) + y) ^ (sum + key[sum >> 11 & 3]);
    sum -= delta;
    y -= ((z << 4 ^ z >> 5) + z) ^ (sum + key[sum & 3]);
  }
  out[0] = y;
  out[1] = z;
}

// XTEA encryption
// rounds should be at least 32
// p_in == p_out is allowed
// p_prev must be initialized to zero before first call to this function
void xtea_enc(uint32_t key[4], uint8_t rounds, uint64_t * p_in, uint64_t * p_out, unsigned int block_cnt, uint64_t * p_prev)    // extern
{
  uint64_t in, out;
  unsigned int i;

  for (i = 0; i < block_cnt; i++) {
    in = p_in[i] ^ *p_prev;
    xtea_enc_block(key, rounds, (uint32_t *) & in, (uint32_t *) & out);
    p_out[i] = out;
    *p_prev = out;
  }
}

// XTEA decryption
// rounds should be at least 32
// p_in == p_out is allowed
// p_prev must be initialized to zero before first call to this function
void xtea_dec(uint32_t key[4], uint8_t rounds, uint64_t * p_in, uint64_t * p_out, unsigned int block_cnt, uint64_t * p_prev)    // extern
{
  uint64_t in, out;
  unsigned int i;

  for (i = 0; i < block_cnt; i++) {
    in = p_in[i];
    xtea_dec_block(key, rounds, (uint32_t *) & in, (uint32_t *) & out);
    p_out[i] = out ^ *p_prev;
    *p_prev = in;
  }
}
