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);
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);
//
// 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
}
//////////////////////////////
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);
}
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 );
// 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.
}
}
-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
// 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
#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__