diff --git a/vlib/crypto/README.md b/vlib/crypto/README.md index 829ae1b902..09a30e586c 100644 --- a/vlib/crypto/README.md +++ b/vlib/crypto/README.md @@ -32,12 +32,12 @@ fn main() { println('performing encryption') mut encrypted := []byte{len: aes.block_size} - cipher.encrypt(mut encrypted, mut data) + cipher.encrypt(mut encrypted, data) println(encrypted) println('performing decryption') mut decrypted := []byte{len: aes.block_size} - cipher.decrypt(mut decrypted, mut encrypted) + cipher.decrypt(mut decrypted, encrypted) println(decrypted) assert decrypted == data diff --git a/vlib/crypto/aes/aes.v b/vlib/crypto/aes/aes.v index b023b8f04a..5c4bc45b69 100644 --- a/vlib/crypto/aes/aes.v +++ b/vlib/crypto/aes/aes.v @@ -5,6 +5,7 @@ // Last commit: https://github.com/golang/go/commit/691a2d457ab1bf03bd46d4b69e0f93b8993c0055 module aes +import crypto.cipher import crypto.internal.subtle pub const ( @@ -17,6 +18,7 @@ pub const ( // handle only one block of data at a time. In most cases, you // probably want to encrypt and decrypt using [[AesCbc](#AesCbc)] struct AesCipher { + block_size int = aes.block_size mut: enc []u32 dec []u32 @@ -26,7 +28,7 @@ mut: // The key argument should be the AES key, // either 16, 24, or 32 bytes to select // AES-128, AES-192, or AES-256. -pub fn new_cipher(key []byte) AesCipher { +pub fn new_cipher(key []byte) cipher.Block { k := key.len match k { 16, 24, 32 { @@ -50,7 +52,7 @@ pub fn (c &AesCipher) block_size() int { // NOTE: `dst` and `src` are both mutable for performance reasons. // NOTE: `dst` and `src` must both be pre-allocated to the correct length. // NOTE: `dst` and `src` may be the same (overlapping entirely). -pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) { +pub fn (c &AesCipher) encrypt(mut dst []byte, src []byte) { if src.len < aes.block_size { panic('crypto.aes: input not full block') } @@ -58,7 +60,7 @@ pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) { panic('crypto.aes: output not full block') } // if subtle.inexact_overlap(dst[:block_size], src[:block_size]) { - if subtle.inexact_overlap((*dst)[..aes.block_size], (*src)[..aes.block_size]) { + if subtle.inexact_overlap(dst[..aes.block_size], src[..aes.block_size]) { panic('crypto.aes: invalid buffer overlap') } // for now use generic version @@ -69,14 +71,14 @@ pub fn (c &AesCipher) encrypt(mut dst []byte, mut src []byte) { // NOTE: `dst` and `src` are both mutable for performance reasons. // NOTE: `dst` and `src` must both be pre-allocated to the correct length. // NOTE: `dst` and `src` may be the same (overlapping entirely). -pub fn (c &AesCipher) decrypt(mut dst []byte, mut src []byte) { +pub fn (c &AesCipher) decrypt(mut dst []byte, src []byte) { if src.len < aes.block_size { panic('crypto.aes: input not full block') } if dst.len < aes.block_size { panic('crypto.aes: output not full block') } - if subtle.inexact_overlap((*dst)[..aes.block_size], (*src)[..aes.block_size]) { + if subtle.inexact_overlap(dst[..aes.block_size], src[..aes.block_size]) { panic('crypto.aes: invalid buffer overlap') } // for now use generic version diff --git a/vlib/crypto/aes/aes_cbc.v b/vlib/crypto/aes/aes_cbc.v index f2e69b995f..74137a4d28 100644 --- a/vlib/crypto/aes/aes_cbc.v +++ b/vlib/crypto/aes/aes_cbc.v @@ -1,126 +1,2 @@ -// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved. -// Use of this source code is governed by an MIT license -// that can be found in the LICENSE file. -// Cipher block chaining (CBC) mode. -// CBC provides confidentiality by xoring (chaining) each plaintext block -// with the previous ciphertext block before applying the block cipher. -// See NIST SP 800-38A, pp 10-11 -// NOTE this will be moved to crypto.cipher interface (joe-c) +// CBC mode has been moved to crypto.cipher module aes - -import crypto.cipher -import crypto.internal.subtle - -struct AesCbc { -mut: - b AesCipher - block_size int - iv []byte - tmp []byte -} - -// internal -fn new_aes_cbc(b AesCipher, iv []byte) AesCbc { - return AesCbc{ - b: b - block_size: b.block_size() - iv: iv.clone() - tmp: []byte{len: (b.block_size())} - } -} - -// new_cbc returns a `AesCbc` which encrypts in cipher block chaining -// mode, using the given Block. The length of iv must be the same as the -// Block's block size. -pub fn new_cbc(b AesCipher, iv []byte) AesCbc { - if iv.len != b.block_size() { - panic('crypto.cipher.new_cbc_encrypter: IV length must equal block size') - } - return new_aes_cbc(b, iv) -} - -// block_size returns the block size of the checksum in bytes. -pub fn (x &AesCbc) block_size() int { - return x.block_size -} - -// encrypt_blocks encrypts the blocks in `src_` to `dst_`. -// Please note: `dst_` is mutable for performance reasons. -pub fn (x &AesCbc) encrypt_blocks(mut dst_ []byte, src_ []byte) { - unsafe { - mut dst := *dst_ - mut src := src_ - if src.len % x.block_size != 0 { - panic('crypto.cipher: input not full blocks') - } - if dst.len < src.len { - panic('crypto.cipher: output smaller than input') - } - if subtle.inexact_overlap(dst[..src.len], src_) { - panic('crypto.cipher: invalid buffer overlap') - } - mut iv := x.iv - for src.len > 0 { - // Write the xor to dst, then encrypt in place. - cipher.xor_bytes(mut dst[..x.block_size], src[..x.block_size], iv) - x.b.encrypt(mut dst[..x.block_size], mut dst[..x.block_size]) - // Move to the next block with this block as the next iv. - iv = dst[..x.block_size] - if x.block_size >= src.len { - src = [] - } else { - src = src[x.block_size..] - } - dst = dst[x.block_size..] - } - // Save the iv for the next crypt_blocks call. - copy(x.iv, iv) - } -} - -// decrypt_blocks decrypts the blocks in `src` to `dst`. -// Please note: `dst` is mutable for performance reasons. -pub fn (mut x AesCbc) decrypt_blocks(mut dst []byte, src []byte) { - if src.len % x.block_size != 0 { - panic('crypto.cipher: input not full blocks') - } - if dst.len < src.len { - panic('crypto.cipher: output smaller than input') - } - if subtle.inexact_overlap((*dst)[..src.len], src) { - panic('crypto.cipher: invalid buffer overlap') - } - if src.len == 0 { - return - } - // For each block, we need to xor the decrypted data with the previous block's ciphertext (the iv). - // To avoid making a copy each time, we loop over the blocks BACKWARDS. - mut end := src.len - mut start := end - x.block_size - mut prev := start - x.block_size - // Copy the last block of ciphertext in preparation as the new iv. - copy(x.tmp, src[start..end]) - // Loop over all but the first block. - for start > 0 { - mut src_chunk := src[start..end] - x.b.decrypt(mut (*dst)[start..end], mut src_chunk) - cipher.xor_bytes(mut (*dst)[start..end], (*dst)[start..end], src[prev..start]) - end = start - start = prev - prev -= x.block_size - } - // The first block is special because it uses the saved iv. - mut src_chunk := src[start..end] - x.b.decrypt(mut (*dst)[start..end], mut src_chunk) - cipher.xor_bytes(mut (*dst)[start..end], (*dst)[start..end], x.iv) - // Set the new iv to the first block we copied earlier. - x.iv = x.tmp - x.tmp = x.iv -} - -fn (x &AesCbc) set_iv(iv []byte) { - if iv.len != x.iv.len { - panic('cipher: incorrect length IV') - } - copy(x.iv, iv) -} diff --git a/vlib/crypto/aes/aes_test.v b/vlib/crypto/aes/aes_test.v index 34c788335c..a676044c95 100644 --- a/vlib/crypto/aes/aes_test.v +++ b/vlib/crypto/aes/aes_test.v @@ -3,25 +3,16 @@ // that can be found in the LICENSE file. import crypto.aes -fn test_crypto_aes() { - // TEST CBC +fn test_aes() { key := '6368616e676520746869732070617373'.bytes() mut ciphertext := '73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b'.bytes() block := aes.new_cipher(key) - // The IV needs to be unique, but not secure. Therefore it's common to - // include it at the beginning of the ciphertext. - if ciphertext.len < aes.block_size { - panic('ciphertext too short') - } - iv := ciphertext[..aes.block_size] - ciphertext = ciphertext[aes.block_size..] - // CBC mode always works in whole blocks. - if ciphertext.len % aes.block_size != 0 { - panic('ciphertext is not a multiple of the block size') - } - mode := aes.new_cbc(block, iv) - cipher_clone := ciphertext.clone() - mode.encrypt_blocks(mut ciphertext, cipher_clone) - assert ciphertext.hex() == 'c210459b514668ddc44674885e4979215265a6c44431a248421254ef357a8c2a308a8bddf5623af9df91737562041cf1' - println('ok') + + block.encrypt(mut ciphertext, ciphertext.clone()) + assert ciphertext.hex() == '05d1737fe0a7c12088ff4c94d62ccbd9353361393663383562306636623033616339373932653065373537663836396363613330366264336362613163363262' + + block.decrypt(mut ciphertext, ciphertext.clone()) + + assert ciphertext.bytestr() == '73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b' + println('test_aes ok') } diff --git a/vlib/crypto/aes/cypher_generic.v b/vlib/crypto/aes/cypher_generic.v index 19efa7a1d6..e4ef590c40 100644 --- a/vlib/crypto/aes/cypher_generic.v +++ b/vlib/crypto/aes/cypher_generic.v @@ -3,9 +3,11 @@ // that can be found in the LICENSE file. module aes +import crypto.cipher + // new_cipher_generic creates and returns a new cipher.Block // this is the generiv v version, no arch optimisations -fn new_cipher_generic(key []byte) AesCipher { +fn new_cipher_generic(key []byte) cipher.Block { n := key.len + 28 mut c := AesCipher{ enc: []u32{len: n} diff --git a/vlib/crypto/cipher/aes_cbc_test.v b/vlib/crypto/cipher/aes_cbc_test.v new file mode 100644 index 0000000000..1c5071e7c4 --- /dev/null +++ b/vlib/crypto/cipher/aes_cbc_test.v @@ -0,0 +1,32 @@ +// Copyright (c) 2019-2022 Alexander Medvednikov. All rights reserved. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +import crypto.aes +import crypto.cipher + +fn test_aes_cbc() { + key := '6368616e676520746869732070617373'.bytes() + iv := '1234567890123456'.bytes() + str := '73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b' + + mut src := str.bytes() + + aes_cbc_en(mut src, key, iv) + assert src.hex() == '20f36e39e6b73a040d3d39445d0282369033693076d1129a68a3364c00a81c508ec06ab94ff5104c321939fbfb9313c549fd68df100a8a23e8006a863733a8fd' + + aes_cbc_de(mut src, key, iv) + assert src.bytestr() == str + println('test_aes_cbc ok') +} + +fn aes_cbc_en(mut src []byte, key []byte, iv []byte) { + block := aes.new_cipher(key) + mode := cipher.new_cbc(block, iv) + mode.encrypt_blocks(mut src, src.clone()) +} + +fn aes_cbc_de(mut src []byte, key []byte, iv []byte) { + block := aes.new_cipher(key) + mut mode := cipher.new_cbc(block, iv) + mode.decrypt_blocks(mut src, src.clone()) +}