hare

[hare] The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Mcrypto/aes/README | 2+-
Acrypto/aes/aes+x86_64.ha | 18++++++++++++++++++
Mcrypto/aes/aes.ha | 5+++++
Mcrypto/aes/aes_ct64.ha | 8--------
Mcrypto/aes/block.ha | 31+++++++++++++++++++++++++++++++
Acrypto/aes/rt+test.ha | 282+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrypto/aes/xts/xts.ha | 8++++----
Mscripts/gen-stdlib | 1+
Mstdlib.mk | 1+
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))