commit 4d3eb64597edb757fa594d1a4cf9983da7bbf9e6
parent 519eed9df83148955efd27c495555fe0625aef41
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Fri, 16 Dec 2022 14:53:51 +0100
crypto::aes: add [[aes]] for choosing the algorithm
The aes function will return the proper aes implementation, depending on
available hardware support. A general init function has been introduced
to be used to initialize the derived block.
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
9 files changed, 343 insertions(+), 13 deletions(-)
diff --git a/crypto/aes/README b/crypto/aes/README
@@ -1,7 +1,7 @@
The crypto::aes module provides an implementation of the Advanced Encryption
Standard per the [[crypto::cipher::block]] interface. Several implementations of
AES are provided which are optimized for different scenarios. To choose the most
-appropriate one for your system, use [[new]].
+appropriate one for your system, use [[aes]].
When combined with a block cipher mode from [[crypto::cipher]], suitable buffer
lengths for static allocation are provided as constants such as [[BLOCKSIZE]],
diff --git a/crypto/aes/aes+x86_64.ha b/crypto/aes/aes+x86_64.ha
@@ -0,0 +1,18 @@
+// License: MPL-2.0
+// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
+use crypto::cipher;
+
+def MAXEXPKEYSIZE: size = CT64_EXPKEYSIZE;
+def MAXNPARALLEL: size = CT64_NPARALLEL;
+
+let rtvtable: *cipher::blockvtable = &ct64_vtable;
+let initfuncptr: *initfunc = &ct64_init;
+
+@init fn init() void = {
+ if (x86ni_available()) {
+ hwsup = true;
+ rtvtable = &x86ni_vtable;
+ initfuncptr = &x86ni_init;
+ };
+};
+
diff --git a/crypto/aes/aes.ha b/crypto/aes/aes.ha
@@ -1,4 +1,9 @@
// License: MPL-2.0
// (c) 2022 Armin Preiml <apreiml@strohwolke.at>
+use crypto::cipher;
def MAXEXPKEYSIZE: size = CT64_EXPKEYSIZE;
+def MAXNPARALLEL: size = CT64_NPARALLEL;
+
+const rtvtable: *cipher::blockvtable = &ct64_vtable;
+const initfuncptr: *initfunc = &ct64_init;
diff --git a/crypto/aes/aes_ct64.ha b/crypto/aes/aes_ct64.ha
@@ -33,16 +33,8 @@ use crypto::math;
use endian;
def CT64_EXPKEYSIZE: size = 960;
-
def CT64_NPARALLEL: size = 4;
-// Size of the buffer used for [[crypto::cipher::ctr]].
-export def CTR_BUFSIZE: size = BLOCKSIZE * (CT64_NPARALLEL + 1);
-
-// Size of the buffer used for [[crypto::cipher::cbc_encryptor]] and
-// [[crypto::cipher::cbc_decryptor]].
-export def CBC_BUFSIZE: size = BLOCKSIZE * 2;
-
// Returns an AES [[crypto::cipher::block]] cipher implementation optimized for
// constant time operation on 64-bit systems.
//
diff --git a/crypto/aes/block.ha b/crypto/aes/block.ha
@@ -6,13 +6,44 @@ use crypto::cipher;
// The block size used by the AES algorithm.
export def BLOCKSIZE: size = 16;
+// Size of the buffer used for [[crypto::cipher::cbc_encryptor]] and
+// [[crypto::cipher::cbc_decryptor]].
+export def CBC_BUFSIZE: size = BLOCKSIZE * 2;
+
+// Size of the buffer used for [[crypto::cipher::ctr]].
+export def CTR_BUFSIZE: size = BLOCKSIZE * (MAXNPARALLEL + 1);
+
export type block = struct {
vtable: cipher::block,
rounds: u32,
expkey: [MAXEXPKEYSIZE]u8,
};
+// Returns an AES [[crypto::cipher::block]] cipher implementation that has
+// hardware support if possible. Check [[hwsupport]] to see if it is available.
+//
+// The caller must call [[init]] to add a key to the cipher before using
+// the cipher, and must call [[crypto::cipher::finish]] when they are finished
+// using the cipher to securely erase any secret data stored in the cipher
+// state.
+export fn aes() block = block {
+ vtable = rtvtable,
+ ...
+};
+
+
+let hwsup: bool = false;
+
+// Checks whether hardware AES support is available.
+export fn hwsupport() bool = hwsup;
+
+type initfunc = fn(b: *block, key: []u8) void;
+
+// Initializes the AES block with an encryption key.
+export fn init(b: *block, key: []u8) void = initfuncptr(b, key);
+
fn block_finish(b: *cipher::block) void = {
let b = b: *block;
bytes::zero(b.expkey);
};
+
diff --git a/crypto/aes/rt+test.ha b/crypto/aes/rt+test.ha
@@ -0,0 +1,282 @@
+// License: MPL-2.0
+// (c) 2021 Armin Preiml <apreiml@strohwolke.at>
+// (c) 2021 Drew DeVault <sir@cmpwn.com>
+use crypto::cipher;
+use bytes;
+
+@test fn rt_finish() void = {
+ const key: [16]u8 = [
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
+ ];
+
+ const zero: [CT64_EXPKEYSIZE]u8 = [0...];
+
+ let block = aes();
+ init(&block, key);
+ cipher::finish(&block);
+
+ assert(len(block.expkey) == len(zero));
+ assert(bytes::equal(zero, block.expkey));
+};
+
+@test fn rt_encrypt_128() void = {
+ const key: [16]u8 = [
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
+ ];
+
+ const plain: [16]u8 = [
+ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d,
+ 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34,
+ ];
+
+ const cipher: [16]u8 = [
+ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb,
+ 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32,
+ ];
+
+ let result: [16]u8 = [0...];
+
+ let block = aes();
+ init(&block, key);
+ defer cipher::finish(&block);
+ cipher::encrypt(&block, result[..], plain[..]);
+
+ assert(bytes::equal(cipher, result));
+};
+
+@test fn rt_encrypt_128_multiple_blocks() void = {
+ const key: [16]u8 = [
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
+ ];
+
+ const plain: [_]u8 = [
+ 0xdb, 0x3a, 0x36, 0x50, 0x85, 0x60, 0x4c, 0x65,
+ 0xc0, 0x22, 0x5a, 0x0d, 0x68, 0x2f, 0x19, 0xc2,
+ 0x16, 0x27, 0xdd, 0xae, 0xd7, 0x28, 0x29, 0x23,
+ 0xfa, 0x97, 0x28, 0xb0, 0x9a, 0x90, 0xfe, 0xb3,
+ 0xde, 0x57, 0xbc, 0x15, 0x15, 0x8d, 0xb6, 0x29,
+ 0x61, 0x40, 0x76, 0x51, 0x32, 0xc2, 0x3d, 0x1e,
+ 0xdf, 0x97, 0x6a, 0x5e, 0x0a, 0x6b, 0xf6, 0x40,
+ 0xb4, 0x08, 0x7e, 0xde, 0x17, 0xa4, 0x58, 0x98
+ ];
+
+ const cipher: [_]u8 = [
+ 0xdd, 0xf4, 0x53, 0x55, 0x18, 0x7a, 0xf0, 0x65,
+ 0x59, 0xd6, 0xdc, 0x4e, 0xc2, 0x89, 0xff, 0x7b,
+ 0x54, 0x41, 0x6a, 0xda, 0xa0, 0xad, 0x85, 0xbd,
+ 0x75, 0x1e, 0x59, 0x15, 0x6d, 0x30, 0xa8, 0xa8,
+ 0xa8, 0x54, 0x6e, 0xab, 0x9c, 0x47, 0x84, 0xd,
+ 0x6e, 0xb5, 0x5e, 0xba, 0xb0, 0xd9, 0x47, 0xfa,
+ 0x53, 0x6b, 0xff, 0x31, 0x47, 0x86, 0xf8, 0x4c,
+ 0x6b, 0x65, 0xaa, 0xa2, 0xd2, 0xd7, 0xc1, 0xdf
+ ];
+
+
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ // test from 1 to 4 parallel blocks
+ for (let i = 0z; i < 4; i += 1) {
+ let result: [64]u8 = [0...];
+
+ let sz = (i + 1) * 16;
+ cipher::encrypt(&block, result[0..sz], plain[0..sz]);
+
+ assert(bytes::equal(cipher[0..sz], result[0..sz]));
+ };
+};
+
+@test fn rt_decrypt_128_multiple_blocks() void = {
+ const key: [16]u8 = [
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
+ ];
+
+ const plain: [_]u8 = [
+ 0xdb, 0x3a, 0x36, 0x50, 0x85, 0x60, 0x4c, 0x65,
+ 0xc0, 0x22, 0x5a, 0x0d, 0x68, 0x2f, 0x19, 0xc2,
+ 0x16, 0x27, 0xdd, 0xae, 0xd7, 0x28, 0x29, 0x23,
+ 0xfa, 0x97, 0x28, 0xb0, 0x9a, 0x90, 0xfe, 0xb3,
+ 0xde, 0x57, 0xbc, 0x15, 0x15, 0x8d, 0xb6, 0x29,
+ 0x61, 0x40, 0x76, 0x51, 0x32, 0xc2, 0x3d, 0x1e,
+ 0xdf, 0x97, 0x6a, 0x5e, 0x0a, 0x6b, 0xf6, 0x40,
+ 0xb4, 0x08, 0x7e, 0xde, 0x17, 0xa4, 0x58, 0x98
+ ];
+
+ const cipher: [_]u8 = [
+ 0xdd, 0xf4, 0x53, 0x55, 0x18, 0x7a, 0xf0, 0x65,
+ 0x59, 0xd6, 0xdc, 0x4e, 0xc2, 0x89, 0xff, 0x7b,
+ 0x54, 0x41, 0x6a, 0xda, 0xa0, 0xad, 0x85, 0xbd,
+ 0x75, 0x1e, 0x59, 0x15, 0x6d, 0x30, 0xa8, 0xa8,
+ 0xa8, 0x54, 0x6e, 0xab, 0x9c, 0x47, 0x84, 0xd,
+ 0x6e, 0xb5, 0x5e, 0xba, 0xb0, 0xd9, 0x47, 0xfa,
+ 0x53, 0x6b, 0xff, 0x31, 0x47, 0x86, 0xf8, 0x4c,
+ 0x6b, 0x65, 0xaa, 0xa2, 0xd2, 0xd7, 0xc1, 0xdf
+ ];
+
+
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ // test from 1 to 4 parallel blocks
+ for (let i = 0z; i < 4; i += 1) {
+ let result: [64]u8 = [0...];
+
+ let sz = (i + 1) * 16;
+ cipher::decrypt(&block, result[0..sz], cipher[0..sz]);
+
+ assert(bytes::equal(plain[0..sz], result[0..sz]));
+ };
+};
+
+@test fn rt_decrypt_128() void = {
+ const key: []u8 = [
+ 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
+ 0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c,
+ ];
+
+ const plain: [16]u8 = [
+ 0x32, 0x43, 0xf6, 0xa8, 0x88, 0x5a, 0x30, 0x8d,
+ 0x31, 0x31, 0x98, 0xa2, 0xe0, 0x37, 0x07, 0x34,
+ ];
+
+ const cipher: [16]u8 = [
+ 0x39, 0x25, 0x84, 0x1d, 0x02, 0xdc, 0x09, 0xfb,
+ 0xdc, 0x11, 0x85, 0x97, 0x19, 0x6a, 0x0b, 0x32,
+ ];
+
+ let result: [16]u8 = [0...];
+
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ cipher::decrypt(&block, result[..], cipher[..]);
+
+ assert(bytes::equal(plain, result));
+};
+
+// fips-197.pdf Appendix C.1
+@test fn rt_example_vector1() void = {
+ const key: [_]u8 = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ ];
+
+ const plain: [_]u8 = [
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ ];
+
+ const cipher: [_]u8 = [
+ 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
+ 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a,
+ ];
+
+ let result: [16]u8 = [0...];
+
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ cipher::encrypt(&block, result[..], plain[..]);
+ assert(bytes::equal(cipher, result));
+
+ cipher::decrypt(&block, result[..], cipher[..]);
+ assert(bytes::equal(plain, result));
+};
+
+@test fn rt_example_vector1_in_place() void = {
+ const key: [_]u8 = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ ];
+
+ const plain: [_]u8 = [
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ ];
+
+ const cipher: [_]u8 = [
+ 0x69, 0xc4, 0xe0, 0xd8, 0x6a, 0x7b, 0x04, 0x30,
+ 0xd8, 0xcd, 0xb7, 0x80, 0x70, 0xb4, 0xc5, 0x5a,
+ ];
+
+ let result: [16]u8 = plain;
+
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ cipher::encrypt(&block, result[..], result[..]);
+ assert(bytes::equal(cipher, result));
+
+ cipher::decrypt(&block, result[..], result[..]);
+ assert(bytes::equal(plain, result));
+};
+
+// fips-197.pdf Appendix C.2
+@test fn rt_example_vector2() void = {
+ const key: [_]u8 = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ ];
+
+ const plain: [_]u8 = [
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ ];
+
+ const cipher: [_]u8 = [
+ 0xdd, 0xa9, 0x7c, 0xa4, 0x86, 0x4c, 0xdf, 0xe0,
+ 0x6e, 0xaf, 0x70, 0xa0, 0xec, 0x0d, 0x71, 0x91,
+ ];
+
+ let result: [16]u8 = [0...];
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ cipher::encrypt(&block, result[..], plain[..]);
+ assert(bytes::equal(cipher, result));
+
+ cipher::decrypt(&block, result[..], cipher[..]);
+ assert(bytes::equal(plain, result));
+};
+
+// fips-197.pdf Appendix C.3
+@test fn rt_example_vector3() void = {
+ const key: []u8 = [
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
+ ];
+
+ const plain: []u8 = [
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff,
+ ];
+
+ const cipher: []u8 = [
+ 0x8e, 0xa2, 0xb7, 0xca, 0x51, 0x67, 0x45, 0xbf,
+ 0xea, 0xfc, 0x49, 0x90, 0x4b, 0x49, 0x60, 0x89,
+ ];
+
+ let result: [16]u8 = [0...];
+ let block = aes();
+ init(&block, key[..]);
+ defer cipher::finish(&block);
+
+ cipher::encrypt(&block, result[..], plain[..]);
+ assert(bytes::equal(cipher, result));
+
+ cipher::decrypt(&block, result[..], cipher[..]);
+ assert(bytes::equal(plain, result));
+};
diff --git a/crypto/aes/xts/xts.ha b/crypto/aes/xts/xts.ha
@@ -13,8 +13,8 @@ export type block = struct {
// Creates a AES-XTS instance. Must be initialized with [[init]] and always be
// finished using [[finish]] to erase sensitive state from memory.
export fn xts() block = block {
- b1 = aes::ct64(),
- b2 = aes::ct64(),
+ b1 = aes::aes(),
+ b2 = aes::aes(),
...
};
@@ -25,8 +25,8 @@ export fn init(b: *block, key: []u8) void = {
"invalid key size");
let sep: size = len(key) / 2;
- aes::ct64_init(&b.b1, key[..sep]);
- aes::ct64_init(&b.b2, key[sep..]);
+ aes::init(&b.b1, key[..sep]);
+ aes::init(&b.b2, key[sep..]);
};
def GF_128_FDBK: u8 = 0x87;
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -247,6 +247,7 @@ crypto_aes() {
ct64+test.ha \
cbc+test.ha \
ctr+test.ha \
+ rt+test.ha \
+test/gcm.ha
gen_ssa crypto::aes bufio bytes crypto::cipher crypto::math \
endian errors io rt
diff --git a/stdlib.mk b/stdlib.mk
@@ -3010,6 +3010,7 @@ testlib_crypto_aes_any_srcs = \
$(STDLIB)/crypto/aes/ct64+test.ha \
$(STDLIB)/crypto/aes/cbc+test.ha \
$(STDLIB)/crypto/aes/ctr+test.ha \
+ $(STDLIB)/crypto/aes/rt+test.ha \
$(STDLIB)/crypto/aes/+test/gcm.ha
$(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(testlib_rt) $(testlib_bufio_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_errors_$(PLATFORM)) $(testlib_io_$(PLATFORM)) $(testlib_rt_$(PLATFORM))