hare

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

commit 07c2da29a0ab5c9af583c162c9d182219718a1b5
parent fc463598115682f4e2288e9ecdc3841a171201df
Author: Armin Preiml <apreiml@strohwolke.at>
Date:   Wed, 26 Jan 2022 19:18:16 +0100

implement crypto::aes::xts

Signed-off-by: Armin Preiml <apreiml@strohwolke.at>

Diffstat:
Acrypto/aes/xts/+test.ha | 83+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/aes/xts/README | 26++++++++++++++++++++++++++
Acrypto/aes/xts/xts.ha | 121+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 11+++++++++++
Mstdlib.mk | 33+++++++++++++++++++++++++++++++++
5 files changed, 274 insertions(+), 0 deletions(-)

diff --git a/crypto/aes/xts/+test.ha b/crypto/aes/xts/+test.ha @@ -0,0 +1,83 @@ +use bytes; + +@test fn oneblock() void = { + let key: [_]u8 = [ + 0xfe, 0x7f, 0xec, 0x77, 0x1b, 0xa4, 0x28, 0xc7, 0xf8, 0x82, + 0x62, 0x2b, 0xe7, 0x60, 0x59, 0xf5, 0x25, 0x71, 0xda, 0x8b, + 0x8f, 0x72, 0x04, 0xe4, 0x38, 0xa2, 0xa5, 0x98, 0x45, 0x14, + 0xff, 0xbe, 0xce, 0xf6, 0xaa, 0xcc, 0xe1, 0x86, 0xc2, 0x2b, + 0x9e, 0x55, 0x86, 0x23, 0x71, 0x96, 0xa3, 0xb1, 0xa1, 0xf9, + 0x11, 0x4d, 0x86, 0xf8, 0x7e, 0xd1, 0xae, 0x5a, 0x3d, 0x4a, + 0x99, 0x65, 0xd0, 0xb1, + ]; + let sector1: [_]u8 = [ + 0xe4, 0x60, 0xb0, 0x44, 0x7c, 0xc0, 0xbe, 0x63, 0xbb, 0x88, + 0x62, 0xab, 0x6e, 0x49, 0xde, 0xea, + ]; + let crypto1: [_]u8 = [ + 0x6f, 0x3c, 0x46, 0xd8, 0x36, 0x25, 0x7d, 0x70, 0x00, 0xc3, + 0x94, 0x92, 0x48, 0x18, 0xa1, 0x67, + ]; + + let result: [16]u8 = [0...]; + + let b = xts(); + init(&b, key); + + encrypt(&b, result, sector1, 1); + assert(bytes::equal(crypto1, result)); + + decrypt(&b, result, crypto1, 1); + assert(bytes::equal(sector1, result)); +}; + +@test fn multiblock() void = { + const key: [_]u8 = [ + 0x61, 0x7f, 0x3d, 0x06, 0xa2, 0x18, 0x4b, 0x63, 0xd7, 0xad, + 0xfa, 0x07, 0xe7, 0x57, 0x6c, 0x2a, 0xb7, 0xb4, 0xce, 0x55, + 0x3d, 0x90, 0xd0, 0x87, 0xdf, 0xc6, 0xf4, 0x72, 0x4c, 0x54, + 0xcd, 0xda, 0x93, 0x0b, 0xb5, 0x40, 0x4b, 0xab, 0x10, 0x85, + 0x6b, 0xf7, 0x45, 0xc7, 0xda, 0x5e, 0x6e, 0x69, 0xdf, 0x68, + 0xdd, 0xb8, 0xdd, 0xd9, 0xc2, 0xe6, 0x85, 0x2e, 0x25, 0x7a, + 0xb4, 0xfc, 0x97, 0x20, + ]; + const sector11: [_]u8 = [ + 0xc9, 0x41, 0xae, 0x7d, 0x5c, 0x0b, 0xa5, 0xb1, 0x31, 0xee, + 0x2e, 0x4b, 0xf8, 0x74, 0x29, 0xc5, 0x39, 0x9b, 0xfe, 0x2e, + 0x51, 0xb8, 0xab, 0xaa, 0x72, 0x49, 0x04, 0x43, 0x2a, 0x4d, + 0x50, 0xdc, + ]; + const crypto11: [_]u8 = [ + 0x30, 0xa3, 0x8f, 0x05, 0x49, 0x1e, 0x4d, 0xda, 0x47, 0x44, + 0xae, 0xff, 0x7f, 0x4d, 0x4e, 0xb5, 0xca, 0xb2, 0xfc, 0xa9, + 0x70, 0x55, 0x1a, 0x05, 0xa1, 0x2a, 0xd8, 0x34, 0x6f, 0x9b, + 0x02, 0xc0, + ]; + const sector14: [_]u8 = [ + 0x2e, 0x95, 0x4d, 0xa7, 0x1f, 0xfb, 0xd3, 0xc8, 0x8e, 0x6f, + 0x07, 0xab, 0x75, 0x53, 0xfc, 0x87, 0x05, 0xe8, 0xe8, 0x5a, + 0x47, 0xbc, 0xc9, 0x12, 0x0c, 0xc4, 0x09, 0x30, 0x86, 0x91, + 0xcc, 0xfa, + ]; + const crypto14: [_]u8 = [ + 0xa1, 0xf8, 0xd6, 0x35, 0xcd, 0x1e, 0xbe, 0xb7, 0x64, 0xcb, + 0xbe, 0x2c, 0x58, 0xb1, 0xc0, 0xd2, 0x4e, 0x8a, 0x9e, 0x09, + 0x76, 0x08, 0xf7, 0x28, 0x94, 0xaf, 0xf8, 0x20, 0x1c, 0xbb, + 0x02, 0x9c, + ]; + + let result: [32]u8 = [0...]; + + let b = xts(); + init(&b, key); + + encrypt(&b, result, sector11, 11); + assert(bytes::equal(crypto11, result)); + encrypt(&b, result, sector14, 14); + assert(bytes::equal(crypto14, result)); + + decrypt(&b, result, crypto11, 11); + assert(bytes::equal(sector11, result)); + decrypt(&b, result, crypto14, 14); + assert(bytes::equal(sector14, result)); +}; diff --git a/crypto/aes/xts/README b/crypto/aes/xts/README @@ -0,0 +1,26 @@ +xts implements the AES-XTS cipher mode as defined in the IEEE Std 1619-2007. + +AES-XTS is an unauthenticated transparent encryption scheme designed for use +cases like disk encryption. Transparent in the sense that the output size is the +same as the input size, and that blocks can be written or read in an arbitrary +order. Similarly to the ECB mode, XTS operates in blocks which are a multiple of +the AES block size. + +The security guarantees can be compared to the ECB ones, but with a different +key for each block. That means following vulnerabilities exist: + +- Traffic analysis: An observer can see when a certain block is written back to + disk with a different value. +- Replay: An adversary may change a block back to an old value, if write access + is available. +- Changing sectors: Changing of the cipher text will result in "random" plain + text. Authentication or error detection can be done before encryption, to + resist such attacks. + +This is a low-level module which implements cryptographic primitives. Direct use +of cryptographic primitives is not recommended for non-experts, as incorrect use +of these primitives can easily lead to the introduction of security +vulnerabilities. Non-experts are advised to use the high-level operations +available in the top-level [[crypto]] module. + +Be advised that Hare's cryptography implementations have not been audited. diff --git a/crypto/aes/xts/xts.ha b/crypto/aes/xts/xts.ha @@ -0,0 +1,121 @@ +use crypto::aes; +use bytes; +use crypto::cipher; + +export type block = struct { + b1: aes::ct64_block, + b2: aes::ct64_block, + x: [aes::BLOCKSIZE]u8, +}; + +// 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(), + ... +}; + +// Initializes the AES-XTS instance. The key length must be 64, 48, or 32 bytes +// (the size of two valid AES keys). +export fn init(b: *block, key: []u8) void = { + assert(len(key) == 64 || len(key) == 48 || len(key) == 32, + "invalid key size"); + let sep: size = len(key) / 2; + + aes::ct64_init(&b.b1, key[..sep]); + aes::ct64_init(&b.b2, key[sep..]); +}; + +def GF_128_FDBK: u8 = 0x87; + +// Encrypts a block given its 'sector' number. The block must be a multiple of +// [[aes::BLOCKSIZE]] (16 bytes) in length. +export fn encrypt(b: *block, dest: []u8, src: []u8, sector: u64) void = { + assert(len(src) == len(dest) && len(src) % aes::BLOCKSIZE == 0); + + let tweak: [aes::BLOCKSIZE]u8 = [0...]; + let carryin: u8 = 0; + let carryout: u8 = 0; + + for (let j = 0z; j < len(tweak); j += 1) { + tweak[j] = (sector & 0xFF): u8; + sector >>= 8; + }; + + cipher::encrypt(&b.b2, tweak, tweak); + + let i = 0z; + for (i + aes::BLOCKSIZE <= len(src); i += aes::BLOCKSIZE) { + for (let j = 0z; j < aes::BLOCKSIZE; j += 1) { + b.x[j] = src[i + j] ^ tweak[j]; + }; + + cipher::encrypt(&b.b1, b.x, b.x); + + for (let j = 0z; j < aes::BLOCKSIZE; j += 1) { + dest[i + j] = b.x[j] ^ tweak[j]; + }; + + // multiply primitive + carryin = 0; + for (let j = 0z; j < aes::BLOCKSIZE; j += 1) { + carryout = (tweak[j] >> 7) & 1; + tweak[j] = ((tweak[j] << 1) + carryin) & 0xff; + carryin = carryout; + }; + + if (carryout != 0) { + tweak[0] ^= GF_128_FDBK; + }; + }; +}; + +// Decrypts a block given its 'sector' number. +export fn decrypt(b: *block, dest: []u8, src: []u8, sector: u64) void = { + assert(len(src) == len(dest) && len(src) % aes::BLOCKSIZE == 0); + + let tweak: [aes::BLOCKSIZE]u8 = [0...]; + let carryin: u8 = 0; + let carryout: u8 = 0; + + for (let j = 0z; j < len(tweak); j += 1) { + tweak[j] = (sector & 0xFF): u8; + sector >>= 8; + }; + + cipher::encrypt(&b.b2, tweak, tweak); + + let i = 0z; + for (i + aes::BLOCKSIZE <= len(src); i += aes::BLOCKSIZE) { + for (let j = 0z; j < aes::BLOCKSIZE; j += 1) { + b.x[j] = src[i + j] ^ tweak[j]; + }; + + cipher::decrypt(&b.b1, b.x, b.x); + + for (let j = 0z; j < aes::BLOCKSIZE; j += 1) { + dest[i + j] = b.x[j] ^ tweak[j]; + }; + + + // multiply primitive + carryin = 0; + for (let j = 0z; j < aes::BLOCKSIZE; j += 1) { + carryout = (tweak[j] >> 7) & 1; + tweak[j] = ((tweak[j] << 1) + carryin) & 0xff; + carryin = carryout; + }; + + if (carryout != 0) { + tweak[0] ^= GF_128_FDBK; + }; + }; +}; + +// Clears the sensible data of AES-XTS instance off the memory. +export fn finish(b: *block) void = { + cipher::finish(&b.b1); + cipher::finish(&b.b2); + bytes::zero(b.x); +}; diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib @@ -222,6 +222,16 @@ crypto_aes() { fi } +crypto_aes_xts() { + if [ $testing -eq 0 ] + then + gen_srcs crypto::aes::xts xts.ha + else + gen_srcs crypto::aes::xts xts.ha +test.ha + fi + gen_ssa crypto::aes::xts crypto::aes crypto::cipher bytes +} + crypto_argon2() { if [ $testing -eq 0 ] then @@ -1155,6 +1165,7 @@ compress::flate compress::zlib crypto crypto::aes +crypto::aes::xts crypto::argon2 crypto::blake2b crypto::chacha diff --git a/stdlib.mk b/stdlib.mk @@ -156,6 +156,12 @@ stdlib_deps_any+=$(stdlib_crypto_aes_any) stdlib_crypto_aes_linux=$(stdlib_crypto_aes_any) stdlib_crypto_aes_freebsd=$(stdlib_crypto_aes_any) +# gen_lib crypto::aes::xts (any) +stdlib_crypto_aes_xts_any=$(HARECACHE)/crypto/aes/xts/crypto_aes_xts-any.o +stdlib_deps_any+=$(stdlib_crypto_aes_xts_any) +stdlib_crypto_aes_xts_linux=$(stdlib_crypto_aes_xts_any) +stdlib_crypto_aes_xts_freebsd=$(stdlib_crypto_aes_xts_any) + # gen_lib crypto::argon2 (any) stdlib_crypto_argon2_any=$(HARECACHE)/crypto/argon2/crypto_argon2-any.o stdlib_deps_any+=$(stdlib_crypto_argon2_any) @@ -704,6 +710,16 @@ $(HARECACHE)/crypto/aes/crypto_aes-any.ssa: $(stdlib_crypto_aes_any_srcs) $(stdl @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::aes \ -t$(HARECACHE)/crypto/aes/crypto_aes.td $(stdlib_crypto_aes_any_srcs) +# crypto::aes::xts (+any) +stdlib_crypto_aes_xts_any_srcs= \ + $(STDLIB)/crypto/aes/xts/xts.ha + +$(HARECACHE)/crypto/aes/xts/crypto_aes_xts-any.ssa: $(stdlib_crypto_aes_xts_any_srcs) $(stdlib_rt) $(stdlib_crypto_aes_$(PLATFORM)) $(stdlib_crypto_cipher_$(PLATFORM)) $(stdlib_bytes_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(HARECACHE)/crypto/aes/xts + @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::aes::xts \ + -t$(HARECACHE)/crypto/aes/xts/crypto_aes_xts.td $(stdlib_crypto_aes_xts_any_srcs) + # crypto::argon2 (+any) stdlib_crypto_argon2_any_srcs= \ $(STDLIB)/crypto/argon2/argon2.ha @@ -1956,6 +1972,12 @@ testlib_deps_any+=$(testlib_crypto_aes_any) testlib_crypto_aes_linux=$(testlib_crypto_aes_any) testlib_crypto_aes_freebsd=$(testlib_crypto_aes_any) +# gen_lib crypto::aes::xts (any) +testlib_crypto_aes_xts_any=$(TESTCACHE)/crypto/aes/xts/crypto_aes_xts-any.o +testlib_deps_any+=$(testlib_crypto_aes_xts_any) +testlib_crypto_aes_xts_linux=$(testlib_crypto_aes_xts_any) +testlib_crypto_aes_xts_freebsd=$(testlib_crypto_aes_xts_any) + # gen_lib crypto::argon2 (any) testlib_crypto_argon2_any=$(TESTCACHE)/crypto/argon2/crypto_argon2-any.o testlib_deps_any+=$(testlib_crypto_argon2_any) @@ -2509,6 +2531,17 @@ $(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(tes @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::aes \ -t$(TESTCACHE)/crypto/aes/crypto_aes.td $(testlib_crypto_aes_any_srcs) +# crypto::aes::xts (+any) +testlib_crypto_aes_xts_any_srcs= \ + $(STDLIB)/crypto/aes/xts/xts.ha \ + $(STDLIB)/crypto/aes/xts/+test.ha + +$(TESTCACHE)/crypto/aes/xts/crypto_aes_xts-any.ssa: $(testlib_crypto_aes_xts_any_srcs) $(testlib_rt) $(testlib_crypto_aes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_bytes_$(PLATFORM)) + @printf 'HAREC \t$@\n' + @mkdir -p $(TESTCACHE)/crypto/aes/xts + @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::aes::xts \ + -t$(TESTCACHE)/crypto/aes/xts/crypto_aes_xts.td $(testlib_crypto_aes_xts_any_srcs) + # crypto::argon2 (+any) testlib_crypto_argon2_any_srcs= \ $(STDLIB)/crypto/argon2/argon2.ha \