Skip to content

Commit 3bcdd52

Browse files
authored
Merge pull request #578 from h2o/kazuho/aes64
implement 64-bit block cipher based on AES
2 parents f350eab + fcb84f7 commit 3bcdd52

File tree

16 files changed

+340
-2
lines changed

16 files changed

+340
-2
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,3 +31,7 @@ deps/picotest
3131
/picotlsvs/testopenssl/testopenssl.vcxproj.user
3232
/fuzz/fuzzer-asn1-type-and-length.c
3333
/fuzz/fuzzer-asn1-validation.c
34+
/deps/micro-ecc/test/ecc_test
35+
/src/esni.c
36+
/t/assets
37+
/.travis.yml

CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ SET(CORE_FILES
5858
lib/pembase64.c)
5959
SET(CORE_TEST_FILES
6060
t/hpke.c
61-
t/picotls.c)
61+
t/picotls.c
62+
t/quiclb.c)
6263
IF (WITH_DTRACE)
6364
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DPICOTLS_USE_DTRACE=1")
6465
DEFINE_DTRACE_DEPENDENCIES(${PROJECT_SOURCE_DIR}/picotls-probes.d picotls)
@@ -184,7 +185,8 @@ IF (WITH_FUSION)
184185
ADD_EXECUTABLE(test-fusion.t
185186
deps/picotest/picotest.c
186187
lib/picotls.c
187-
t/fusion.c)
188+
t/fusion.c
189+
t/quiclb.c)
188190
TARGET_LINK_LIBRARIES(test-fusion.t picotls-minicrypto)
189191
SET_TARGET_PROPERTIES(test-fusion.t PROPERTIES COMPILE_FLAGS "-mavx2 -maes -mpclmul -mvaes -mvpclmulqdq")
190192
IF (WITH_DTRACE)

