]> git.codecow.com Git - Monocypher.git/commitdiff
API overhaul
authorLoup Vaillant <loup@loup-vaillant.fr>
Sun, 4 Sep 2016 13:16:59 +0000 (15:16 +0200)
committerLoup Vaillant <loup@loup-vaillant.fr>
Sun, 4 Sep 2016 13:20:21 +0000 (15:20 +0200)
chacha20.c
chacha20.h

index 46e34cbdf62615307b0ef6120b06a806a0bfc4ed..cd488c462927358c4fac4dfd420ea7d22d9fe664 100644 (file)
@@ -137,8 +137,8 @@ chacha20_rounds(uint32_t output[16], const crypto_chacha_ctx *ctx)
     output[15] = x15;
 }
 
-static void
-chacha20_block(uint8_t output[64], const crypto_chacha_ctx *ctx)
+void
+crypto_block_chacha20(uint8_t output[64], crypto_chacha_ctx *ctx)
 {
     uint32_t buffer[16];
     chacha20_rounds(buffer, ctx);
@@ -172,13 +172,21 @@ chacha20_block(uint8_t output[64], const crypto_chacha_ctx *ctx)
         uint32_t sum = buffer[i] + ctx->input[i];
         store32_le(output + i*4, sum);
     }
+
+    // finally, we increment the counter
+    ctx->input[12]++;
+    if (!ctx->input[12])   // The counter is not secret, so this conditional
+        ctx->input[13]++;  // doesn't introduce any timing attack.
 }
 
-// This one is the same as chacha20_block, only it gives you only
+// This one is like crypto_block_chacha20, only it gives you only
 // half the output (256 bytes). It's basically the same as HSalsa20,
 // except build on ChaCha.  It is provably as secure as ChaCha20
+//
+// It's only used for XChacha20, so we just use it to initialize the key
+// space of an output context
 static void
-half_chacha20_block(uint32_t output[8], const crypto_chacha_ctx *ctx)
+init_Xkey(crypto_chacha_ctx *output, const crypto_chacha_ctx *ctx)
 {
     uint32_t buffer[16];
     chacha20_rounds(buffer, ctx);
@@ -196,8 +204,8 @@ half_chacha20_block(uint32_t output[8], const crypto_chacha_ctx *ctx)
     //
     // This lets us avoid a couple additional loads and additions,
     // for even moar speed.
-    memcpy(output, buffer     , sizeof(uint32_t) * 4);
-    memcpy(output, buffer + 12, sizeof(uint32_t) * 4);
+    memcpy(output->input + 4, buffer     , sizeof(uint32_t) * 4); // constant
+    memcpy(output->input + 8, buffer + 12, sizeof(uint32_t) * 4); // nonce, ctr
 }
 
 //////////////////////////////
@@ -238,16 +246,11 @@ init_key(crypto_chacha_ctx *ctx, const uint8_t key[32])
     ctx->input[11] = load32_le(key + 28);
 }
 
-static void
-init_ctr(crypto_chacha_ctx *ctx, uint64_t ctr)
-{
-    ctx->input[12] = (uint32_t) ctr;                   // LSB
-    ctx->input[13] = (uint32_t) (ctr >> (uint64_t)32); // MSB
-}
-
 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);
 }
@@ -261,33 +264,30 @@ init_big_nonce(crypto_chacha_ctx *ctx, const uint8_t nonce[16])
     ctx->input[15] = load32_le(nonce + 12);
 }
 
-static void
-init_chacha20(crypto_chacha_ctx *ctx,
-              const uint8_t      key[32],
-              const uint8_t      nonce[8],
-              uint64_t           ctr)
+///////////////////
+/// 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_ctr     (ctx, ctr  );
     init_nonce   (ctx, nonce);
 }
 
