Quantcast
Channel: Hacker News
Viewing all articles
Browse latest Browse all 25817

The design of Chacha20

$
0
0

{ title: The design of Chacha20 description: Chacha20 is a secure, fast, and amazingly simple encryption algorithm. Let's take a look. } January 2017 (Update: thanks Thomas Ptacek for [pointing out](https://news.ycombinator.com/reply?id=13365580) the origins of Chacha20.) The design of Chacha20 ====================== Chacha20 is a secure, fast, and amazingly simple encryption algorithm. It's author Daniel J. Bernstein explains it well in his [Salsa20][] and [Chacha20][] design papers (which I recommend), but did not dwell on details experts already know. Filling the gap took me a while. [Salsa20]: http://cr.yp.to/snuffle/design.pdf [Chacha20]: http://cr.yp.to/chacha/chacha-20080128.pdf Quick summary: Chacha20 is ARX-based hash function, keyed, running in counter mode. It embodies the idea that one can use a hash function to encrypt data. Stream ciphers -------------- While Chacha20 is mainly used for encryption, its core is a pseudo-random number generator. The cipher text is obtained by XOR'ing the plain text with a pseudo-random stream: cipher_text = plain_text XOR chacha_stream(key, nonce) Provided you never use the same nonce with the same key twice, you can treat that stream as a [one time pad][OTP]. This makes it very simple: unlike block ciphers, you don't have to worry about padding, and decryption is the same operation as encryption: [OTP]: https://en.wikipedia.org/wiki/One-time_pad (Wikipedia) plain_text = cipher_text XOR chacha_stream(key, nonce) Now we just have to get that stream. The quarter-round ----------------- Chacha20 works by scrambling the hell out of 512-bit blocks, using 80 applications of the _quarter-round_. The quarter round itself scrambles 4 32-bit numbers (that's a fourth of a block) thus: a += b; d ^= a; d > (32 - n)) #define QUARTERROUND(a, b, c, d) \ a += b; d ^= a; ROT_L32(d, 16); \ c += d; b ^= c; ROT_L32(b, 12); \ a += b; d ^= a; ROT_L32(d, 8); \ c += d; b ^= c; ROT_L32(b, 7) for (int i = 0; i #include // This is a chacha20 context. // To use safely, just follow these guidelines: // - Always initialize your context with one of the crypto_init_* functions below // - Dont't modify it, except through the crypto_*_chacha20 below. // - Never duplicate it. typedef struct crypto_chacha_ctx { uint32_t input[16]; // current input, unencrypted uint8_t random_pool[64]; // last input, encrypted uint8_t pool_index; // pointer to random_pool } crypto_chacha_ctx; // Initializes a chacha context. // // WARNING: DON'T USE THE SAME NONCE AND KEY TWICE // // You'd be exposing the XOR of subsequent encrypted // messages, thus destroying your confidentiality. // // WARNING: DON'T SELECT THE NONCE AT RANDOM // // If you encode enough messages with a random nonce, there's a good // chance some of them will use the same nonce by accident. 64 bits // just isn't enough for this. Use a counter instead. // // If there are multiple parties sending out messages, you can give them // all an initial nonce of 0, 1 .. n-1 respectively, and have them increment // their nonce by n. (Also make sure the nonces never wrap around.). void crypto_init_chacha20(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[8]); // Initializes a chacha context, with a bigger nonce (192 bits). // // It's slower than regular initialization, but that big nonce can now // be selected at random without fear of collision. No more complex, // stateful headache. void crypto_init_Xchacha20(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[24]); // Encrypts the plain_text by XORing it with a pseudo-random // stream of numbers, seeded by the provided chacha20 context. // It can safely be chained thus: // // crypto_encrypt_chacha20(ctx, plaint_0, cipher_0, length_0); // crypto_encrypt_chacha20(ctx, plaint_1, cipher_1, length_1); // crypto_encrypt_chacha20(ctx, plaint_2, cipher_2, length_2); // // plain_text is allowed to be null (0), in which case it will be // interpreted as an all zero input. The cipher_text will then // contain the raw chacha20 stream. Useful as a random number // generator. void crypto_encrypt_chacha20(crypto_chacha_ctx *ctx, const uint8_t *plain_text, uint8_t *cipher_text, size_t msg_length); #endif // CHACHA20_H ### chacha20.c #include "chacha20.h" ///////////////// /// Utilities /// ///////////////// static uint32_t load32_le(const uint8_t s[4]) { // Portable, slow way. // Only affects initialisation, though. return s[0] | (s[1] > 8) & 0xff; output[2] = (input >> 16) & 0xff; output[3] = (input >> 24) & 0xff; } //////////// /// Core /// //////////// static void chacha20_rounds(uint32_t out[16], const uint32_t in[16]) { for (int i = 0; i > (32 - n)) #define QUARTERROUND(a, b, c, d) \ a += b; d ^= a; ROT_L32(d, 16); \ c += d; b ^= c; ROT_L32(b, 12); \ a += b; d ^= a; ROT_L32(d, 8); \ c += d; b ^= c; ROT_L32(b, 7) QUARTERROUND(out[0], out[4], out[ 8], out[12]); // column 0 QUARTERROUND(out[1], out[5], out[ 9], out[13]); // column 1 QUARTERROUND(out[2], out[6], out[10], out[14]); // column 2 QUARTERROUND(out[3], out[7], out[11], out[15]); // column 3 QUARTERROUND(out[0], out[5], out[10], out[15]); // diagonal 1 QUARTERROUND(out[1], out[6], out[11], out[12]); // diagonal 2 QUARTERROUND(out[2], out[7], out[ 8], out[13]); // diagonal 3 QUARTERROUND(out[3], out[4], out[ 9], out[14]); // diagonal 4 } } ////////////////////////////// /// Context initialization /// ////////////////////////////// static void init_constant(crypto_chacha_ctx *ctx) { ctx->input[0] = load32_le((uint8_t*)"expa"); ctx->input[1] = load32_le((uint8_t*)"nd 3"); ctx->input[2] = load32_le((uint8_t*)"2-by"); ctx->input[3] = load32_le((uint8_t*)"te k"); } static void init_key(crypto_chacha_ctx *ctx, const uint8_t key[32]) { for (int i = 0; i input[i + 4] = load32_le(key + i*4); } static void init_nonce(crypto_chacha_ctx *ctx, const uint8_t nonce[8]) { ctx->input[12] = 0; // counter ctx->input[13] = 0; // counter ctx->input[14] = load32_le(nonce + 0); ctx->input[15] = load32_le(nonce + 4); } /////////////////// /// Exposed API /// /////////////////// void crypto_init_chacha20(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[8]) { init_constant(ctx ); init_key (ctx, key ); init_nonce (ctx, nonce); ctx->pool_index = 64; // the random pool starts empty } void crypto_init_Xchacha20(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[24]) { crypto_chacha_ctx init_ctx; init_constant (&init_ctx ); init_key (&init_ctx, key); // init big nonce for (int i = 0; i input); // init key for (int i = 0; i input[i + 4] = buffer[i ]; // constant ctx->input[i + 8] = buffer[i + 12]; // counter and nonce } init_nonce(ctx, nonce + 16); ctx->pool_index = 64; // the random pool starts empty } void crypto_encrypt_chacha20(crypto_chacha_ctx *ctx, const uint8_t *plain_text, uint8_t *cipher_text, size_t msg_length) { for (size_t i = 0; i pool_index == 64) { // fill the pool uint32_t buffer[16]; chacha20_rounds(buffer, ctx->input); for (int i = 0; i random_pool + i*4, buffer[i] + ctx->input[i]); // update the counters ctx->pool_index = 0; ctx->input[12]++; if (!ctx->input[12]) ctx->input[13]++; } // use the pool for encryption (or random stream) cipher_text[i] = (plain_text == 0 ? 0 : plain_text[i]) ^ ctx->random_pool[ctx->pool_index]; ctx->pool_index++; } }


Viewing all articles
Browse latest Browse all 25817

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>