include/picotls.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ extern "C" {
117117
#define PTLS_BLOWFISH_KEY_SIZE 16
118118
#define PTLS_BLOWFISH_BLOCK_SIZE 8
119119

120+
#define PTLS_QUICLB_KEY_SIZE 16 /* same as the underlying aes128ecb */
121+
#define PTLS_QUICLB_MIN_BLOCK_SIZE 7
122+
#define PTLS_QUICLB_MAX_BLOCK_SIZE 19 /* inclusive */
123+
120124
#define PTLS_SHA256_BLOCK_SIZE 64
121125
#define PTLS_SHA256_DIGEST_SIZE 32
122126

include/picotls/fusion.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ extern ptls_cipher_algorithm_t ptls_fusion_aes128ctr, ptls_fusion_aes256ctr;
103103
extern ptls_aead_algorithm_t ptls_fusion_aes128gcm, ptls_fusion_aes256gcm;
104104
extern ptls_aead_algorithm_t ptls_non_temporal_aes128gcm, ptls_non_temporal_aes256gcm;
105105

106+
extern ptls_cipher_algorithm_t ptls_fusion_quiclb;
107+
106108
/**
107109
* Returns a boolean indicating if fusion can be used.
108110
*/

include/picotls/openssl.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,8 @@ extern ptls_cipher_suite_t ptls_openssl_tls12_ecdhe_ecdsa_chacha20poly1305sha256
149149
extern ptls_cipher_algorithm_t ptls_openssl_bfecb;
150150
#endif
151151

152+
extern ptls_cipher_algorithm_t ptls_openssl_quiclb;
153+
152154
extern ptls_hpke_kem_t ptls_openssl_hpke_kem_p256sha256;
153155
extern ptls_hpke_kem_t ptls_openssl_hpke_kem_p384sha384;
154156
#if PTLS_OPENSSL_HAVE_X25519

lib/fusion.c

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
#include <wmmintrin.h>
4848
#include "picotls.h"
4949
#include "picotls/fusion.h"
50+
#include "./quiclb-impl.h"
5051

5152
#if defined(__clang__)
5253
#if __has_feature(address_sanitizer)
@@ -2178,6 +2179,57 @@ ptls_aead_algorithm_t ptls_non_temporal_aes256gcm = {"AES256-GCM",
21782179
sizeof(struct aesgcm_context),
21792180
non_temporal_aes256gcm_setup};
21802181

2182+
struct fusion_quiclb_context {
2183+
ptls_cipher_context_t super;
2184+
ptls_fusion_aesecb_context_t aesecb;
2185+
};
2186+
2187+
static void fusion_quiclb_dispose(ptls_cipher_context_t *_ctx)
2188+
{
2189+
struct fusion_quiclb_context *ctx = (struct fusion_quiclb_context *)_ctx;
2190+
ptls_fusion_aesecb_dispose(&ctx->aesecb);
2191+
}
2192+
2193+
static inline void fusion_quiclb_aes(void *aesecb, union picotls_quiclb_block *block)
2194+
{
2195+
block->m128 = aesecb_encrypt(aesecb, block->m128);
2196+
}
2197+
2198+
static void fusion_quiclb_encrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
2199+
{
2200+
struct fusion_quiclb_context *ctx = (struct fusion_quiclb_context *)_ctx;
2201+
picotls_quiclb_transform(fusion_quiclb_aes, &ctx->aesecb, output, input, len, 1);
2202+
}
2203+
2204+
static void fusion_quiclb_decrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
2205+
{
2206+
struct fusion_quiclb_context *ctx = (struct fusion_quiclb_context *)_ctx;
2207+
picotls_quiclb_transform(fusion_quiclb_aes, &ctx->aesecb, output, input, len, 0);
2208+
}
2209+
2210+
static int fusion_quiclb_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
2211+
{
2212+
struct fusion_quiclb_context *ctx = (struct fusion_quiclb_context *)_ctx;
2213+
2214+
*ctx = (struct fusion_quiclb_context){
2215+
.super.do_dispose = fusion_quiclb_dispose,
2216+
.super.do_init = picotls_quiclb_do_init,
2217+
.super.do_transform = is_enc ? fusion_quiclb_encrypt : fusion_quiclb_decrypt,
2218+
};
2219+
ptls_fusion_aesecb_init(&ctx->aesecb, 1, key, PTLS_AES128_KEY_SIZE, 0);
2220+
2221+
return 0;
2222+
}
2223+
2224+
ptls_cipher_algorithm_t ptls_fusion_quiclb = {
2225+
.name = "QUICLB",
2226+
.key_size = PTLS_QUICLB_KEY_SIZE,
2227+
.block_size = 8,
2228+
.iv_size = 0,
2229+
.context_size = sizeof(struct fusion_quiclb_context),
2230+
.setup_crypto = fusion_quiclb_setup_crypto,
2231+
};
2232+
21812233
#ifdef _WINDOWS
21822234
/**
21832235
* ptls_fusion_is_supported_by_cpu:

lib/openssl.c

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
#ifdef PTLS_HAVE_AEGIS
5959
#include "./libaegis.h"
6060
#endif
61+
#include "./quiclb-impl.h"
6162

6263
#ifdef _WINDOWS
6364
#ifndef _CRT_SECURE_NO_WARNINGS
@@ -1436,6 +1437,43 @@ static int bfecb_setup_crypto(ptls_cipher_context_t *ctx, int is_enc, const void
14361437

14371438
#endif
14381439

1440+
struct quiclb_context_t {
1441+
ptls_cipher_context_t super;
1442+
ptls_cipher_context_t *aesecb;
1443+
};
1444+
1445+
static void quiclb_dispose(ptls_cipher_context_t *_ctx)
1446+
{
1447+
struct quiclb_context_t *ctx = (struct quiclb_context_t *)_ctx;
1448+
ptls_cipher_free(ctx->aesecb);
1449+
}
1450+
1451+
static void quiclb_encrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
1452+
{
1453+
struct quiclb_context_t *ctx = (struct quiclb_context_t *)_ctx;
1454+
picotls_quiclb_transform(picotls_quiclb_cipher_aes, ctx->aesecb, output, input, len, 1);
1455+
}
1456+
1457+
static void quiclb_decrypt(ptls_cipher_context_t *_ctx, void *output, const void *input, size_t len)
1458+
{
1459+
struct quiclb_context_t *ctx = (struct quiclb_context_t *)_ctx;
1460+
picotls_quiclb_transform(picotls_quiclb_cipher_aes, ctx->aesecb, output, input, len, 0);
1461+
}
1462+
1463+
static int quiclb_setup_crypto(ptls_cipher_context_t *_ctx, int is_enc, const void *key)
1464+
{
1465+
struct quiclb_context_t *ctx = (struct quiclb_context_t *)_ctx;
1466+
1467+
*ctx = (struct quiclb_context_t){
1468+
.super.do_dispose = quiclb_dispose,
1469+
.super.do_init = picotls_quiclb_do_init,
1470+
.super.do_transform = is_enc ? quiclb_encrypt : quiclb_decrypt,
1471+
};
1472+
if ((ctx->aesecb = ptls_cipher_new(&ptls_openssl_aes128ecb, 1, key)) == NULL)
1473+
return PTLS_ERROR_LIBRARY;
1474+
return 0;
1475+
}
1476+
14391477
struct aead_crypto_context_t {
14401478
ptls_aead_context_t super;
14411479
EVP_CIPHER_CTX *evp_ctx;
@@ -2610,6 +2648,15 @@ ptls_cipher_algorithm_t ptls_openssl_bfecb = {"BF-ECB", PTLS_BLOWFISH_KEY
26102648
0 /* iv size */, sizeof(struct cipher_context_t), bfecb_setup_crypto};
26112649
#endif
26122650

