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:
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 \