text: /*
  chacha.c version $Date: 2014/11/12 15:27:52 $
  D. J. Bernstein
  Romain Dolbeau
  Public domain.
  */
  
  #include "e/ecrypt-sync.h"
  #include "api.h"
  
  #include <arm_neon.h>
  #include <stdio.h>
  #define ROUNDS 12
  
  #define ROTATE(v,c) (ROTL32(v,c))
  #define XOR(v,w) ((v) ^ (w))
  #define PLUS(v,w) (U32V((v) + (w)))
  #define PLUSONE(v) (PLUS((v),1))
  
  #define QUARTERROUND(a,b,c,d) \
    x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]),16); \
    x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]),12); \
    x[a] = PLUS(x[a],x[b]); x[d] = ROTATE(XOR(x[d],x[a]), 8); \
    x[c] = PLUS(x[c],x[d]); x[b] = ROTATE(XOR(x[b],x[c]), 7);
  
  static void salsa20_wordtobyte(u8 output[64],const u32 input[16])
  {
    u32 x[16];
    int i;
  
    for (i = 0;i < 16;++i) x[i] = input[i];
    for (i = ROUNDS;i > 0;i -= 2) {
      QUARTERROUND( 0, 4, 8,12)
      QUARTERROUND( 1, 5, 9,13)
      QUARTERROUND( 2, 6,10,14)
      QUARTERROUND( 3, 7,11,15)
      QUARTERROUND( 0, 5,10,15)
      QUARTERROUND( 1, 6,11,12)
      QUARTERROUND( 2, 7, 8,13)
      QUARTERROUND( 3, 4, 9,14)
    }
    for (i = 0;i < 16;++i) x[i] = PLUS(x[i],input[i]);
    for (i = 0;i < 16;++i) U32TO8_LITTLE(output + 4 * i,x[i]);
  }
  
  void ECRYPT_init(void)
  {
    return;
  }
  
  static const char sigma[16] = "expand 32-byte k";
  static const char tau[16] = "expand 16-byte k";
  
  void ECRYPT_keysetup(ECRYPT_ctx *x,const u8 *k,u32 kbits,u32 ivbits)
  {
    const char *constants;
  
    x->input[4] = U8TO32_LITTLE(k + 0);
    x->input[5] = U8TO32_LITTLE(k + 4);
    x->input[6] = U8TO32_LITTLE(k + 8);
    x->input[7] = U8TO32_LITTLE(k + 12);
    if (kbits == 256) { /* recommended */
      k += 16;
      constants = sigma;
    } else { /* kbits == 128 */
      constants = tau;
    }
    x->input[8] = U8TO32_LITTLE(k + 0);
    x->input[9] = U8TO32_LITTLE(k + 4);
    x->input[10] = U8TO32_LITTLE(k + 8);
    x->input[11] = U8TO32_LITTLE(k + 12);
    x->input[0] = U8TO32_LITTLE(constants + 0);
    x->input[1] = U8TO32_LITTLE(constants + 4);
    x->input[2] = U8TO32_LITTLE(constants + 8);
    x->input[3] = U8TO32_LITTLE(constants + 12);
  }
  
  void ECRYPT_ivsetup(ECRYPT_ctx *x,const u8 *iv)
  {
    x->input[12] = 0;
    x->input[13] = 0;
    x->input[14] = U8TO32_LITTLE(iv + 0);
    x->input[15] = U8TO32_LITTLE(iv + 4);
  }
  
  void ECRYPT_encrypt_bytes(ECRYPT_ctx *x_,const u8 *m,u8 *c_,u32 bytes)
  {
    u8 output[64];
    int i;
    u32* x = (u32*)&x_->input;
    u8* out = c_;
  
  #ifdef __MIPSEL__
  #if defined(__mips_msa)
  #include "u4_msa.h"
  #endif
  #endif
  
    if (!bytes) return;
    for (;;) {
      salsa20_wordtobyte(output,x);
      x[12] = PLUSONE(x[12]);
      if (!x[12]) {
        x[13] = PLUSONE(x[13]);
        /* stopping at 2^70 bytes per nonce is user's responsibility */
      }
      if (bytes <= 64) {
        for (i = 0;i < bytes;++i) out[i] = m[i] ^ output[i];
        return;
      }
      for (i = 0;i < 64;++i) out[i] = m[i] ^ output[i];
      bytes -= 64;
      out += 64;
      m += 64;
    }
  }
  
  void ECRYPT_decrypt_bytes(ECRYPT_ctx *x,const u8 *c,u8 *m,u32 bytes)
  {
    ECRYPT_encrypt_bytes(x,c,m,bytes);
  }
  
  void ECRYPT_keystream_bytes(ECRYPT_ctx *x,u8 *stream,u32 bytes)
  {
    u32 i;
    for (i = 0;i < bytes;++i) stream[i] = 0;
    ECRYPT_encrypt_bytes(x,stream,stream,bytes);
  }
  