2651+
ptls_cipher_algorithm_t ptls_openssl_quiclb = {
2652+
.name = "QUICLB",
2653+
.key_size = PTLS_QUICLB_KEY_SIZE,
2654+
.block_size = 8,
2655+
.iv_size = 0,
2656+
.context_size = sizeof(struct quiclb_context_t),
2657+
.setup_crypto = quiclb_setup_crypto,
2658+
};
2659+
26132660
ptls_hpke_kem_t ptls_openssl_hpke_kem_p256sha256 = {PTLS_HPKE_KEM_P256_SHA256, &ptls_openssl_secp256r1, &ptls_openssl_sha256};
26142661
ptls_hpke_kem_t ptls_openssl_hpke_kem_p384sha384 = {PTLS_HPKE_KEM_P384_SHA384, &ptls_openssl_secp384r1, &ptls_openssl_sha384};
26152662
#if PTLS_OPENSSL_HAVE_X25519

lib/quiclb-impl.h

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
/*
2+
* Copyright (c) 2025 Fastly, Kazuho Oku
3+
*
4+
* Permission is hereby granted, free of charge, to any person obtaining a copy
5+
* of this software and associated documentation files (the "Software"), to
6+
* deal in the Software without restriction, including without limitation the
7+
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
8+
* sell copies of the Software, and to permit persons to whom the Software is
9+
* furnished to do so, subject to the following conditions:
10+
*
11+
* The above copyright notice and this permission notice shall be included in
12+
* all copies or substantial portions of the Software.
13+
*
14+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
20+
* IN THE SOFTWARE.
21+
*/
22+
#ifndef picotls_quiclb_h
23+
#define picotls_quiclb_h
24+
25+
#if defined(__x86_64__) || defined(_M_X64)
26+
#include <emmintrin.h>
27+
#define PICOTLS_QUICLB_HAVE_SSE2 1
28+
#endif
29+
30+
union picotls_quiclb_block {
31+
uint8_t bytes[PTLS_AES_BLOCK_SIZE];
32+
uint64_t u64[PTLS_AES_BLOCK_SIZE / sizeof(uint64_t)];
33+
#if PICOTLS_QUICLB_HAVE_SSE2
34+
__m128i m128;
35+
#endif
36+
};
37+
38+
/**
39+
* encrypts one block of AES, assuming the context is `ptls_cipher_context_t` backed by ptls_foo_aes128ecb
40+
*/
41+
static inline void picotls_quiclb_cipher_aes(void *aesecb, union picotls_quiclb_block *block)
42+
{
43+
ptls_cipher_encrypt(aesecb, block->bytes, block->bytes, PTLS_AES_BLOCK_SIZE);
44+
}
45+
46+
/**
47+
* calculates X ^ AES(mask_and_expand(Y))
48+
*/
49+
static inline void picotls_quiclb_one_round(void (*aesecb_func)(void *aesecb, union picotls_quiclb_block *), void *aesecb_ctx,
50+
union picotls_quiclb_block *dest, const union picotls_quiclb_block *x,
51+
const union picotls_quiclb_block *y, const union picotls_quiclb_block *mask,
52+
const union picotls_quiclb_block *len_pass)
53+
{
54+
#if PICOTLS_QUICLB_HAVE_SSE2
55+
dest->m128 = _mm_or_si128(_mm_and_si128(y->m128, mask->m128), len_pass->m128);
56+
#else
57+
for (size_t i = 0; i < PTLS_ELEMENTSOF(dest->u64); ++i)
58+
dest->u64[i] = (y->u64[i] & mask->u64[i]) | len_pass->u64[i];
59+
#endif
60+
61+
aesecb_func(aesecb_ctx, dest);
62+
63+
#if PICOTLS_QUICLB_HAVE_SSE2
64+
dest->m128 = _mm_xor_si128(dest->m128, x->m128);
65+
#else
66+
for (size_t i = 0; i < PTLS_ELEMENTSOF(dest->u64); ++i)
67+
dest->u64[i] ^= x->u64[i];
68+
#endif
69+
}
70+
71+
static inline void picotls_quiclb_split_input(union picotls_quiclb_block *l, union picotls_quiclb_block *r, const uint8_t *input,
72+
size_t len)
73+
{
74+
size_t i;
75+
for (i = 0; i < (len + 1) / 2; ++i)
76+
l->bytes[i] = input[i];
77+
for (; i < PTLS_ELEMENTSOF(l->bytes); ++i)
78+
l->bytes[i] = 0;
79+
for (i = 0; i < (len + 1) / 2; ++i)
80+
r->bytes[i] = input[i + len / 2];
81+
for (; i < PTLS_ELEMENTSOF(r->bytes); ++i)
82+
r->bytes[i] = 0;
83+
}
84+
85+
static inline void picotls_quiclb_merge_output(uint8_t *output, size_t len, const union picotls_quiclb_block *l,
86+
const union picotls_quiclb_block *r)
87+
{
88+
uint8_t *outp = output;
89+
90+
for (size_t i = 0; i < len / 2; ++i)
91+
*outp++ = l->bytes[i];
92+
93+
if (len % 2 == 0) {
94+
for (size_t i = 0; i < len / 2; ++i)
95+
*outp++ = r->bytes[i];
96+
} else {
97+
*outp++ = (l->bytes[len / 2] & 0xf0) | (r->bytes[0] & 0x0f);
98+
for (size_t i = 0; i < len / 2; ++i)
99+
*outp++ = r->bytes[i + 1];
100+
}
101+
}
102+
103+
static inline void picotls_quiclb_do_init(ptls_cipher_context_t *ctx, const void *iv)
104+
{
105+
/* no-op */
106+
}
107+
108+
static inline void picotls_quiclb_transform(void (*aesecb_func)(void *aesecb, union picotls_quiclb_block *), void *aesecb_ctx,
109+
void *output, const void *input, size_t len, int encrypt)
110+
{
111+
static const struct quiclb_mask_t {
112+
union picotls_quiclb_block l, r;
113+
} masks[] = {
114+
{{{0xff, 0xff, 0xff, 0xf0}}, {{0x0f, 0xff, 0xff, 0xff}}}, /* 7 (MIN_LEN) */
115+
{{{0xff, 0xff, 0xff, 0xff}}, {{0xff, 0xff, 0xff, 0xff}}}, /* 8 */
116+
{{{0xff, 0xff, 0xff, 0xff, 0xf0}}, {{0x0f, 0xff, 0xff, 0xff, 0xff}}}, /* 9 */
117+
{{{0xff, 0xff, 0xff, 0xff, 0xff}}, {{0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 10 */
118+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}}, {{0x0f, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 11 */
119+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 12 */
120+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}}, {{0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 13 */
121+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 14 */
122+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}}, {{0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 15 */
123+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}, {{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 16 */
124+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}},
125+
{{0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 17 */
126+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
127+
{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}}, /* 18 */
128+
{{{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0}},
129+
{{0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}}} /* 19 */
130+
};
131+
132+
assert(PTLS_QUICLB_MIN_BLOCK_SIZE <= len && len <= PTLS_QUICLB_MAX_BLOCK_SIZE);
133+
PTLS_BUILD_ASSERT(PTLS_QUICLB_MAX_BLOCK_SIZE == PTLS_QUICLB_MIN_BLOCK_SIZE + PTLS_ELEMENTSOF(masks) - 1);
134+
135+
const struct quiclb_mask_t *mask = &masks[len - PTLS_QUICLB_MIN_BLOCK_SIZE];
136+
union picotls_quiclb_block l0, r0, r1, l1, r2, l2, len_pass = {{0}};
137+
len_pass.bytes[14] = (uint8_t)len;
138+
139+
#define ROUND(rnd, dest, x, y, mask_side) \
140+
do { \
141+
len_pass.bytes[15] = (rnd); \
142+
picotls_quiclb_one_round(aesecb_func, aesecb_ctx, &dest, &x, &y, &mask->mask_side, &len_pass); \
143+
} while (0)
144+
145+
if (encrypt) {
146+
picotls_quiclb_split_input(&l0, &r0, input, len);
147+
ROUND(1, r1, r0, l0, l);
148+
ROUND(2, l1, l0, r1, r);
149+
ROUND(3, r2, r1, l1, l);
150+
ROUND(4, l2, l1, r2, r);
151+
picotls_quiclb_merge_output(output, len, &l2, &r2);
152+
} else {
153+
picotls_quiclb_split_input(&l2, &r2, input, len);
154+
ROUND(4, l1, l2, r2, r);
155+
ROUND(3, r1, r2, l1, l);
156+
ROUND(2, l0, l1, r1, r);
157+
ROUND(1, r0, r1, l0, l);
158+
picotls_quiclb_merge_output(output, len, &l0, &r0);
159+
}
160+
161+
#undef ROUND
162+
}
163+
164+
#endif

picotlsvs/fusiontest/fusiontest.vcxproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<ItemGroup>
2222
<ClCompile Include="..\..\deps\picotest\picotest.c" />
2323
<ClCompile Include="..\..\t\fusion.c" />
24+
<ClCompile Include="..\..\t\quiclb.c" />
2425
</ItemGroup>
2526
<PropertyGroup Label="Globals">
2627
<VCProjectVersion>16.0</VCProjectVersion>

picotlsvs/fusiontest/fusiontest.vcxproj.filters

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,8 @@
2121
<ClCompile Include="..\..\deps\picotest\picotest.c">
2222
<Filter>Source Files</Filter>
2323
</ClCompile>
24+
<ClCompile Include="..\..\t\quiclb.c">
25+
<Filter>Source Files</Filter>
26+
</ClCompile>
2427
</ItemGroup>
2528
</Project>

0 commit comments

Comments
 (0)