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