001 //https://en.wikipedia.org/wiki/Salsa20#ChaCha_variant
002 I64 ROTL(U32 n,I64 b) {
003   n&=U32_MAX;
004   return U32_MAX&(n<<b|n>>(32-b));
005 }
006 U0 QR_ChaCha20(I32 *_a,I32 *_b,I32 *_c,I32 *_d) {
007   I64 a=*_a;
008   I64 b=*_b;
009   I64 c=*_c;
010   I64 d=*_d;
011   a+=b; d^=a; d=ROTL(d,16);
012   a&=U32_MAX;
013   c+=d; b^=c; b=ROTL(b,12);
014   b&=U32_MAX;
015   a+=b; d^=a; d=ROTL(d,8);
016   a&=U32_MAX;
017   c+=d; b^=c; b=ROTL(b,7);
018   c&=U32_MAX;
019   *_a=a;
020   *_b=b;
021   *_c=c;
022   *_d=d;
023   
024 }
025 U0 ChaCha20PrepareState(U32* out,U32 *key=NULL,I64 counter,U32 *nonce) {
026   static U32 const[4]={0x61707865,0x3320646e,0x79622d32,0x6b206574};
027   U32 i;
028   MemSetU32(out,0,16);
029   MemCpy(out,const,4*4);
030   for(i=0;i!=32/4;i++)
031     out[i+4]|=key[i];
032   for(i=0;i!=3;i++)
033     out[13+i]|=nonce[i];
034   out[12](U64)|=counter;
035 }
036 U0 ChaCha20Block(U32 *out,U32 *in) {
037   U32 tmp[16],i;
038   MemCpy(tmp,in,16*4);
039   I64 rounds=20;
040   while((rounds-=2)>=0) {
041 // Odd round
042     QR_ChaCha20(&tmp[0],&tmp[4],&tmp[ 8],&tmp[12]); // column 1
043     QR_ChaCha20(&tmp[1],&tmp[5],&tmp[ 9],&tmp[13]); // column 2
044     QR_ChaCha20(&tmp[2],&tmp[6],&tmp[10],&tmp[14]); // column 3
045     QR_ChaCha20(&tmp[3],&tmp[7],&tmp[11],&tmp[15]); // column 4
046 // Even round
047     QR_ChaCha20(&tmp[0],&tmp[5],&tmp[10],&tmp[15]); // diagonal 1 (main diagonal)
048     QR_ChaCha20(&tmp[1],&tmp[6],&tmp[11],&tmp[12]); // diagonal 2
049     QR_ChaCha20(&tmp[2],&tmp[7],&tmp[ 8],&tmp[13]); // diagonal 3
050     QR_ChaCha20(&tmp[3],&tmp[4],&tmp[ 9],&tmp[14]); // diagonal 4
051   }
052   for(i=0;i!=16;i++)
053     out[i]=tmp[i]+in[i];
054 }
055 U0 ChaCha20Block2(U32 *out,U32 *key=NULL,I64 counter,U32 *nonce) {
056   ChaCha20PrepareState(out,key,counter,nonce);
057   ChaCha20Block(out,out);
058 }
059 U32 *ChaCha20Enc(U32 *key,U32 *nonce,U8 *data,I64 len,I64 *_fsz=NULL) {
060   I64 final_len=CeilI64(len,64)*4*16;
061   I64 j;
062   U32 key_stream[16],block[16];
063   U32 *final=CAlloc(final_len+1);
064   I64 counter=1,i;
065   for(j=0;j!=len/64;j++) {
066     ChaCha20Block2(key_stream,key,1+j,nonce);
067     MemSetU32(block,0,16);
068     MemCpy(block,data+j*64,64);
069     for(i=0;i!=16;i++)
070       final[j*16+i]=block[i]^key_stream[i];
071   }
072   if(len%64) {
073     ChaCha20Block2(key_stream,key,1+j,nonce);
074     MemSetU32(block,0,16);
075     MemCpy(block,data+j*64,len-j*64);
076     for(i=0;i!=16;i++)
077       final[j*16+i]=block[i]^key_stream[i];
078   }
079   if(_fsz) *_fsz=len;
080   return final;
081 }
082 //https://www.rfc-editor.org/rfc/rfc7539
083 U0 TestQR () {
084 //2.1
085   U32 a=0x11111111;
086   U32 b=0x01020304;
087   U32 c=0x9b8d6f43;
088   U32 d=0x01234567;
089   QR_ChaCha20(&a,&b,&c,&d);
090   if(a!=0xea2a92f4) throw('TestA');
091   if(b!=0xcb1cf8ce) throw('TestB');
092   if(c!=0x4581472e) throw('TestC');
093   if(d!=0x5881c4bb) throw('TestD');
094 
095     //2.2.1
096   "2.2.21\n";
097   U8 *key="\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f";
098   U8 *nonce="\x00\x00\x00\x09\x00\x00\x00\x4a\x00\x00\x00\x00";
099   U32 state[16];
100   static U32 want[16]={
101   0x61707865  ,0x3320646e  ,0x79622d32  ,0x6b206574
102         ,0x03020100  ,0x07060504  ,0x0b0a0908  ,0x0f0e0d0c
103         ,0x13121110  ,0x17161514  ,0x1b1a1918  ,0x1f1e1d1c
104         ,0x00000001  ,0x09000000  ,0x4a000000,0x00000000
105         };
106   ChaCha20PrepareState(state,key,1,nonce);
107   for(a=0;a!=4;a++)   {
108     for(b=0;b!=4;b++) 
109       "%x,",state[a*4+b];
110     "\n";
111   }
112   if(MemCmp(want,state,16*4))
113     throw('TestStat');
114   ChaCha20Block(state,state);
115   static U32 want2[16]={
116   0xe4e7f110  ,0x15593bd1  ,0x1fdd0f50  ,0xc47120a3
117         ,0xc7f4d1c7  ,0x0368c033  ,0x9aaa2204  ,0x4e6cd4c3
118         ,0x466482d2  ,0x09aa9f07  ,0x05d7c214  ,0xa2028bd9
119         ,0xd19c12b5  ,0xb94e16de  ,0xe883d0cb  ,0x4e3c50a2
120         };
121   if(MemCmp(want2,state,16*4))
122     throw('TestBlok');
123 
124 //2.4.1
125   U8 *nonce2="\x00\x00\x00\x00\x00\x00\x00\x4a\x00\x00\x00\x00";
126   U8 *data="\x4c\x61\x64\x69\x65\x73\x20\x61\x6e\x64\x20\x47\x65\x6e\x74\x6c\x65\x6d\x65\x6e\x20\x6f\x66\x20\x74\x68\x65\x20\x63\x6c\x61\x73\x73\x20\x6f\x66\x20\x27\x39\x39\x3a\x20\x49\x66\x20\x49\x20\x63\x6f\x75\x6c\x64\x20\x6f\x66\x66\x65\x72\x20\x79\x6f\x75\x20\x6f\x6e\x6c\x79\x20\x6f\x6e\x65\x20\x74\x69\x70\x20\x66\x6f\x72\x20\x74\x68\x65\x20\x66\x75\x74\x75\x72\x65\x2c\x20\x73\x75\x6e\x73\x63\x72\x65\x65\x6e\x20\x77\x6f\x75\x6c\x64\x20\x62\x65\x20\x69\x74\x2e";
127   I64 enc_sz;
128   U8 *enc_data=ChaCha20Enc(key,nonce2,data,StrLen(data),&enc_sz);
129   "ENC:%d\n",enc_sz;
130   for(a=0;a!=enc_sz;a++) {
131     "%x,",enc_data[a];
132   }
133 "\n";
134   "%s\n",ChaCha20Enc(key,nonce2,enc_data,enc_sz);
135   
136 }
137 #if __CMD_LINE__
138 TestQR;
139 #endif