-// Initializes a chacha context, with a bigger nonce.
-//
-// It uses a cascade scheme where a first block is initialised with
-// the first 128 bits of the nounce (and no counter), and a second block
-// is initialised with a derived key from the first block, and the
-// last 64 bits of the nonce.
-//
-// It's slower than regular initialization, but that big nonce can now
-// be selected at random without fear of collision.
-static void
-init_Xchacha20(crypto_chacha_ctx *ctx,
-               const uint8_t      key[32],
-               const uint8_t      nonce[24],
-               uint64_t           ctr)
+void
+crypto_init_Xchacha20(crypto_chacha_ctx *ctx,
+                      const uint8_t      key[32],
+                      const uint8_t      nonce[24])
 {
+    // Chacha20 doesn't have enough nonce space to accomodate for a 192 bits
+    // nonce.  So we're using a cascade scheme, where a first block is
+    // initialised with the first 128 bits of the nounce (and no counter),
+    // and a second block is initialised with a derived key from the first
+    // block, and the last 64 bits of the nonce.
+
     // initialise a first block
     crypto_chacha_ctx init_ctx;
     init_constant (&init_ctx       );
@@ -296,25 +296,20 @@ init_Xchacha20(crypto_chacha_ctx *ctx,
 
     // set up the cascade
     init_constant(ctx            );
-    init_ctr     (ctx, ctr       );
+    init_Xkey    (ctx, &init_ctx );
     init_nonce   (ctx, nonce + 16);
-    half_chacha20_block(ctx->input + 5, &init_ctx); // init derived key
 }
 
-//////////////////
-/// Encryption ///
-//////////////////
-static void
-encrypt_chacha20(crypto_chacha_ctx *ctx,
-                 const uint8_t     *plain_text,
-                 uint8_t           *cipher_text,
-                 size_t             msg_length)
+void
+crypto_encrypt_chacha20(crypto_chacha_ctx *ctx,
+                        const uint8_t     *plain_text,
+                        uint8_t           *cipher_text,
+                        size_t             msg_length)
 {
     size_t remaining_bytes = msg_length;
     for (;;) {
         uint8_t random_block[64];
-        chacha20_block(random_block, ctx);
-        increment_counter(ctx); // the only modification of the context
+        crypto_block_chacha20(random_block, ctx);
 
         // XOR the last pseudo-random block with the input,
         // then end the loop.
@@ -333,73 +328,21 @@ encrypt_chacha20(crypto_chacha_ctx *ctx,
     }
 }
 
-void
-crypto_encrypt_chacha20(const uint8_t  key[32],
-                        const uint8_t  nonce[8],
-                        uint64_t       ctr,
-                        const uint8_t *plain_text,
-                        uint8_t       *cipher_text,
-                        size_t         msg_length)
-{
-    crypto_chacha_ctx ctx;
-    init_chacha20(&ctx, key, nonce, ctr);
-    encrypt_chacha20(&ctx, plain_text, cipher_text, msg_length);
-}
-
-
-void
-crypto_encrypt_Xchacha20(const uint8_t  key[32],
-                         const uint8_t  nonce[24],
-                         uint64_t       ctr,
-                         const uint8_t *plain_text,
-                         uint8_t       *cipher_text,
-                         size_t         msg_length)
-{
-    crypto_chacha_ctx ctx;
-    init_Xchacha20(&ctx, key, nonce, ctr);
-    encrypt_chacha20(&ctx, plain_text, cipher_text, msg_length);
-}
-
-void
-crypto_block_chacha20(const uint8_t key[32],
-                      const uint8_t nonce[8],
-                      uint64_t      ctr,
-                      uint8_t       output[64])
-{
-    crypto_chacha_ctx ctx;
-    init_chacha20(&ctx, key, nonce, ctr);
-    chacha20_block(output, &ctx);
-}
-
-void
-crypto_block_Xchacha20(const uint8_t key[32],
-                       const uint8_t nonce[24],
-                       uint64_t      ctr,
-                       uint8_t       output[64])
-{
-    crypto_chacha_ctx ctx;
-    init_Xchacha20(&ctx, key, nonce, ctr);
-    chacha20_block(output, &ctx);
-}
-
 ///////////////////////////////
 /// Random number generator ///
 ///////////////////////////////
 void
-crypto_init_rng(crypto_rng_context *ctx,
-                const uint8_t       key[32],
-                const uint8_t       nonce[8])
+crypto_init_rng(crypto_rng_context *ctx, const uint8_t key[32])
 {
-    init_chacha20(&ctx->chacha_ctx, key, nonce, 0);
+    // note how we allwas use the same nonce
+    crypto_init_chacha20(&ctx->chacha_ctx, key, (uint8_t*)"01234567");
     ctx->remaining_bytes = 0;
 }
 
 #define MIN(a, b) ((a) < (b) ? (a) : (b))
 
 void
-crypto_random_bytes(crypto_rng_context *ctx,
-                    uint8_t            *out,
-                    size_t              nb_bytes)
+crypto_random_bytes(crypto_rng_context *ctx, uint8_t *out, size_t nb_bytes)
 {
     // Consume any remaining byte from a previous
     // call to random_bytes
@@ -413,14 +356,14 @@ crypto_random_bytes(crypto_rng_context *ctx,
 
     // fill the output stream block by block
     while (nb_bytes >= 64) {
-        chacha20_block(out, &ctx->chacha_ctx);
+        crypto_block_chacha20(out, &ctx->chacha_ctx);
         increment_counter(&ctx->chacha_ctx);
         out      += 64;
         nb_bytes -= 64;
     }
 
     // Generate one last block and finish this
-    chacha20_block(ctx->reminder, &ctx->chacha_ctx); // there was no reminder
+    crypto_block_chacha20(ctx->reminder, &ctx->chacha_ctx); // no reminder
     increment_counter(&ctx->chacha_ctx);
     memcpy(out, ctx->reminder, nb_bytes); // those two lines work even
     ctx->remaining_bytes = 64 - nb_bytes; // when nb_bytes is already 0
index 4bd598f74f9ec4ae75a3ea2b9f5ed7c097b140c0..b57ec6ce1393513ed055f26dde9964ce24ed06b3 100644 (file)
@@ -4,89 +4,96 @@
 #include <inttypes.h>
 #include <stddef.h>
 
+// 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];
 } crypto_chacha_ctx;
 
-typedef struct crypto_rng_context {
-    crypto_chacha_ctx chacha_ctx;
-    uint8_t           reminder[64];
-    size_t            remaining_bytes;
-} crypto_rng_context;
 
-// Encrypts the plain_text by XORing it with a pseudo-random
-// stream of numbers, seeded by the key and the nonce.
-// NEVER ENCRYPT 2 DISTINCT MESSAGES WITH THE SAME KEY AND NONCE.
-// If you do, you will expose the XOR of the two messages, and your
-// confidentiality is toast.  One time pads are called one time pads
-// for a reason.
+// 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
 //
-// To decrypt a message, encrypt it again with the same key and nonce.
+// 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.
 //
-// key        : Your secret key.  Chose a (pseudo-)random one!
-// nonce      : Only once per key. Seriously, don't mess this up.
-// ctr        : Initial counter value. Typically 0 or 1
-// plain_text : Your precious secret message
-// cipher_text: Output buffer
-// msg_length : Length of plain_text and cipher_text
+// 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_encrypt_chacha20(const uint8_t  key[32],
-                        const uint8_t  nonce[8],
-                        uint64_t       ctr,
-                        const uint8_t *plain_text,
-                        uint8_t       *cipher_text,
-                        size_t         msg_length);
+crypto_init_chacha20(crypto_chacha_ctx *ctx,
+                     const uint8_t      key[32],
+                     const uint8_t      nonce[8]);
 
-// This one is very similar to encrypt_chacha20, except it provides
-// a nonce large enough to chose at random, withouth worrying about
-// collisions.  Handy for stateless protocols.
+// Initializes a chacha context, with a bigger nonce (192 bits).
 //
-// Streaming performance is the same, initialization is a bit slower.
+// 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_encrypt_Xchacha20(const uint8_t  key[32],
-                         const uint8_t  nonce[24],
-                         uint64_t       ctr,
-                         const uint8_t *plain_text,
-                         uint8_t       *cipher_text,
-                         size_t         msg_length);
+crypto_init_Xchacha20(crypto_chacha_ctx *ctx,
+                      const uint8_t      key[32],
+                      const uint8_t      nonce[24]);
 
-// Will look random as long as you never use the same input twice.
-// Can be used with encrypt_bytes as long as the counter here is smaller
-// than the encrypt_bytes counter, and the message is not long enough to
-// wrap around the counter.
-// Use 1 for the encrypt_bytes counter and 0 here, and you should be safe.
+// Outputs a single block from the provided context, then increments
+// the counter of that context by 1.  Can safely be called several
+// times over the same context like this:
+//    crypto_block_chacha20(output      , ctx);
+//    crypto_block_chacha20(output +  64, ctx);
+//    crypto_block_chacha20(output + 128, ctx);
+//    crypto_block_chacha20(output + 192, ctx);
+// Since che context will in fact have been changed at each call,
+// the resulting outputs will be completely different.
 void
-crypto_block_chacha20(const uint8_t key[32],
-                      const uint8_t nonce[8],
-                      uint64_t      ctr,
-                      uint8_t       output[64]);
+crypto_block_chacha20(uint8_t output[64], crypto_chacha_ctx *ctx);
 
-// Similar to block_chacha20, except for the big nonce and worse performance.
+// Encrypts the plain_text by XORing it with a pseudo-random
+// stream of numbers, seeded by the provided chacha20 contex.
+// It is built on top of crypto_chacha20_block, and can be safely
+// used with it, thus:
+//
+//    crypto_block_chacha20(output, ctx);
+//    crypto_encrypt_chacha20(ctx, plaint_t, cipher_t, length);
 void
-crypto_block_Xchacha20(const uint8_t key[32],
-                       const uint8_t nonce[24],
-                       uint64_t      ctr,
-                       uint8_t       output[64]);
+crypto_encrypt_chacha20(crypto_chacha_ctx *ctx,
+                        const uint8_t     *plain_text,
+                        uint8_t           *cipher_text,
+                        size_t             msg_length);
+
 
+////////////////////////////////////////////////////////////////////////////////
 
+// Experimental: random number generator
+
+typedef struct crypto_rng_context {
+    crypto_chacha_ctx chacha_ctx;
+    uint8_t           reminder[64];
+    size_t            remaining_bytes;
+} crypto_rng_context;
 
 // Inits a cryptographically secure Random Number Generator, with the
 // given key and nonce.  The output of that RNG will depend entirely
 // on the key and nonce.
-// NEVER USE THE SAME KEY AND NONCE FOR THIS AND MESSAGE ENCRYPTION.
+// NEVER USE THE SAME KEY FOR THIS AND MESSAGE ENCRYPTION.
 // If you do, you could leak the very key stream used to encrypt
 // your messages.  They'd be instantly deciphered.
 void
-crypto_init_rng(crypto_rng_context *ctx,
-                const uint8_t       key[32],
-                const uint8_t       nonce[8]);
+crypto_init_rng(crypto_rng_context *ctx, const uint8_t key[32]);
 
 // provides pseudo-random bytes, deterministically (the output and
 // the end state of ctx depends entirely on the initial state of ctx).
 // It's a chacha20 stream, really.
 void
-crypto_random_bytes(crypto_rng_context *ctx,
-                    uint8_t            *out,
-                    size_t              nb_bytes);
+crypto_random_bytes(crypto_rng_context *ctx, uint8_t *out, size_t nb_bytes);
 
 #endif // __CHACHA20__