commit 46fe35af85c8fb3cc9512712f03d205b4ce94dd5
parent d8bd3c7d124ae7972e16b97ee721a96c1afe15bf
Author: Armin Preiml <apreiml@strohwolke.at>
Date: Sun, 14 Nov 2021 18:16:06 +0100
add cipher block chaining mode to crypto::cipher
Signed-off-by: Armin Preiml <apreiml@strohwolke.at>
Diffstat:
4 files changed, 141 insertions(+), 7 deletions(-)
diff --git a/crypto/aes/cbc+test.ha b/crypto/aes/cbc+test.ha
@@ -0,0 +1,51 @@
+use crypto::cipher;
+use bytes;
+
+@test fn cbc_encrypt_decrypt() void = {
+ const key: [_]u8 = [
+ 0xde, 0x05, 0x73, 0x76, 0xbf, 0x1b, 0x0c, 0xd1,
+ 0x26, 0x66, 0xa9, 0x63, 0x30, 0xce, 0x1c, 0xde,
+ ];
+
+ const iv: [_]u8 = [
+ 0x2c, 0x80, 0x9e, 0x1e, 0xc3, 0x02, 0x28, 0xae,
+ 0x0a, 0x19, 0xea, 0xef, 0x73, 0x6c, 0x9a, 0xbb,
+ ];
+
+ const plain: [_]u8 = [
+ 0x50, 0xe6, 0xd6, 0x87, 0xeb, 0xb4, 0x65, 0x9c,
+ 0xfc, 0x70, 0x7e, 0x00, 0xe6, 0xd6, 0xef, 0x23,
+ 0xd6, 0x11, 0xca, 0x11, 0x08, 0x2a, 0xde, 0x07,
+ 0xf2, 0xe4, 0x09, 0x17, 0x5a, 0xac, 0xdf, 0x6c,
+ 0x9b, 0x08, 0x82, 0x92, 0x7e, 0x2c, 0xbc, 0xb7,
+ 0x74, 0xe0, 0xe8, 0x7b, 0xe5, 0x72, 0x86, 0x6f,
+ 0x92, 0x37, 0x85, 0xb0, 0x5e, 0x53, 0xd0, 0xcb,
+ 0x42, 0x70, 0x3d, 0x49, 0x6a, 0x48, 0x47, 0x36,
+ ];
+
+ const cipher: [_]u8 = [
+ 0xb4, 0x6b, 0x0b, 0x36, 0xe1, 0xd7, 0x84, 0x1a,
+ 0xf4, 0x3e, 0x74, 0x29, 0x72, 0x9c, 0x60, 0x60,
+ 0xe1, 0x8a, 0xfc, 0x87, 0x3b, 0xe3, 0x18, 0x89,
+ 0x8c, 0x5b, 0x5f, 0x12, 0xe6, 0x71, 0x0d, 0x7e,
+ 0xf3, 0xfd, 0xa6, 0x82, 0x4e, 0xa8, 0x86, 0x9f,
+ 0xaf, 0x60, 0xe9, 0x16, 0x05, 0xda, 0xe1, 0xa5,
+ 0xaa, 0xc0, 0x57, 0x20, 0xf8, 0xbf, 0x17, 0x37,
+ 0x74, 0x8d, 0xd5, 0x3d, 0xf8, 0x0c, 0x97, 0x05,
+ ];
+
+ let result: [64]u8 = [0...];
+
+ let b = ct64(key);
+ let cbc = cipher::cbc_encryptor(&b, iv[..]);
+ defer cipher::cbc_finish(&cbc);
+
+ cipher::cbc_encrypt(&cbc, result, plain);
+ assert(bytes::equal(cipher, result));
+
+ let cbcd = cipher::cbc_decryptor(&b, iv[..]);
+ defer cipher::cbc_finish(&cbcd);
+
+ cipher::cbc_decrypt(&cbcd, result, cipher);
+ assert(bytes::equal(plain, result));
+};
diff --git a/crypto/cipher/cbc.ha b/crypto/cipher/cbc.ha
@@ -0,0 +1,79 @@
+use bytes;
+use crypto::math;
+
+export type cbc_mode = struct {
+ b: *block,
+ encrypt: bool,
+ carry: []u8,
+};
+
+// Creates a cipher block chaining mode encryptor. The encryptor will
+// keep its state, so data can be encrypted using multiple cbc_encrypt calls.
+// Needs to be finished with [[crypto::cipher::cbc_finish]]
+export fn cbc_encryptor(b: *block, iv: []u8) cbc_mode = {
+ assert(len(iv) == sz(b), "len(iv) must be the same as block length sz");
+
+ return cbc_mode {
+ b = b,
+ encrypt = true,
+ carry = alloc(iv),
+ };
+};
+
+// Creates a cipher block chaining mode decryptor. The decryptor will
+// keep its state, so data can be encrypted using multiple cbc_decrypt calls.
+// Needs to be finished with [[crypto::cipher::cbc_finish]]
+export fn cbc_decryptor(b: *block, iv: []u8) cbc_mode = {
+ assert(len(iv) == sz(b), "len(iv) must be the same as block length sz");
+
+ return cbc_mode {
+ b = b,
+ encrypt = false,
+ carry = alloc(iv),
+ };
+};
+
+// Frees a cipher block chaining mode encryptor or decryptor
+export fn cbc_finish(c: *cbc_mode) void = {
+ free(c.carry);
+};
+
+// Encrypts given blocks with a length that is a multiple of the block size.
+// In place encryption only works if dest and src point exactly at the
+// same range.
+export fn cbc_encrypt(c: *cbc_mode, dest: []u8, src: []u8) void = {
+ let sz = sz(c.b);
+
+ assert(c.encrypt);
+ assert(len(dest) % sz == 0 && len(src) == len(dest),
+ "size of dest and src needs to match and be a multiple of block size");
+
+ for (let i = 0z; i < len(dest); i += sz) {
+ let eb = i + sz;
+ crypto::math::xor(dest[i..eb], src[i..eb], c.carry);
+ encrypt(c.b, dest[i..eb], dest[i..eb]);
+ bytes::copy(c.carry, dest[i..eb]);
+ };
+};
+
+// Decrypts given blocks with a length that is a multiple of the block size.
+// In place decryption only works if dest and src point exactly at the
+// same range.
+export fn cbc_decrypt(c: *cbc_mode, dest: []u8, src: []u8) void = {
+ let sz = sz(c.b);
+
+ assert(!c.encrypt);
+ assert(len(dest) % sz == 0 && len(src) == len(dest),
+ "size of dest and src needs to match and be a multiple of block size");
+
+ let carry = alloc(c.carry);
+ defer free(carry);
+
+ for (let i = 0z; i < len(dest); i += sz) {
+ let eb = i + sz;
+ bytes::copy(carry, c.carry);
+ bytes::copy(c.carry, src[i..eb]);
+ decrypt(c.b, dest[i..eb], src[i..eb]);
+ crypto::math::xor(dest[i..eb], dest[i..eb], carry);
+ };
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -201,8 +201,8 @@ crypto_aes() {
gensrcs_crypto_aes
gen_ssa crypto::aes crypto::cipher crypto::math endian
else
- gensrcs_crypto_aes ct64+test.ha
- gen_ssa crypto::aes crypto::cipher crypto::math endian bytes
+ gensrcs_crypto_aes ct64+test.ha cbc+test.ha
+ gen_ssa crypto::aes bytes crypto::cipher crypto::math endian
fi
}
@@ -225,8 +225,9 @@ crypto_blake2b() {
crypto_cipher() {
gen_srcs crypto::cipher \
+ cbc.ha \
cipher.ha
- gen_ssa crypto::cipher
+ gen_ssa crypto::cipher bytes crypto::math
}
crypto_math() {
diff --git a/stdlib.mk b/stdlib.mk
@@ -673,9 +673,10 @@ $(HARECACHE)/crypto/blake2b/crypto_blake2b-any.ssa: $(stdlib_crypto_blake2b_any_
# crypto::cipher (+any)
stdlib_crypto_cipher_any_srcs= \
+ $(STDLIB)/crypto/cipher/cbc.ha \
$(STDLIB)/crypto/cipher/cipher.ha
-$(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_srcs) $(stdlib_rt)
+$(HARECACHE)/crypto/cipher/crypto_cipher-any.ssa: $(stdlib_crypto_cipher_any_srcs) $(stdlib_rt) $(stdlib_bytes_$(PLATFORM)) $(stdlib_crypto_math_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/crypto/cipher
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Ncrypto::cipher \
@@ -2355,9 +2356,10 @@ $(TESTCACHE)/compress/zlib/compress_zlib-any.ssa: $(testlib_compress_zlib_any_sr
# crypto::aes (+any)
testlib_crypto_aes_any_srcs= \
$(STDLIB)/crypto/aes/aes_ct64.ha \
- $(STDLIB)/crypto/aes/ct64+test.ha
+ $(STDLIB)/crypto/aes/ct64+test.ha \
+ $(STDLIB)/crypto/aes/cbc+test.ha
-$(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(testlib_rt) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM)) $(testlib_bytes_$(PLATFORM))
+$(TESTCACHE)/crypto/aes/crypto_aes-any.ssa: $(testlib_crypto_aes_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_cipher_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM)) $(testlib_endian_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto/aes
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::aes \
@@ -2377,9 +2379,10 @@ $(TESTCACHE)/crypto/blake2b/crypto_blake2b-any.ssa: $(testlib_crypto_blake2b_any
# crypto::cipher (+any)
testlib_crypto_cipher_any_srcs= \
+ $(STDLIB)/crypto/cipher/cbc.ha \
$(STDLIB)/crypto/cipher/cipher.ha
-$(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_srcs) $(testlib_rt)
+$(TESTCACHE)/crypto/cipher/crypto_cipher-any.ssa: $(testlib_crypto_cipher_any_srcs) $(testlib_rt) $(testlib_bytes_$(PLATFORM)) $(testlib_crypto_math_$(PLATFORM))
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/crypto/cipher
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Ncrypto::cipher \