Add support for xor_iv
diff --git a/include/picotls.h b/include/picotls.h
index 0ffe7d7..ca25ef3 100644
--- a/include/picotls.h
+++ b/include/picotls.h
@@ -324,6 +324,7 @@
const struct st_ptls_aead_algorithm_t *algo;
/* field above this line must not be altered by the crypto binding */
void (*dispose_crypto)(struct st_ptls_aead_context_t *ctx);
+ void (*do_xor_iv)(struct st_ptls_aead_context_t *ctx, const void * xor, size_t xorlen);
void (*do_encrypt_init)(struct st_ptls_aead_context_t *ctx, uint64_t seq, const void *aad, size_t aadlen);
size_t (*do_encrypt_update)(struct st_ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen);
size_t (*do_encrypt_final)(struct st_ptls_aead_context_t *ctx, void *output);
@@ -1229,6 +1230,11 @@
*/
void ptls_aead_free(ptls_aead_context_t *ctx);
/**
+ * Permutates the static IV by applying given bytes using bit-wise XOR. This API can be used for supplying nonces longer than 64-
+ * bits.
+ */
+static void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len);
+/**
*
*/
static size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
@@ -1413,6 +1419,11 @@
ctx->do_transform(ctx, output, input, len);
}
+inline void ptls_aead_xor_iv(ptls_aead_context_t *ctx, const void *bytes, size_t len)
+{
+ ctx->do_xor_iv(ctx, bytes, len);
+}
+
inline size_t ptls_aead_encrypt(ptls_aead_context_t *ctx, void *output, const void *input, size_t inlen, uint64_t seq,
const void *aad, size_t aadlen)
{
diff --git a/lib/cifra/aes-common.h b/lib/cifra/aes-common.h
index 0c393c5..95a9463 100644
--- a/lib/cifra/aes-common.h
+++ b/lib/cifra/aes-common.h
@@ -154,11 +154,21 @@
return tag_offset;
}
+static inline void minicrypto_aesgcm_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+ struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
+ const uint8_t *bytes = _bytes;
+
+ for (size_t i = 0; i < len; ++i)
+ ctx->static_iv[i] ^= bytes[i];
+}
+
static inline int aead_aesgcm_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv)
{
struct aesgcm_context_t *ctx = (struct aesgcm_context_t *)_ctx;
ctx->super.dispose_crypto = aesgcm_dispose_crypto;
+ ctx->super.do_xor_iv = minicrypto_aesgcm_aead_xor_iv;
if (is_enc) {
ctx->super.do_encrypt_init = aesgcm_encrypt_init;
ctx->super.do_encrypt_update = aesgcm_encrypt_update;
diff --git a/lib/cifra/chacha20.c b/lib/cifra/chacha20.c
index 57a6c39..e51a930 100644
--- a/lib/cifra/chacha20.c
+++ b/lib/cifra/chacha20.c
@@ -179,11 +179,21 @@
return ret;
}
+static void minicrypto_chacha20_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+ struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
+ const uint8_t *bytes = _bytes;
+
+ for (size_t i = 0; i < len; ++i)
+ ctx->static_iv[i] ^= bytes[i];
+}
+
static int aead_chacha20poly1305_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv)
{
struct chacha20poly1305_context_t *ctx = (struct chacha20poly1305_context_t *)_ctx;
ctx->super.dispose_crypto = chacha20poly1305_dispose_crypto;
+ ctx->super.do_xor_iv = minicrypto_chacha20_aead_xor_iv;
if (is_enc) {
ctx->super.do_encrypt_init = chacha20poly1305_init;
ctx->super.do_encrypt_update = chacha20poly1305_encrypt_update;
diff --git a/lib/fusion.c b/lib/fusion.c
index 1610447..cbf70a5 100644
--- a/lib/fusion.c
+++ b/lib/fusion.c
@@ -924,6 +924,14 @@
return enclen;
}
+static inline void aesgcm_fusion_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+ struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;
+ const uint8_t *bytes = _bytes;
+ __m128i xor_mask = loadn(_bytes, len);
+ ctx->static_iv = _mm_xor_si128(ctx->static_iv, xor_mask);
+}
+
static int aesgcm_setup(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv, size_t key_size)
{
struct aesgcm_context *ctx = (struct aesgcm_context *)_ctx;
@@ -934,6 +942,7 @@
return 0;
ctx->super.dispose_crypto = aesgcm_dispose_crypto;
+ ctx->super.do_xor_iv = aesgcm_fusion_aead_xor_iv;
ctx->super.do_encrypt_init = aead_do_encrypt_init;
ctx->super.do_encrypt_update = aead_do_encrypt_update;
ctx->super.do_encrypt_final = aead_do_encrypt_final;
diff --git a/lib/openssl.c b/lib/openssl.c
index c7ba115..00c7777 100644
--- a/lib/openssl.c
+++ b/lib/openssl.c
@@ -779,6 +779,15 @@
EVP_CIPHER_CTX_free(ctx->evp_ctx);
}
+static void aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+ struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
+ const uint8_t *bytes = _bytes;
+
+ for (size_t i = 0; i < len; ++i)
+ ctx->static_iv[i] ^= bytes[i];
+}
+
static void aead_do_encrypt_init(ptls_aead_context_t *_ctx, uint64_t seq, const void *aad, size_t aadlen)
{
struct aead_crypto_context_t *ctx = (struct aead_crypto_context_t *)_ctx;
@@ -864,6 +873,7 @@
return 0;
ctx->super.dispose_crypto = aead_dispose_crypto;
+ ctx->super.do_xor_iv = aead_xor_iv;
if (is_enc) {
ctx->super.do_encrypt_init = aead_do_encrypt_init;
ctx->super.do_encrypt_update = aead_do_encrypt_update;
diff --git a/lib/ptlsbcrypt.c b/lib/ptlsbcrypt.c
index 9f46a47..43c442f 100644
--- a/lib/ptlsbcrypt.c
+++ b/lib/ptlsbcrypt.c
@@ -476,6 +476,15 @@
}
}
+static void ptls_bcrypt_aead_xor_iv(ptls_aead_context_t *_ctx, const void *_bytes, size_t len)
+{
+ struct ptls_bcrypt_aead_context_t *ctx = (struct ptls_bcrypt_aead_context_t *)_ctx;
+ const uint8_t *bytes = _bytes;
+
+ for (size_t i = 0; i < len; ++i)
+ ctx->bctx.iv[i] ^= bytes[i];
+}
+
static int ptls_bcrypt_aead_setup_crypto(ptls_aead_context_t *_ctx, int is_enc, const void *key, const void *iv,
wchar_t const *bcrypt_name, wchar_t const *bcrypt_mode, size_t bcrypt_mode_size)
{
@@ -530,6 +539,7 @@
memcpy(ctx->bctx.iv_static, iv, ctx->super.algo->iv_size);
if (is_enc) {
ctx->super.dispose_crypto = ptls_bcrypt_aead_dispose_crypto;
+ ctx->super.do_xor_iv = ptls_bcrypt_aead_xor_iv;
ctx->super.do_decrypt = NULL;
ctx->super.do_encrypt_init = ptls_bcrypt_aead_do_encrypt_init;
ctx->super.do_encrypt_update = ptls_bcrypt_aead_do_encrypt_update;
diff --git a/t/fusion.c b/t/fusion.c
index a7a9685..cd30702 100644
--- a/t/fusion.c
+++ b/t/fusion.c
@@ -193,6 +193,31 @@
ptls_fusion_aesgcm_free(aead);
}
+static void gcm_iv96(void)
+{
+ static const uint8_t key[16] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff},
+ aad[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19},
+ iv[] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31},
+ plaintext[] =
+ "hello world\nhello world\nhello world\nhello world\nhello world\nhello world\nhello world\n";
+
+ ptls_aead_context_t *aead = ptls_aead_new_direct(&ptls_fusion_aes128gcm, 0, key, iv);
+ uint8_t encrypted[sizeof(plaintext) + 16], decrypted[sizeof(plaintext)];
+ uint8_t seq32[4] = {0x11, 0x23, 0x45, 0x67};
+ uint8_t seq32_bad[4] = {0x89, 0xab, 0xcd, 0xef};
+ ptls_aead_xor_iv(aead, seq32, sizeof(seq32));
+ ptls_aead_encrypt(aead, encrypted, plaintext, sizeof(plaintext), 0, aad, sizeof(aad));
+ ok(ptls_aead_decrypt(aead, decrypted, encrypted, sizeof(encrypted), 0, aad, sizeof(aad)) == sizeof(plaintext));
+ ok(memcmp(decrypted, plaintext, sizeof(plaintext)) == 0);
+ ptls_aead_xor_iv(aead, seq32, sizeof(seq32));
+ ptls_aead_xor_iv(aead, seq32_bad, sizeof(seq32_bad));
+ ok(ptls_aead_decrypt(aead, decrypted, encrypted, sizeof(encrypted), 0, aad, sizeof(aad)) == SIZE_MAX);
+ ptls_aead_xor_iv(aead, seq32_bad, sizeof(seq32_bad));
+ ptls_aead_xor_iv(aead, seq32, sizeof(seq32));
+ ok(ptls_aead_decrypt(aead, decrypted, encrypted, sizeof(encrypted), 0, aad, sizeof(aad)) == sizeof(plaintext));
+ ptls_aead_free(aead);
+}
+
static void test_generated(int aes256)
{
ptls_cipher_context_t *rand = ptls_cipher_new(&ptls_minicrypto_aes128ctr, 1, zero);
@@ -274,6 +299,7 @@
subtest("gcm-basic", gcm_basic);
subtest("gcm-capacity", gcm_capacity);
subtest("gcm-test-vectors", gcm_test_vectors);
+ subtest("gcm-iv96", gcm_iv96);
subtest("generated-128", test_generated_aes128);
subtest("generated-256", test_generated_aes256);
diff --git a/t/picotls.c b/t/picotls.c
index ca07a90..d0e88ff 100644
--- a/t/picotls.c
+++ b/t/picotls.c
@@ -192,6 +192,46 @@
ptls_aead_free(c);
}
+static void test_aad96_ciphersuite(ptls_cipher_suite_t *cs1, ptls_cipher_suite_t *cs2)
+{
+ const char *traffic_secret = "012345678901234567890123456789012345678901234567", *src = "hello world", *aad = "my true aad";
+ ptls_aead_context_t *c;
+ char enc[256], dec[256];
+ uint8_t seq32[4] = {0xa1, 0xb2, 0xc3, 0xd4};
+ uint8_t seq32_bad[4] = {0xa2, 0xb3, 0xc4, 0xe5};
+ size_t enclen, declen;
+
+ /* encrypt */
+ c = ptls_aead_new(cs1->aead, cs1->hash, 1, traffic_secret, NULL);
+ assert(c != NULL);
+ ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+ ptls_aead_encrypt_init(c, 123, aad, strlen(aad));
+ enclen = ptls_aead_encrypt_update(c, enc, src, strlen(src));
+ enclen += ptls_aead_encrypt_final(c, enc + enclen);
+ ptls_aead_free(c);
+
+ /* decrypt */
+ c = ptls_aead_new(cs2->aead, cs2->hash, 0, traffic_secret, NULL);
+ assert(c != NULL);
+ /* test first decryption */
+ ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+ declen = ptls_aead_decrypt(c, dec, enc, enclen, 123, aad, strlen(aad));
+ ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+ ok(declen == strlen(src));
+ ok(memcmp(src, dec, declen) == 0);
+ /* test that setting the wrong IV creates an error */
+ ptls_aead_xor_iv(c, seq32_bad, sizeof(seq32_bad));
+ declen = ptls_aead_decrypt(c, dec, enc, enclen, 123, aad, strlen(aad));
+ ptls_aead_xor_iv(c, seq32_bad, sizeof(seq32_bad));
+ ok(declen == SIZE_MAX);
+ /* test second decryption with correct IV to verify no side effect */
+ ptls_aead_xor_iv(c, seq32, sizeof(seq32));
+ declen = ptls_aead_decrypt(c, dec, enc, enclen, 123, aad, strlen(aad));
+ ok(declen == strlen(src));
+ ok(memcmp(src, dec, declen) == 0);
+ ptls_aead_free(c);
+}
+
static void test_ecb(ptls_cipher_algorithm_t *algo, const void *expected, size_t expected_len)
{
static const uint8_t key[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
@@ -289,6 +329,7 @@
test_ciphersuite(cs, cs_peer);
test_aad_ciphersuite(cs, cs_peer);
+ test_aad96_ciphersuite(cs, cs_peer);
}
static void test_aes256gcm(void)
@@ -299,6 +340,7 @@
if (cs != NULL && cs_peer != NULL) {
test_ciphersuite(cs, cs_peer);
test_aad_ciphersuite(cs, cs_peer);
+ test_aad96_ciphersuite(cs, cs_peer);
}
}
@@ -310,6 +352,7 @@
if (cs != NULL && cs_peer != NULL) {
test_ciphersuite(cs, cs_peer);
test_aad_ciphersuite(cs, cs_peer);
+ test_aad96_ciphersuite(cs, cs_peer);
}
}