From 0d07a6423051fffe423a509ea1b016ef5f52eec5 Mon Sep 17 00:00:00 2001 From: Bastian Buck <59334447+bstnbuck@users.noreply.github.com> Date: Thu, 24 Feb 2022 11:06:33 +0100 Subject: [PATCH] crypto: implement Counter (CTR) Mode for AES and DES (#13582) --- vlib/crypto/cipher/aes_ctr_test.v | 29 +++++++++++++ vlib/crypto/cipher/ctr.v | 72 +++++++++++++++++++++++++++++++ vlib/crypto/cipher/des_ctr_test.v | 54 +++++++++++++++++++++++ 3 files changed, 155 insertions(+) create mode 100644 vlib/crypto/cipher/aes_ctr_test.v create mode 100644 vlib/crypto/cipher/ctr.v create mode 100644 vlib/crypto/cipher/des_ctr_test.v diff --git a/vlib/crypto/cipher/aes_ctr_test.v b/vlib/crypto/cipher/aes_ctr_test.v new file mode 100644 index 0000000000..06ad8313a5 --- /dev/null +++ b/vlib/crypto/cipher/aes_ctr_test.v @@ -0,0 +1,29 @@ +import crypto.aes +import crypto.cipher + +fn test_aes_ctr() { + key := '6368616e676520746869732070617373'.bytes() + iv := '1234567890123456'.bytes() + str := '73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b' + + mut src := str.bytes() + + aes_ctr_en(mut src, key, iv) + assert src.hex() == '04380c1470ab2d8a0b3f9b4c1949b8ace811f22bc0a455a8946a3ea7a03e9f8440820f273ce749e955e4adb9e8f6ffde82bae507e0b6164b531d3eeb32f5beb5' + + aes_ctr_de(mut src, key, iv) + assert src.bytestr() == str + println('test_aes_ctr ok') +} + +fn aes_ctr_en(mut src []byte, key []byte, iv []byte) { + block := aes.new_cipher(key) + mode := cipher.new_ctr(block, iv) + mode.xor_key_stream(mut src, src.clone()) +} + +fn aes_ctr_de(mut src []byte, key []byte, iv []byte) { + block := aes.new_cipher(key) + mode := cipher.new_ctr(block, iv) + mode.xor_key_stream(mut src, src.clone()) +} diff --git a/vlib/crypto/cipher/ctr.v b/vlib/crypto/cipher/ctr.v new file mode 100644 index 0000000000..28fb427215 --- /dev/null +++ b/vlib/crypto/cipher/ctr.v @@ -0,0 +1,72 @@ +// The source code refers to the go standard library, which will be combined with AES in the future. +// Use of this source code is governed by an MIT license +// that can be found in the LICENSE file. +// +// Counter (CTR) mode. +// +// CTR converts a block cipher into a stream cipher by +// repeatedly encrypting an incrementing counter and +// xoring the resulting stream of data with the input. +// +// See NIST SP 800-38A, pp 13-15 +module cipher + +import crypto.internal.subtle + +struct Ctr { +mut: + b Block + next []byte + out []byte + out_used int +} + +// new_ctr returns a Ctr which encrypts/decrypts using the given Block in +// counter mode. The length of iv must be the same as the Block's block size. +pub fn new_ctr(b Block, iv []byte) Ctr { + block_size := b.block_size + if iv.len != block_size { + panic('cipher.new_cfb: IV length must be equal block size') + } + return Ctr{ + b: b + out: []byte{len: b.block_size} + next: iv.clone() + out_used: block_size + } +} + +pub fn (x &Ctr) xor_key_stream(mut dst_ []byte, src_ []byte) { + unsafe { + mut dst := *dst_ + mut src := src_ + if dst.len < src.len { + panic('crypto.cipher.xor_key_stream: output smaller than input') + } + + if subtle.inexact_overlap(dst[..src.len], src) { + panic('crypto.cipher.xor_key_stream: invalid buffer overlap') + } + + for src.len > 0 { + if x.out_used == x.out.len { + x.b.encrypt(mut x.out, x.next) + x.out_used = 0 + } + + n := xor_bytes(mut dst, src, x.out[x.out_used..]) + + // increment counter + for i := x.next.len - 1; i >= 0; i-- { + x.next[i]++ + if x.next[i] != 0 { + break + } + } + + dst = dst[n..] + src = src[n..] + x.out_used += n + } + } +} diff --git a/vlib/crypto/cipher/des_ctr_test.v b/vlib/crypto/cipher/des_ctr_test.v new file mode 100644 index 0000000000..9a204877ed --- /dev/null +++ b/vlib/crypto/cipher/des_ctr_test.v @@ -0,0 +1,54 @@ +import crypto.des +import crypto.cipher + +const ( + key = '123456789012345678901234'.bytes() + iv = 'abcdegfh'.bytes() + str = '73c86d43a9d700a253a96c85b0f6b03ac9792e0e757f869cca306bd3cba1c62b' +) + +fn test_triple_des_ctr() { + mut src := str.bytes() + + triple_des_ctr_en(mut src, key, iv) + assert src.hex() == '00d963619a67c84e5aa688174f259f4615768a516ec3fd10c98848f8626120db27fd2932130a4ba5e90acd5c347583bd4705770d41465b1ba11b4a523f449beb' + + triple_des_ctr_de(mut src, key, iv) + assert src.bytestr() == str + println('test_triple_des_ctr ok') +} + +fn test_des_ctr() { + mut src := str.bytes() + + des_ctr_en(mut src, key[..8], iv) + assert src.hex() == '2743e2164b8604565f34bb120eb5dcd0f9db5f9a6498bdb678497c68c3cdda70500f4028159903d77d6b2a7ee4eb710c60e0f816deab4d919287c6a0cd9d2cd3' + + des_ctr_de(mut src, key[..8], iv) + assert src.bytestr() == str + println('test_des_ctr ok') +} + +fn des_ctr_en(mut src []byte, key []byte, iv []byte) { + block := des.new_cipher(key) + mode := cipher.new_ctr(block, iv) + mode.xor_key_stream(mut src, src.clone()) +} + +fn des_ctr_de(mut src []byte, key []byte, iv []byte) { + block := des.new_cipher(key) + mode := cipher.new_ctr(block, iv) + mode.xor_key_stream(mut src, src.clone()) +} + +fn triple_des_ctr_en(mut src []byte, key []byte, iv []byte) { + block := des.new_triple_des_cipher(key) + mode := cipher.new_ctr(block, iv) + mode.xor_key_stream(mut src, src.clone()) +} + +fn triple_des_ctr_de(mut src []byte, key []byte, iv []byte) { + block := des.new_triple_des_cipher(key) + mode := cipher.new_ctr(block, iv) + mode.xor_key_stream(mut src, src.clone()) +}