--- /dev/null
+#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);
+}
--- /dev/null
+#ifndef AE_H
+#define AE_H
+
+#include <inttypes.h>
+#include <stddef.h>
+
+
+// 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
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);
}
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); \
// 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,
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);
+}
// 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,
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
// 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]]);
}
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)
{
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);
}
// 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
#! /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
/// 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])
{
}
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])
{
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
}
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)
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);
+}
// 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]);
// 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
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;
}
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]);
#include "blake2b.h"
#include "poly1305.h"
#include "argon2i.h"
+#include "ae.h"
/////////////////////////
/// General utilities ///
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);
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)
{
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);
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,
}
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;
}
+++ /dev/null
-key: 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
-nonce: 000000000000004a00000000
-text: 4c616469657320616e642047656e746c656d656e206f662074686520636c617373206f66202739393a204966204920636f756c64206f6666657220796f75206f6e6c79206f6e652074697020666f7220746865206675747572652c2073756e73637265656e20776f756c642062652069742e
-cipher: 6e2e359a2568f98041ba0728dd0d6981e97e7aec1d4360c20a27afccfd9fae0bf91b65c5524733ab8f593dabcd62b3571639d624e65152ab8f530c359f0861d807ca0dbf500d6a6156a38e088a22b65e52bc514d16ccf806818ce91ab77937365af90bbf74a35be6b40b8eedf2785e42874d