From c095528f4079777730f771c3197264e2d2d6b4a0 Mon Sep 17 00:00:00 2001 From: Loup Vaillant Date: Thu, 12 Jan 2017 18:53:58 +0100 Subject: [PATCH] authenticated encryption, renames, Xchacha bugfix --- ae.c | 64 +++++++++++++++++++++++++++++ ae.h | 48 ++++++++++++++++++++++ argon2i.c | 29 ++++++++++---- argon2i.h | 23 ++++++++++- blake2b.c | 14 +++---- blake2b.h | 2 +- build.sh | 5 ++- chacha20.c | 33 +++++++-------- chacha20.h | 24 +++++------ poly1305.c | 6 +-- poly1305.h | 2 +- test.c | 84 +++++++++++++++++++-------------------- vectors_ietf_chacha20.txt | 4 -- 13 files changed, 237 insertions(+), 101 deletions(-) create mode 100644 ae.c create mode 100644 ae.h delete mode 100644 vectors_ietf_chacha20.txt diff --git a/ae.c b/ae.c new file mode 100644 index 0000000..2313e28 --- /dev/null +++ b/ae.c @@ -0,0 +1,64 @@ +#include "ae.h" +#include "chacha20.h" +#include "poly1305.h" + +void +crypto_ae_lock_detached(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t text_size, + uint8_t mac[16]) +{ + crypto_chacha_ctx e_ctx; + uint8_t auth_key[32]; + crypto_chacha20_Xinit (&e_ctx, key, nonce); + crypto_chacha20_random(&e_ctx, auth_key, 32); + + crypto_chacha20_encrypt(&e_ctx, plaintext, ciphertext, text_size); + crypto_poly1305_auth(mac, ciphertext, text_size, auth_key); +} + +int +crypto_ae_unlock_detached(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t text_size, + const uint8_t mac[16]) +{ + crypto_chacha_ctx e_ctx; + uint8_t auth_key[32]; + crypto_chacha20_Xinit (&e_ctx, key, nonce); + crypto_chacha20_random(&e_ctx, auth_key, 32); + + uint8_t real_mac[16]; + crypto_poly1305_auth(real_mac, ciphertext, text_size, auth_key); + + if (crypto_poly1305_verify(real_mac, mac)) + return -1; + + crypto_chacha20_encrypt(&e_ctx, ciphertext, plaintext, text_size); + return 0; +} + +void +crypto_ae_lock(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + size_t text_size, + uint8_t *box) +{ + crypto_ae_lock_detached(key, nonce, plaintext, box + 16, text_size, box); +} + +int +crypto_ae_unlock(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *box, + size_t text_size, + uint8_t *plaintext) +{ + return crypto_ae_unlock_detached(key, nonce, box + 16, + plaintext, text_size, box); +} diff --git a/ae.h b/ae.h new file mode 100644 index 0000000..8224824 --- /dev/null +++ b/ae.h @@ -0,0 +1,48 @@ +#ifndef AE_H +#define AE_H + +#include +#include + + +// Authenticated encryption with XChacha20 and Poly1305. +void +crypto_ae_lock_detached(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + uint8_t *ciphertext, + size_t text_size, + uint8_t mac[16]); + +// Authenticated encryption with XChacha20 and Poly1305. +// Returns -1 and has no effect if the message is forged. +int +crypto_ae_unlock_detached(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *ciphertext, + uint8_t *plaintext, + size_t text_size, + const uint8_t mac[16]); + +// Like the above, only puts the mac and the ciphertext together +// in a "box", mac first +void +crypto_ae_lock(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *plaintext, + size_t text_size, + uint8_t *box); // text_size + 16 + +// Unlocks a box locked by aead_lock() +int +crypto_ae_unlock(const uint8_t key[32], + const uint8_t nonce[24], + const uint8_t *box, // text_size + 16 + size_t text_size, + uint8_t *plaintext); + + + + + +#endif // AE_H diff --git a/argon2i.c b/argon2i.c index bbaefba..771956e 100644 --- a/argon2i.c +++ b/argon2i.c @@ -135,7 +135,7 @@ extended_hash(uint8_t *digest, uint32_t digest_size, in += 32; out += 32; } - crypto_general_blake2b(digest + out, digest_size - (32 * r), + crypto_blake2b_general(digest + out, digest_size - (32 * r), 0, 0, // no key digest + in , 64); } @@ -146,11 +146,11 @@ static void g_rounds(block *work_block) { #define LSB(x) ((x) & 0xffffffff) -#define G(a, b, c, d) \ - a += b + 2 * LSB(a) * LSB(b); d = rotr64(d ^ a, 32); \ - c += d + 2 * LSB(c) * LSB(d); b = rotr64(b ^ c, 24); \ - a += b + 2 * LSB(a) * LSB(b); d = rotr64(d ^ a, 16); \ - c += d + 2 * LSB(c) * LSB(d); b = rotr64(b ^ c, 63) +#define G(a, b, c, d) \ + a += b + 2 * LSB(a) * LSB(b); d ^= a; d = rotr64(d, 32); \ + c += d + 2 * LSB(c) * LSB(d); b ^= c; b = rotr64(b, 24); \ + a += b + 2 * LSB(a) * LSB(b); d ^= a; d = rotr64(d, 16); \ + c += d + 2 * LSB(c) * LSB(d); b ^= c; b = rotr64(b, 63) #define ROUND(v0, v1, v2, v3, v4, v5, v6, v7, \ v8, v9, v10, v11, v12, v13, v14, v15) \ G(v0, v4, v8, v12); G(v1, v5, v9, v13); \ @@ -301,7 +301,7 @@ gidx_next(gidx_ctx *ctx) // Main algorithm void -crypto_Argon2i_hash(uint8_t *tag, uint32_t tag_size, +crypto_argon2i_hash(uint8_t *tag, uint32_t tag_size, const uint8_t *password, uint32_t password_size, const uint8_t *salt, uint32_t salt_size, const uint8_t *key, uint32_t key_size, @@ -386,3 +386,18 @@ crypto_Argon2i_hash(uint8_t *tag, uint32_t tag_size, store_block(final_block, blocks + (nb_blocks - 1)); extended_hash(tag, tag_size, final_block, 1024); } + +void +crypto_argon2i(uint8_t tag[32], + const uint8_t *password, uint32_t password_size, + const uint8_t *salt, uint32_t salt_size, + void *work_area, + uint32_t nb_blocks, + uint32_t nb_iterations) +{ + crypto_argon2i_hash(tag , 32, + password, password_size, + salt , salt_size, + 0, 0, 0, 0, + work_area, nb_blocks, nb_iterations); +} diff --git a/argon2i.h b/argon2i.h index fd46b20..b7babf2 100644 --- a/argon2i.h +++ b/argon2i.h @@ -6,8 +6,21 @@ // Implements argon2i, with degree of paralelism 1, // because it's good enough, and threads are scary. +// +// key and ad are optionnal. They can be NULL if their respective size is 0. +// work_area is a pointer to a contiguous chunk of memory of at least +// nb_blocks * 1024 bytes. It must be suitably aligned for 64-bit words. +// Don't worry too much about alignment, malloc()'s results work. +// +// Choice of parameters for password hashing: +// - If you need a key, use a 32 bytes one. +// - Do what you will with the ad. +// - Use a 32 bytes tag (to get a 256-bit key) +// - Put 128 bits of entropy in the salt. 16 random bytes work well. +// - Use all the memory you can get away with. +// - Use as much iterations as reasonable. No less than 10 passes if you can. void -crypto_Argon2i_hash(uint8_t *tag, uint32_t tag_size, // >= 4 +crypto_argon2i_hash(uint8_t *tag, uint32_t tag_size, // >= 4 const uint8_t *password, uint32_t password_size, const uint8_t *salt, uint32_t salt_size, // >= 8 const uint8_t *key, uint32_t key_size, @@ -16,6 +29,14 @@ crypto_Argon2i_hash(uint8_t *tag, uint32_t tag_size, // >= 4 uint32_t nb_blocks, // >= 8 uint32_t nb_iterations); +// Convenience function. No key, no ad, 64 bytes tag +void +crypto_argon2i(uint8_t tag[32], + const uint8_t *password, uint32_t password_size, + const uint8_t *salt, uint32_t salt_size, // >= 8 + void *work_area, + uint32_t nb_blocks, // >= 8 + uint32_t nb_iterations); #endif // ARGON2I_H diff --git a/blake2b.c b/blake2b.c index 7a35200..58faa26 100644 --- a/blake2b.c +++ b/blake2b.c @@ -76,11 +76,11 @@ blake2b_compress(crypto_blake2b_ctx *ctx, _Bool last_block) // shuffle the work variables with the 12 rounds for (int i = 0; i < 12; i++) { -#define B2B_G(a, b, c, d, x, y) \ - v[a] += v[b] + x; v[d] = rotr64(v[d] ^ v[a], 32); \ - v[c] += v[d] ; v[b] = rotr64(v[b] ^ v[c], 24); \ - v[a] += v[b] + y; v[d] = rotr64(v[d] ^ v[a], 16); \ - v[c] += v[d] ; v[b] = rotr64(v[b] ^ v[c], 63) +#define B2B_G(a, b, c, d, x, y) \ + v[a] += v[b] + x; v[d] ^= v[a]; v[d] = rotr64(v[d], 32); \ + v[c] += v[d] ; v[b] ^= v[c]; v[b] = rotr64(v[b], 24); \ + v[a] += v[b] + y; v[d] ^= v[a]; v[d] = rotr64(v[d], 16); \ + v[c] += v[d] ; v[b] ^= v[c]; v[b] = rotr64(v[b], 63) B2B_G( 0, 4, 8, 12, m[sigma[i][ 0]], m[sigma[i][ 1]]); B2B_G( 1, 5, 9, 13, m[sigma[i][ 2]], m[sigma[i][ 3]]); @@ -158,7 +158,7 @@ crypto_blake2b_final(crypto_blake2b_ctx *ctx, uint8_t *out) } void -crypto_general_blake2b( uint8_t*out, size_t outlen, +crypto_blake2b_general( uint8_t*out, size_t outlen, const uint8_t*key, size_t keylen, const uint8_t*in, size_t inlen) { @@ -171,5 +171,5 @@ crypto_general_blake2b( uint8_t*out, size_t outlen, void crypto_blake2b(uint8_t *out, const uint8_t *in, size_t inlen) { - crypto_general_blake2b(out, 64, 0, 0, in, inlen); + crypto_blake2b_general(out, 64, 0, 0, in, inlen); } diff --git a/blake2b.h b/blake2b.h index 0b38dc8..25687f4 100644 --- a/blake2b.h +++ b/blake2b.h @@ -37,7 +37,7 @@ crypto_blake2b_final(crypto_blake2b_ctx *ctx, uint8_t *out); // All-in-one convenience function. // outlen, keylen, and key work the same as they do in the general_init function void -crypto_general_blake2b( uint8_t *out, size_t outlen, // digest +crypto_blake2b_general( uint8_t *out, size_t outlen, // digest const uint8_t *key, size_t keylen, // optional secret key const uint8_t *in , size_t inlen); // data to be hashed diff --git a/build.sh b/build.sh index ed13e02..6d4096a 100755 --- a/build.sh +++ b/build.sh @@ -1,13 +1,14 @@ #! /bin/bash CC="gcc" -CFLAGS="-O2 -Wall -Wextra -std=c11" +CFLAGS="-O2 -Wall -Wextra -std=c11 -g" $CC $CFLAGS -c chacha20.c $CC $CFLAGS -c blake2b.c $CC $CFLAGS -c poly1305.c $CC $CFLAGS -c argon2i.c +$CC $CFLAGS -c ae.c $CC $CFLAGS -c test.c -$CC $CFLAGS -o test test.o chacha20.o argon2i.o blake2b.o poly1305.o +$CC $CFLAGS -o test test.o chacha20.o argon2i.o blake2b.o poly1305.o ae.o $CC $CFLAGS -o speed_blake2b speed_blake2b.c blake2b.o diff --git a/chacha20.c b/chacha20.c index 342090d..b578685 100644 --- a/chacha20.c +++ b/chacha20.c @@ -87,7 +87,7 @@ init_nonce(crypto_chacha_ctx *ctx, const uint8_t nonce[8]) /// Exposed API /// /////////////////// void -crypto_init_chacha20(crypto_chacha_ctx *ctx, +crypto_chacha20_init(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[8]) { @@ -97,20 +97,7 @@ crypto_init_chacha20(crypto_chacha_ctx *ctx, } void -crypto_init_ietf_chacha20(crypto_chacha_ctx *ctx, - const uint8_t key[32], - const uint8_t nonce[12]) -{ - init_constant(ctx); - init_key(ctx, key); - ctx->input[12] = 0; - ctx->input[13] = load32_le(nonce ); - ctx->input[14] = load32_le(nonce + 4); - ctx->input[15] = load32_le(nonce + 8); -} - -void -crypto_init_Xchacha20(crypto_chacha_ctx *ctx, +crypto_chacha20_Xinit(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[24]) { @@ -121,9 +108,11 @@ crypto_init_Xchacha20(crypto_chacha_ctx *ctx, for (int i = 0; i < 4; i++) init_ctx.input[i + 12] = load32_le(nonce + i*4); - init_constant(ctx); + // chacha20 rounds (reversible if we reveal all the buffer) uint32_t buffer[16]; - chacha20_rounds(buffer, ctx->input); + chacha20_rounds(buffer, init_ctx.input); + + init_constant(ctx); // init key for (int i = 0; i < 4; i++) { ctx->input[i + 4] = buffer[i ]; // constant @@ -133,7 +122,7 @@ crypto_init_Xchacha20(crypto_chacha_ctx *ctx, } void -crypto_encrypt_chacha20(crypto_chacha_ctx *ctx, +crypto_chacha20_encrypt(crypto_chacha_ctx *ctx, const uint8_t *plain_text, uint8_t *cipher_text, size_t msg_length) @@ -159,3 +148,11 @@ crypto_encrypt_chacha20(crypto_chacha_ctx *ctx, ctx->pool_index++; } } + +void +crypto_chacha20_random(crypto_chacha_ctx *ctx, + uint8_t *cipher_text, + size_t msg_length) +{ + crypto_chacha20_encrypt(ctx, 0, cipher_text, msg_length); +} diff --git a/chacha20.h b/chacha20.h index 5ac197a..56e42f3 100644 --- a/chacha20.h +++ b/chacha20.h @@ -32,28 +32,17 @@ typedef struct crypto_chacha_ctx { // 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, +crypto_chacha20_init(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[8]); -// Initializes a chacha context, with a slightly bigger nonce (96 bits), -// barely enough to be selected at random (if in doubt, don't). -// -// The price you pay for this nonce is a smaller counter, which cannot -// handle messages biger than 128Gib. -// WARNING: ANY MESSAGE THAT EXCEEDS 128Gib WILL SPILL ITS SECRETS. -void -crypto_init_ietf_chacha20(crypto_chacha_ctx *ctx, - const uint8_t key[32], - const uint8_t nonce[12]); - // Initializes a chacha context, with an even bigger nonce (192 bits), // more than enough to be selected at random. // // The price you pay for that is a slower initialization. The security // guarantees are the same as regular initialization. void -crypto_init_Xchacha20(crypto_chacha_ctx *ctx, +crypto_chacha20_Xinit(crypto_chacha_ctx *ctx, const uint8_t key[32], const uint8_t nonce[24]); @@ -70,9 +59,16 @@ crypto_init_Xchacha20(crypto_chacha_ctx *ctx, // contain the raw chacha20 stream. Useful as a random number // generator. void -crypto_encrypt_chacha20(crypto_chacha_ctx *ctx, +crypto_chacha20_encrypt(crypto_chacha_ctx *ctx, const uint8_t *plain_text, uint8_t *cipher_text, size_t msg_length); +// convenience function. +// equivalent to encrypt_chacha20(ctx, 0, cipher_text, msg_length) +void +crypto_chacha20_random(crypto_chacha_ctx *ctx, + uint8_t *cipher_text, + size_t msg_length); + #endif // CHACHA20_H diff --git a/poly1305.c b/poly1305.c index 84f351e..8d279ce 100644 --- a/poly1305.c +++ b/poly1305.c @@ -241,11 +241,9 @@ crypto_poly1305_auth(uint8_t mac[16], int crypto_poly1305_verify(const uint8_t mac1[16], const uint8_t mac2[16]) { - size_t i; unsigned diff = 0; - for (i = 0; i < 16; i++) { + for (int i = 0; i < 16; i++) { diff |= (mac1[i] ^ mac2[i]); } - diff = (diff - 1) >> ((sizeof(unsigned) * 8) - 1); - return (diff & 1); + return diff; } diff --git a/poly1305.h b/poly1305.h index 243ed8a..b88ff1f 100644 --- a/poly1305.h +++ b/poly1305.h @@ -42,7 +42,7 @@ crypto_poly1305_auth(uint8_t mac[16], const uint8_t key[32]); // Constant time equality verification -// returns 1 if it matches, 0 otherwise. +// returns 0 if it matches, something else otherwise. int crypto_poly1305_verify(const uint8_t mac1[16], const uint8_t mac2[16]); diff --git a/test.c b/test.c index c540535..ed9e481 100644 --- a/test.c +++ b/test.c @@ -7,6 +7,7 @@ #include "blake2b.h" #include "poly1305.h" #include "argon2i.h" +#include "ae.h" ///////////////////////// /// General utilities /// @@ -173,8 +174,8 @@ test_chacha20(char* filename) vector out = vec_uninitialized(stream.size); crypto_chacha_ctx ctx; - crypto_init_chacha20(&ctx, key.buffer, nonce.buffer); - crypto_encrypt_chacha20(&ctx, 0, out.buffer, out.size); + crypto_chacha20_init(&ctx, key.buffer, nonce.buffer); + crypto_chacha20_random(&ctx, out.buffer, out.size); status |= vec_cmp(&out, &stream); vec_del(&out); @@ -187,36 +188,6 @@ test_chacha20(char* filename) return status; } -static int -test_ietf_chacha20(char* filename) -{ - int status = 0; - FILE *file = file_open(filename); - while (getc(file) != EOF) { - vector key = read_hex_line(file); - vector nonce = read_hex_line(file); - vector plain = read_hex_line(file); - vector cipher = read_hex_line(file); - vector out = vec_uninitialized(plain.size); - - uint8_t dump[64]; - crypto_chacha_ctx ctx; - crypto_init_ietf_chacha20(&ctx, key.buffer, nonce.buffer); - crypto_encrypt_chacha20(&ctx, 0, dump, 64); - crypto_encrypt_chacha20(&ctx, plain.buffer, out.buffer, out.size); - status |= vec_cmp(&out, &cipher); - - vec_del(&out); - vec_del(&cipher); - vec_del(&plain); - vec_del(&nonce); - vec_del(&key); - } - printf("%s: ietf_chacha20\n", status != 0 ? "FAILED" : "OK"); - fclose(file); - return status; -} - static int test_blake2b(char* filename) { @@ -228,7 +199,7 @@ test_blake2b(char* filename) vector hash = read_hex_line(file); vector out = vec_uninitialized(hash.size); - crypto_general_blake2b(out.buffer, hash.size, + crypto_blake2b_general(out.buffer, hash.size, key.buffer, key .size, in .buffer, in .size); @@ -285,7 +256,7 @@ test_argon2i(char *filename) void *work_area = malloc(nb_blocks.buffer[0] * 1024); - crypto_Argon2i_hash(out .buffer, out .size, + crypto_argon2i_hash(out .buffer, out .size, password.buffer, password.size, salt .buffer, salt .size, key .buffer, key .size, @@ -308,19 +279,48 @@ test_argon2i(char *filename) } printf("%s: argon2i\n", status != 0 ? "FAILED" : "OK"); fclose(file); - return 0; // return status; + return status; +} + +static int +test_ae() +{ + uint8_t key[32] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7 + }; + uint8_t nonce[24] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7 + }; + uint8_t plaintext[8] = { 0, 1, 2, 3, 4, 5, 6, 7 }; + uint8_t box[24] = { + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7 + }; + uint8_t out[8]; + + crypto_ae_lock(key, nonce, plaintext, 8, box); + int status = crypto_ae_unlock(key, nonce, box, 8, out); + + printf("%s: authenticated encryption\n", status != 0 ? "FAILED" : "OK"); + return status; } int main(void) { int status = 0; - status |= test_test_equal ("vectors_test_equal.txt" ); - status |= test_test_diff ("vectors_test_diff.txt" ); - status |= test_chacha20 ("vectors_chacha20.txt" ); - status |= test_ietf_chacha20("vectors_ietf_chacha20.txt"); - status |= test_blake2b ("vectors_blake2b.txt" ); - status |= test_poly1305 ("vectors_poly1305.txt" ); - status |= test_argon2i ("vectors_argon2i.txt" ); + status |= test_test_equal("vectors_test_equal.txt"); + status |= test_test_diff ("vectors_test_diff.txt" ); + status |= test_chacha20 ("vectors_chacha20.txt" ); + status |= test_blake2b ("vectors_blake2b.txt" ); + status |= test_poly1305 ("vectors_poly1305.txt" ); + status |= test_argon2i ("vectors_argon2i.txt" ); + status |= test_ae ( ); printf(status ? "TESTS FAILED\n" : "ALL TESTS OK\n"); return status; } diff --git a/vectors_ietf_chacha20.txt b/vectors_ietf_chacha20.txt deleted file mode 100644 index 06601d6..0000000 --- a/vectors_ietf_chacha20.txt +++ /dev/null @@ -1,4 +0,0 @@ -key: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f -nonce: 000000000000004a00000000 -text: 4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e -cipher: 6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d -- 2.47.3