hare

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

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:
Acrypto/aes/cbc+test.ha | 51+++++++++++++++++++++++++++++++++++++++++++++++++++
Acrypto/cipher/cbc.ha | 79+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 7++++---
Mstdlib.mk | 11+++++++----
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 \