DF-0220 / ref_keystream.c
/* * DF-0220 reference: compute the pre-reseed "degenerate" ChaCha20 keystream * that the csprng emits when reseed_cnt == 0. * * CRITICAL DETAIL: csprng_init() does bzero(&state->cipher_ctx, ...) and * chacha_keysetup() is NOT called before the first chacha_encrypt_bytes(). * Therefore the cipher context's full input[0..15] is ZERO -- including the * 4 sigma-constant words that chacha_keysetup() would normally write at * input[0..3]. So the keystream is NOT "chacha20(key=0^32, ctr=0)"; it is * the even-more-degenerate all-zero-state keystream. * * This program reproduces the kernel's exact transform (the same chacha.c * compiled with CHACHA_NONCE0_CTR128 + KEYSTREAM_ONLY, as subr_csprng.c does) * on the all-zero state, for 64 bytes, and prints it as hex. * * Build: cc -O2 -o ref_keystream ref_keystream.c * Run: ./ref_keystream [Nbytes=64] */ #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> typedef uint8_t u8; typedef uint32_t u32; #define U8V(v) ((u8)(v) & 0xFFu) #define U32V(v) ((u32)(v) & 0xFFFFFFFFu) #define ROTL32(v,n) (U32V((v)<<(n)) | ((v)>>(32-(n)))) #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 U32TO8_LITTLE(p,v) do{ (p)[0]=U8V((v)); (p)[1]=U8V((v)>>8); \ (p)[2]=U8V((v)>>16); (p)[3]=U8V((v)>>24); }while(0) #define QR(a,b,c,d) \ a=PLUS(a,b); d=ROTATE(XOR(d,a),16); \ c=PLUS(c,d); b=ROTATE(XOR(b,c),12); \ a=PLUS(a,b); d=ROTATE(XOR(d,a), 8); \ c=PLUS(c,d); b=ROTATE(XOR(b,c), 7); /* Reproduce the EXACT block transform from sys/crypto/chacha20/chacha.c * lines 149-241 (20-round chacha, no message XOR = KEYSTREAM_ONLY, 128-bit * counter increment via CHACHA_NONCE0_CTR128). */ static void chacha_block_zero(u32 in[16], u8 out[64]) { u32 x[16]; int i; memcpy(x, in, sizeof(x)); for (i = 20; i > 0; i -= 2) { QR(x[0],x[4],x[8], x[12]); QR(x[1],x[5],x[9], x[13]); QR(x[2],x[6],x[10],x[14]); QR(x[3],x[7],x[11],x[15]); QR(x[0],x[5],x[10],x[15]); QR(x[1],x[6],x[11],x[12]); QR(x[2],x[7],x[8], x[13]); QR(x[3],x[4],x[9], x[14]); } for (i = 0; i < 16; i++) { x[i] = PLUS(x[i], in[i]); U32TO8_LITTLE(out + 4*i, x[i]); } } int main(int argc, char **argv) { int nbytes = 64; if (argc > 1) nbytes = atoi(argv[1]); if (nbytes <= 0) nbytes = 64; /* All-zero state, exactly as csprng_init leaves cipher_ctx (and as * subr_csprng.c:155 chacha_encrypt_bytes runs on it pre-reseed). */ u32 in[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}; u8 blk[64]; int off = 0, i; printf("ref_preseed_keystream (%d bytes, all-zero chacha input[16]):", nbytes); while (off < nbytes) { chacha_block_zero(in, blk); for (i = 0; i < 64 && off < nbytes; i++, off++) { if (off % 16 == 0) printf("\n %04x:", off); printf(" %02x", blk[i]); } /* CHACHA_NONCE0_CTR128 128-bit counter increment (chacha.c:211-220) */ in[12] = PLUSONE(in[12]); if (!in[12]) { in[13]=PLUSONE(in[13]); if(!in[13]){ in[14]=PLUSONE(in[14]); if(!in[14]){ in[15]=PLUSONE(in[15]); } } } } printf("\n"); return 0; } |