cbc.ha (3066B)
1 // SPDX-License-Identifier: MPL-2.0 2 // (c) Hare authors <https://harelang.org> 3 4 use crypto::math; 5 6 export type cbc_mode = struct { 7 b: *block, 8 encrypt: bool, 9 carry: []u8, 10 carrybuf: []u8, 11 }; 12 13 // Creates a cipher block chaining (CBC) mode encryptor. 14 // 15 // The user must supply an initialization vector (IV) equal in length to the 16 // block size of the underlying [[block]] cipher, and a temporary state buffer 17 // whose size is equal to the block size times two. The module providing the 18 // underlying block cipher usually provides constants which define the lengths 19 // of these buffers for static allocation. 20 export fn cbc_encryptor(b: *block, iv: []u8, buf: []u8) cbc_mode = { 21 assert(len(iv) == blocksz(b), 22 "len(iv) must be the same as the block size"); 23 assert(len(buf) == blocksz(b) * 2, 24 "buffer needs to be two times of the block size"); 25 26 const bsz = blocksz(b); 27 let carry = buf[0..bsz]; 28 carry[..] = iv[..]; 29 30 return cbc_mode { 31 b = b, 32 encrypt = true, 33 carry = carry, 34 carrybuf = buf[bsz..], 35 }; 36 }; 37 38 // Creates a cipher block chaining (CBC) mode decryptor. 39 // 40 // The user must supply an initialization vector (IV) equal in length to the 41 // block size of the underlying [[block]] cipher, and a temporary state buffer 42 // whose size is equal to the block size times two. The module providing the 43 // underlying block cipher usually provides constants which define the lengths 44 // of these buffers for static allocation. 45 export fn cbc_decryptor(b: *block, iv: []u8, buf: []u8) cbc_mode = { 46 assert(len(iv) == blocksz(b), 47 "len(iv) must be the same as block length sz"); 48 assert(len(buf) == blocksz(b) * 2, 49 "buffer needs to be two times of the block size"); 50 51 const bsz = blocksz(b); 52 let carry = buf[0..bsz]; 53 carry[..] = iv[..]; 54 55 return cbc_mode { 56 b = b, 57 encrypt = false, 58 carry = carry, 59 carrybuf = buf[bsz..], 60 }; 61 }; 62 63 // Encrypts given blocks with a length that is a multiple of the block size. 64 // In place encryption only works if dest and src point exactly at the 65 // same range. 66 export fn cbc_encrypt(c: *cbc_mode, dest: []u8, src: []u8) void = { 67 const sz = blocksz(c.b); 68 assert(c.encrypt); 69 assert(len(dest) % sz == 0 && len(src) == len(dest), 70 "size of dest and src needs to match and be a multiple of block size"); 71 72 for (let i = 0z; i < len(dest); i += sz) { 73 let eb = i + sz; 74 math::xor(dest[i..eb], src[i..eb], c.carry); 75 encrypt(c.b, dest[i..eb], dest[i..eb]); 76 c.carry[..] = dest[i..eb]; 77 }; 78 }; 79 80 // Decrypts given blocks with a length that is a multiple of the block size. 81 // In place decryption only works if dest and src point exactly at the 82 // same range. 83 export fn cbc_decrypt(c: *cbc_mode, dest: []u8, src: []u8) void = { 84 const sz = blocksz(c.b); 85 assert(!c.encrypt); 86 assert(len(dest) % sz == 0 && len(src) == len(dest), 87 "size of dest and src needs to match and be a multiple of block size"); 88 89 for (let i = 0z; i < len(dest); i += sz) { 90 let eb = i + sz; 91 c.carrybuf[..] = c.carry[..]; 92 c.carry[..] = src[i..eb]; 93 decrypt(c.b, dest[i..eb], src[i..eb]); 94 math::xor(dest[i..eb], dest[i..eb], c.carrybuf); 95 }; 96 };