| 1 | // The source code refers to the go standard library, which will be combined with AES in the future. |
| 2 | |
| 3 | // Use of this source code is governed by an MIT license |
| 4 | // that can be found in the LICENSE file. |
| 5 | // Cipher block chaining (CBC) mode. |
| 6 | // CBC provides confidentiality by xoring (chaining) each plaintext block |
| 7 | // with the previous ciphertext block before applying the block cipher. |
| 8 | // See NIST SP 800-38A, pp 10-11 |
| 9 | // NOTE this will be moved to crypto.cipher interface (joe-c) |
| 10 | module cipher |
| 11 | |
| 12 | import crypto.internal.subtle |
| 13 | |
| 14 | struct Cbc { |
| 15 | mut: |
| 16 | b Block |
| 17 | block_size int |
| 18 | iv []u8 |
| 19 | tmp []u8 |
| 20 | } |
| 21 | |
| 22 | // free the resources taken by the Cbc `x` |
| 23 | @[unsafe] |
| 24 | pub fn (mut x Cbc) free() { |
| 25 | $if prealloc { |
| 26 | return |
| 27 | } |
| 28 | unsafe { |
| 29 | // x.b.free() TODO add? |
| 30 | x.iv.free() |
| 31 | x.tmp.free() |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | // internal |
| 36 | fn new_des_cbc(b Block, iv []u8) Cbc { |
| 37 | return Cbc{ |
| 38 | b: b |
| 39 | block_size: b.block_size |
| 40 | iv: iv.clone() |
| 41 | tmp: []u8{len: b.block_size} |
| 42 | } |
| 43 | } |
| 44 | |
| 45 | // new_cbc returns a `DesCbc` which encrypts in cipher block chaining |
| 46 | // mode, using the given Block. The length of iv must be the same as the |
| 47 | // Block's block size. |
| 48 | pub fn new_cbc(b Block, iv []u8) Cbc { |
| 49 | if iv.len != b.block_size { |
| 50 | panic('crypto.cipher.new_cbc_encrypter: IV length must equal block size') |
| 51 | } |
| 52 | return new_des_cbc(b, iv) |
| 53 | } |
| 54 | |
| 55 | // encrypt_blocks encrypts the blocks in `src_` to `dst_`. |
| 56 | // Please note: `dst_` is mutable for performance reasons. |
| 57 | pub fn (mut x Cbc) encrypt_blocks(mut dst_ []u8, src_ []u8) { |
| 58 | unsafe { |
| 59 | mut dst := *dst_ |
| 60 | mut src := src_ |
| 61 | if src.len % x.block_size != 0 { |
| 62 | panic('crypto.cipher: input not full blocks') |
| 63 | } |
| 64 | if dst.len < src.len { |
| 65 | panic('crypto.cipher: output smaller than input') |
| 66 | } |
| 67 | if subtle.inexact_overlap(dst[..src.len], src_) { |
| 68 | panic('crypto.cipher: invalid buffer overlap') |
| 69 | } |
| 70 | mut iv := x.iv |
| 71 | for src.len > 0 { |
| 72 | // Write the xor to dst, then encrypt in place. |
| 73 | xor_bytes(mut dst[..x.block_size], src[..x.block_size], iv) |
| 74 | x.b.encrypt(mut dst[..x.block_size], dst[..x.block_size]) |
| 75 | // Move to the next block with this block as the next iv. |
| 76 | iv = dst[..x.block_size] |
| 77 | if x.block_size >= src.len { |
| 78 | src = [] |
| 79 | } else { |
| 80 | src = src[x.block_size..] |
| 81 | } |
| 82 | dst = dst[x.block_size..] |
| 83 | } |
| 84 | // Save the iv for the next crypt_blocks call. |
| 85 | copy(mut x.iv, iv) |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | // decrypt_blocks decrypts the blocks in `src` to `dst`. |
| 90 | // Please note: `dst` is mutable for performance reasons. |
| 91 | pub fn (mut x Cbc) decrypt_blocks(mut dst []u8, src []u8) { |
| 92 | if src.len % x.block_size != 0 { |
| 93 | panic('crypto.cipher: input not full blocks') |
| 94 | } |
| 95 | if dst.len < src.len { |
| 96 | panic('crypto.cipher: output smaller than input') |
| 97 | } |
| 98 | if subtle.inexact_overlap((*dst)[..src.len], src) { |
| 99 | panic('crypto.cipher: invalid buffer overlap') |
| 100 | } |
| 101 | if src.len == 0 { |
| 102 | return |
| 103 | } |
| 104 | // For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv). |
| 105 | // To avoid making a copy each time, we loop over the blocks BACKWARDS. |
| 106 | mut end := src.len |
| 107 | mut start := end - x.block_size |
| 108 | mut prev := start - x.block_size |
| 109 | // Copy the last block of ciphertext in preparation as the new iv. |
| 110 | copy(mut x.tmp, src[start..end]) |
| 111 | // Loop over all but the first block. |
| 112 | for start > 0 { |
| 113 | src_chunk := src[start..end] |
| 114 | x.b.decrypt(mut (*dst)[start..end], src_chunk) |
| 115 | xor_bytes(mut (*dst)[start..end], (*dst)[start..end], src[prev..start]) |
| 116 | end = start |
| 117 | start = prev |
| 118 | prev -= x.block_size |
| 119 | } |
| 120 | // The first block is special because it uses the saved iv. |
| 121 | src_chunk := src[start..end] |
| 122 | x.b.decrypt(mut (*dst)[start..end], src_chunk) |
| 123 | xor_bytes(mut (*dst)[start..end], (*dst)[start..end], x.iv) |
| 124 | // Set the new iv to the first block we copied earlier. |
| 125 | x.iv, x.tmp = x.tmp, x.iv |
| 126 | } |
| 127 | |
| 128 | fn (mut x Cbc) set_iv(iv []u8) { |
| 129 | if iv.len != x.iv.len { |
| 130 | panic('cipher: incorrect length IV') |
| 131 | } |
| 132 | copy(mut x.iv, iv) |
| 133 | } |
| 134 | |