Tests not complete, no documentation yet.
I'm currently rethinking the AEAD API as a whole, and to be honest I'm
so happy with this streaming API that I believe it could replace the
regular API entirely.
One problem with the AEAD API is the sheer number of arguments.
`crypto_lock_aead()` and `crypto_unlock_aead()` currently have 8
arguments, comprising 6 pointers (all of the same type) and 2 sizes.
There are way too many opportunities to swap arguments and break stuff.
The streaming API however is divided into an init phase, which has only
3 arguments, and a read/write phase, which has 7, but "only" 4 pointers
to byte buffers. Which I don't think we can improve much. We could try
and use a second struct similar to what we do with Argon2, but with only
7 arguments (compared to Argon2's 15) I don't think we would gain that
much readability.
As for how to use the streaming API for single shot uses, that's obvious
enough:
- Declare the context and call Init.
- Call read/write.
- Wipe the context.
One may argue that everything else (Poly1305, Blake2b, SHA-512, and
HMAC) provide a one-shot API, and we should do so here as well. There's
just one problem: we don't have one init function, we have _three_.
If we provide a one-shot API, orthogonality would need all 3 variants.
That's 6 functions total (3 locks, 3 unlocks), which is a bit much,
especially since at least one of them is only provided for compatibility
with a standard I don't entirely agree with. We could of course only
provide only the single one-shot API (like we do right now), but that
leaves such an obvious hole in the API.
Stopping at just the 5 functions we need for everything (streaming,
one-shot, all 3 variants) is very tempting.
See #218
crypto_poly1305_final (&poly_ctx, mac); // ...here
}
-void crypto_lock_aead(u8 mac[16], u8 *cipher_text,
- const u8 key[32], const u8 nonce[24],
- const u8 *ad , size_t ad_size,
- const u8 *plain_text, size_t text_size)
+void crypto_aead_init_x(crypto_aead_ctx *ctx,
+ u8 const key[32], const u8 nonce[24])
{
- u8 sub_key[32];
- u8 auth_key[64]; // "Wasting" the whole Chacha block is faster
- crypto_chacha20_h(sub_key, key, nonce);
- crypto_chacha20_djb(auth_key, 0, 64, sub_key, nonce + 16, 0);
+ crypto_chacha20_h(ctx->key, key, nonce);
+ COPY(ctx->nonce, nonce + 16, 8);
+ ctx->counter = 0;
+}
+
+void crypto_aead_init_djb(crypto_aead_ctx *ctx,
+ const u8 key[32], const u8 nonce[8])
+{
+ COPY(ctx->key , key , 32);
+ COPY(ctx->nonce, nonce, 8);
+ ctx->counter = 0;
+}
+
+void crypto_aead_init_ietf(crypto_aead_ctx *ctx,
+ const u8 key[32], const u8 nonce[12])
+{
+ COPY(ctx->key , key , 32);
+ COPY(ctx->nonce, nonce + 4, 8);
+ ctx->counter = (u64)load32_le(nonce) << 32;
+}
+
+void crypto_aead_write(crypto_aead_ctx *ctx, u8 *cipher_text, u8 mac[16],
+ const u8 *ad, size_t ad_size,
+ const u8 *plain_text, size_t text_size)
+{
+ u8 auth_key[64]; // the last 32 bytes are used for rekeying.
+ crypto_chacha20_djb(auth_key, 0, 64, ctx->key, ctx->nonce, ctx->counter);
crypto_chacha20_djb(cipher_text, plain_text, text_size,
- sub_key, nonce + 16, 1);
+ ctx->key, ctx->nonce, ctx->counter + 1);
lock_auth(mac, auth_key, ad, ad_size, cipher_text, text_size);
- WIPE_BUFFER(sub_key);
+ COPY(ctx->key, auth_key + 32, 32);
WIPE_BUFFER(auth_key);
}
-int crypto_unlock_aead(u8 *plain_text, const u8 key[32], const u8 nonce[24],
- const u8 mac[16],
- const u8 *ad , size_t ad_size,
- const u8 *cipher_text, size_t text_size)
+int crypto_aead_read(crypto_aead_ctx *ctx, u8 *plain_text, const u8 mac[16],
+ const u8 *ad, size_t ad_size,
+ const u8 *cipher_text, size_t text_size)
{
- u8 sub_key[32];
- u8 auth_key[64]; // "Wasting" the whole Chacha block is faster
- crypto_chacha20_h(sub_key, key, nonce);
- crypto_chacha20_djb(auth_key, 0, 64, sub_key, nonce + 16, 0);
+ u8 auth_key[64]; // the last 32 bytes are used for rekeying.
u8 real_mac[16];
+ crypto_chacha20_djb(auth_key, 0, 64, ctx->key, ctx->nonce, ctx->counter);
lock_auth(real_mac, auth_key, ad, ad_size, cipher_text, text_size);
- WIPE_BUFFER(auth_key);
int mismatch = crypto_verify16(mac, real_mac);
if (!mismatch) {
crypto_chacha20_djb(plain_text, cipher_text, text_size,
- sub_key, nonce + 16, 1);
+ ctx->key, ctx->nonce, ctx->counter + 1);
+ COPY(ctx->key, auth_key + 32, 32);
}
- WIPE_BUFFER(sub_key);
+ WIPE_BUFFER(auth_key);
WIPE_BUFFER(real_mac);
return mismatch;
}
-void crypto_lock(u8 mac[16], u8 *cipher_text,
- const u8 key[32], const u8 nonce[24],
- const u8 *plain_text, size_t text_size)
+void crypto_lock_aead(u8 mac[16], u8 *cipher_text, const u8 key[32],
+ const u8 nonce[24], const u8 *ad, size_t ad_size,
+ const u8 *plain_text, size_t text_size)
+{
+ crypto_aead_ctx ctx;
+ crypto_aead_init_x(&ctx, key, nonce);
+ crypto_aead_write(&ctx, cipher_text, mac, ad, ad_size,
+ plain_text, text_size);
+ crypto_wipe(&ctx, sizeof(ctx));
+}
+
+int crypto_unlock_aead(u8 *plain_text, const u8 key[32], const u8 nonce[24],
+ const u8 mac[16], const u8 *ad, size_t ad_size,
+ const u8 *cipher_text, size_t text_size)
+{
+ crypto_aead_ctx ctx;
+ crypto_aead_init_x(&ctx, key, nonce);
+ int mismatch = crypto_aead_read(&ctx, plain_text, mac, ad, ad_size,
+ cipher_text, text_size);
+ crypto_wipe(&ctx, sizeof(ctx));
+ return mismatch;
+}
+
+void crypto_lock(u8 mac[16], u8 *cipher_text, const u8 key[32],
+ const u8 nonce[24], const u8 *plain_text, size_t text_size)
{
crypto_lock_aead(mac, cipher_text, key, nonce, 0, 0, plain_text, text_size);
}
-int crypto_unlock(u8 *plain_text,
- const u8 key[32], const u8 nonce[24], const u8 mac[16],
- const u8 *cipher_text, size_t text_size)
+int crypto_unlock(u8 *plain_text, const u8 key[32], const u8 nonce[24],
+ const u8 mac[16], const u8 *cipher_text, size_t text_size)
{
return crypto_unlock_aead(plain_text, key, nonce, mac, 0, 0,
cipher_text, text_size);
const uint8_t *ad , size_t ad_size,
const uint8_t *cipher_text, size_t text_size);
+// Authenticated stream
+// --------------------
+typedef struct {
+ uint64_t counter;
+ uint8_t key[32];
+ uint8_t nonce[8];
+} crypto_aead_ctx;
+
+void crypto_aead_init_x(crypto_aead_ctx *ctx,
+ const uint8_t key[32], const uint8_t nonce[24]);
+void crypto_aead_init_djb(crypto_aead_ctx *ctx,
+ const uint8_t key[32], const uint8_t nonce[8]);
+void crypto_aead_init_ietf(crypto_aead_ctx *ctx,
+ const uint8_t key[32], const uint8_t nonce[12]);
+
+void crypto_aead_write(crypto_aead_ctx *ctx,
+ uint8_t *cipher_text,
+ uint8_t mac[16],
+ const uint8_t *ad , size_t ad_size,
+ const uint8_t *plain_text, size_t text_size);
+int crypto_aead_read(crypto_aead_ctx *ctx,
+ uint8_t *plain_text,
+ const uint8_t mac[16],
+ const uint8_t *ad , size_t ad_size,
+ const uint8_t *cipher_text, size_t text_size);
+
// General purpose hash (BLAKE2b)
// ------------------------------
--- /dev/null
+// This file is dual-licensed. Choose whichever licence you want from
+// the two licences listed below.
+//
+// The first licence is a regular 2-clause BSD licence. The second licence
+// is the CC-0 from Creative Commons. It is intended to release Monocypher
+// to the public domain. The BSD licence serves as a fallback option.
+//
+// SPDX-License-Identifier: BSD-2-Clause OR CC0-1.0
+//
+// ------------------------------------------------------------------------
+//
+// Copyright (c) 2022, Loup Vaillant
+// All rights reserved.
+//
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// 1. Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright
+// notice, this list of conditions and the following disclaimer in the
+// documentation and/or other materials provided with the
+// distribution.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+// ------------------------------------------------------------------------
+//
+// Written in 2022 by Loup Vaillant
+//
+// To the extent possible under law, the author(s) have dedicated all copyright
+// and related neighboring rights to this software to the public domain
+// worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication along
+// with this software. If not, see
+// <https://creativecommons.org/publicdomain/zero/1.0/>
+
+#include <sodium.h>
+#include "utils.h"
+
+static void test(size_t text_size, size_t ad_size)
+{
+ RANDOM_INPUT(key , 32);
+ RANDOM_INPUT(nonce, 12);
+ RANDOM_INPUT(ad , 32); // ad size <= 32
+ RANDOM_INPUT(text , 128); // text_size <= 128
+ u8 out[16 + 128]; // text_size <= 128
+
+ crypto_aead_chacha20poly1305_ietf_encrypt_detached(
+ out + 16, out, 0, text, text_size, ad, ad_size, 0, nonce, key);
+
+ print_vector(key , 32);
+ print_vector(nonce , 12);
+ print_vector(ad , ad_size);
+ print_vector(text , text_size);
+ print_vector(out , text_size + 16);
+ printf("\n");
+}
+
+int main(void)
+{
+ SODIUM_INIT;
+ // regular tests
+ FOR (text_size, 0, 128) { test(text_size, 0); }
+ FOR (text_size, 0, 128) { test(text_size, 4); }
+ FOR ( ad_size, 0, 32) { test(0, ad_size); }
+ FOR ( ad_size, 0, 32) { test(16, ad_size); }
+ return 0;
+}
.PHONY: all clean
-VEC = chacha20 hchacha20 xchacha20 ietf_chacha20 aead_ietf \
+VEC = chacha20 hchacha20 xchacha20 ietf_chacha20 \
+ aead_ietf aead_8439 \
poly1305 blake2b sha512 hmac_sha512 argon2 \
edDSA edDSA_pk ed_25519 ed_25519_pk ed_25519_check \
x25519 x25519_pk elligator_inv elligator_dir
xchacha20.all.vec : xchacha20.vec
ietf_chacha20.all.vec : ietf_chacha20.vec
aead_ietf.all.vec : aead_ietf.vec
+aead_8439.all.vec : aead_8439.vec
blake2b.all.vec : blake2b.vec vectors/blake2b
sha512.all.vec : sha512.vec
hmac_sha512.all.vec : hmac_sha512.vec vectors/hmac_sha512
ad.buf, ad.size, text.buf, text.size);
}
+static void aead_8439(vector_reader *reader)
+{
+ vector key = next_input(reader);
+ vector nonce = next_input(reader);
+ vector ad = next_input(reader);
+ vector text = next_input(reader);
+ vector out = next_output(reader);
+ crypto_aead_ctx ctx;
+ crypto_aead_init_ietf(&ctx, key.buf, nonce.buf);
+ crypto_aead_write(&ctx, out.buf + 16, out.buf, ad.buf, ad.size,
+ text.buf, text.size);
+}
+
static void test_aead()
{
VECTORS(aead_ietf);
+ VECTORS(aead_8439);
printf("\taead (roundtrip)\n");
FOR (i, 0, 1000) {
crypto_lock_aead(box2, box2 + 16, key, nonce, 0, 0, plaintext, 8);
ASSERT_EQUAL(box, box2, 24);
}
+
+ printf("\taead incr (roundtrip)\n");
+ FOR (i, 0, 50) {
+ RANDOM_INPUT(key , 32);
+ RANDOM_INPUT(nonce , 24);
+ crypto_aead_ctx ctx_xa;
+ crypto_aead_ctx ctx_xb;
+ crypto_aead_ctx ctx_da;
+ crypto_aead_ctx ctx_db;
+ crypto_aead_ctx ctx_ia;
+ crypto_aead_ctx ctx_ib;
+ crypto_aead_init_x (&ctx_xa, key, nonce);
+ crypto_aead_init_x (&ctx_xb, key, nonce);
+ crypto_aead_init_djb (&ctx_da, key, nonce);
+ crypto_aead_init_djb (&ctx_db, key, nonce);
+ crypto_aead_init_ietf(&ctx_ia, key, nonce);
+ crypto_aead_init_ietf(&ctx_ib, key, nonce);
+
+ FOR (j, 0, 10) {
+ RANDOM_INPUT(ad, 4); // additional data
+ RANDOM_INPUT(pt, 8); // plaintext
+ u8 mac[16];
+ u8 ct [ 8];
+ u8 pt2[ 8];
+ // AEAD roundtrip (happy path)
+ crypto_aead_write (&ctx_xa, ct , mac, ad, 4, pt, 8);
+ ASSERT_OK(crypto_aead_read(&ctx_xb, pt2, mac, ad, 4, ct, 8));
+ ASSERT_EQUAL(pt, pt2, 8);
+ ASSERT_EQUAL(&ctx_xa, &ctx_xb, sizeof(crypto_aead_ctx));
+
+ crypto_aead_write (&ctx_da, ct , mac, ad, 4, pt, 8);
+ ASSERT_OK(crypto_aead_read(&ctx_db, pt2, mac, ad, 4, ct, 8));
+ ASSERT_EQUAL(pt, pt2, 8);
+
+ crypto_aead_write (&ctx_ia, ct , mac, ad, 4, pt, 8);
+ ASSERT_OK(crypto_aead_read(&ctx_ib, pt2, mac, ad, 4, ct, 8));
+ ASSERT_EQUAL(pt, pt2, 8);
+ }
+ }
+ printf("\n");
}
///////////////