From 2b42ea98832d407caf7b0b25b84a3cb2cee55c43 Mon Sep 17 00:00:00 2001 From: 688862 <96077242+688862@users.noreply.github.com> Date: Fri, 7 Jan 2022 19:51:37 +0800 Subject: [PATCH] crypto: add a crypto.des module (#13065) --- vlib/crypto/cipher/cbc.v | 121 +++++++++++++++++++ vlib/crypto/cipher/cipher.v | 55 +++++++++ vlib/crypto/cipher/des_cbc_test.v | 54 +++++++++ vlib/crypto/des/block.v | 189 ++++++++++++++++++++++++++++++ vlib/crypto/des/const.v | 154 ++++++++++++++++++++++++ vlib/crypto/des/des.v | 169 ++++++++++++++++++++++++++ vlib/crypto/des/des_test.v | 51 ++++++++ 7 files changed, 793 insertions(+) create mode 100644 vlib/crypto/cipher/cbc.v create mode 100644 vlib/crypto/cipher/cipher.v create mode 100644 vlib/crypto/cipher/des_cbc_test.v create mode 100644 vlib/crypto/des/block.v create mode 100644 vlib/crypto/des/const.v create mode 100644 vlib/crypto/des/des.v create mode 100644 vlib/crypto/des/des_test.v diff --git a/vlib/crypto/cipher/cbc.v b/vlib/crypto/cipher/cbc.v new file mode 100644 index 0000000000..45b2e87f5f --- /dev/null +++ b/vlib/crypto/cipher/cbc.v @@ -0,0 +1,121 @@ +// 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. +// 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) +module cipher + +import crypto.internal.subtle + +struct Cbc { +mut: + b Block + block_size int + iv []byte + tmp []byte +} + +// internal +fn new_des_cbc(b Block, iv []byte) Cbc { + return Cbc{ + b: b + block_size: b.block_size + iv: iv.clone() + tmp: []byte{len: b.block_size} + } +} + +// new_cbc returns a `DesCbc` 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 Block, iv []byte) Cbc { + if iv.len != b.block_size { + panic('crypto.cipher.new_cbc_encrypter: IV length must equal block size') + } + return new_des_cbc(b, iv) +} + +// encrypt_blocks encrypts the blocks in `src_` to `dst_`. +// Please note: `dst_` is mutable for performance reasons. +pub fn (x &Cbc) 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. + xor_bytes(mut dst[..x.block_size], src[..x.block_size], iv) + x.b.encrypt(mut dst[..x.block_size], 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 Cbc) 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 { + src_chunk := src[start..end] + x.b.decrypt(mut (*dst)[start..end], src_chunk) + 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. + src_chunk := src[start..end] + x.b.decrypt(mut (*dst)[start..end], src_chunk) + 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 &Cbc) set_iv(iv []byte) { + if iv.len != x.iv.len { + panic('cipher: incorrect length IV') + } + copy(x.iv, iv) +} diff --git a/vlib/crypto/cipher/cipher.v b/vlib/crypto/cipher/cipher.v new file mode 100644 index 0000000000..81db26ee71 --- /dev/null +++ b/vlib/crypto/cipher/cipher.v @@ -0,0 +1,55 @@ +// The source code refers to the go standard library, which will be combined with AES in the future. + +module cipher + +// A Block represents an implementation of block cipher +// using a given key. It provides the capability to encrypt +// or decrypt individual blocks. The mode implementations +// extend that capability to streams of blocks. +interface Block { + block_size int // block_size returns the cipher's block size. + encrypt(mut dst []byte, src []byte) // Encrypt encrypts the first block in src into dst. + // Dst and src must overlap entirely or not at all. + decrypt(mut dst []byte, src []byte) // Decrypt decrypts the first block in src into dst. + // Dst and src must overlap entirely or not at all. +} + +// A Stream represents a stream cipher. +interface Stream { + // xor_key_stream XORs each byte in the given slice with a byte from the + // cipher's key stream. Dst and src must overlap entirely or not at all. + // + // If len(dst) < len(src), xor_key_stream should panic. It is acceptable + // to pass a dst bigger than src, and in that case, xor_key_stream will + // only update dst[:len(src)] and will not touch the rest of dst. + // + // Multiple calls to xor_key_stream behave as if the concatenation of + // the src buffers was passed in a single run. That is, Stream + // maintains state and does not reset at each xor_key_stream call. + xor_key_stream(mut dst []byte, src []byte) +} + +// A BlockMode represents a block cipher running in a block-based mode (CBC, +// ECB etc). +interface BlockMode { + block_size int // block_size returns the mode's block size. + crypt_blocks(mut dst []byte, src []byte) // crypt_blocks encrypts or decrypts a number of blocks. The length of + // src must be a multiple of the block size. Dst and src must overlap + // entirely or not at all. + // + // If len(dst) < len(src), crypt_blocks should panic. It is acceptable + // to pass a dst bigger than src, and in that case, crypt_blocks will + // only update dst[:len(src)] and will not touch the rest of dst. + // + // Multiple calls to crypt_blocks behave as if the concatenation of + // the src buffers was passed in a single run. That is, BlockMode + // maintains state and does not reset at each crypt_blocks call. +} + +// Utility routines + +// fn dup(p []byte) []byte { +// q := make([]byte, p.len) +// copy(q, p) +// return q +// } diff --git a/vlib/crypto/cipher/des_cbc_test.v b/vlib/crypto/cipher/des_cbc_test.v new file mode 100644 index 0000000000..1640cd985f --- /dev/null +++ b/vlib/crypto/cipher/des_cbc_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_cbc() { + mut src := str.bytes() + + triple_des_cbc_en(mut src, key, iv) + assert src.hex() == '59d657007dc96062e8fffd574afda480ddba21fa06337ac5676eb4e40db256f2ab31ed442c0e4a82ef59b96292d24902c86b20c50bd8506e387775ca58f8c4fe' + + triple_des_cbc_de(mut src, key, iv) + assert src.bytestr() == str + println('test_triple_des_cbc ok') +} + +fn test_des_cbc() { + mut src := str.bytes() + + des_cbc_en(mut src, key[..8], iv) + assert src.hex() == '198f94ca5989900ce73b26c3ce0005fa747b74d81e8cc5d529f96c1a2e7748d39f9900b9049cbfda35ef720d495b134f4f7dd2d7d3d910af488cdccd27d9f057' + + des_cbc_de(mut src, key[..8], iv) + assert src.bytestr() == str + println('test_des_cbc ok') +} + +fn des_cbc_en(mut src []byte, key []byte, iv []byte) { + block := des.new_cipher(key) + mode := cipher.new_cbc(block, iv) + mode.encrypt_blocks(mut src, src.clone()) +} + +fn des_cbc_de(mut src []byte, key []byte, iv []byte) { + block := des.new_cipher(key) + mut mode := cipher.new_cbc(block, iv) + mode.decrypt_blocks(mut src, src.clone()) +} + +fn triple_des_cbc_en(mut src []byte, key []byte, iv []byte) { + block := des.new_triple_des_cipher(key) + mode := cipher.new_cbc(block, iv) + mode.encrypt_blocks(mut src, src.clone()) +} + +fn triple_des_cbc_de(mut src []byte, key []byte, iv []byte) { + block := des.new_triple_des_cipher(key) + mut mode := cipher.new_cbc(block, iv) + mode.decrypt_blocks(mut src, src.clone()) +} diff --git a/vlib/crypto/des/block.v b/vlib/crypto/des/block.v new file mode 100644 index 0000000000..619068b669 --- /dev/null +++ b/vlib/crypto/des/block.v @@ -0,0 +1,189 @@ +// The source code refers to the go standard library, which can be merged with AES later + +module des + +import encoding.binary + +fn feistel(ll u32, rr u32, k0 u64, k1 u64) (u32, u32) { + mut l := ll + mut r := rr + mut t := r ^ u32(k0 >> 32) + + l ^= feistel_box[7][t & 0x3f] ^ feistel_box[5][(t >> 8) & 0x3f] ^ feistel_box[3][(t >> 16) & 0x3f] ^ feistel_box[1][(t >> 24) & 0x3f] + + t = ((r << 28) | (r >> 4)) ^ u32(k0) + l ^= feistel_box[6][t & 0x3f] ^ feistel_box[4][(t >> 8) & 0x3f] ^ feistel_box[2][(t >> 16) & 0x3f] ^ feistel_box[0][(t >> 24) & 0x3f] + + t = l ^ u32(k1 >> 32) + r ^= feistel_box[7][t & 0x3f] ^ feistel_box[5][(t >> 8) & 0x3f] ^ feistel_box[3][(t >> 16) & 0x3f] ^ feistel_box[1][(t >> 24) & 0x3f] + + t = ((l << 28) | (l >> 4)) ^ u32(k1) + r ^= feistel_box[6][t & 0x3f] ^ feistel_box[4][(t >> 8) & 0x3f] ^ feistel_box[2][(t >> 16) & 0x3f] ^ feistel_box[0][(t >> 24) & 0x3f] + + return l, r +} + +fn crypt_block(subkeys []u64, mut dst []byte, src []byte, decrypt bool) { + mut b := binary.big_endian_u64(src) + b = permute_initial_block(b) + + mut left, mut right := u32(b >> 32), u32(b) + + left = (left << 1) | (left >> 31) + right = (right << 1) | (right >> 31) + + if decrypt { + for i := 0; i < 8; i++ { + left, right = feistel(left, right, subkeys[15 - 2 * i], subkeys[15 - (2 * i + 1)]) + } + } else { + for i := 0; i < 8; i++ { + left, right = feistel(left, right, subkeys[2 * i], subkeys[2 * i + 1]) + } + } + + left = (left << 31) | (left >> 1) + right = (right << 31) | (right >> 1) + + // switch left & right and perform final permutation + pre_output := (u64(right) << 32) | u64(left) + binary.big_endian_put_u64(mut dst, permute_final_block(pre_output)) +} + +// Encrypt one block from src into dst, using the subkeys. +pub fn encrypt_block(subkeys []u64, mut dst []byte, src []byte) { + crypt_block(subkeys, mut dst, src, false) +} + +// Decrypt one block from src into dst, using the subkeys. +fn decrypt_block(subkeys []u64, mut dst []byte, src []byte) { + crypt_block(subkeys, mut dst, src, true) +} + +// general purpose function to perform DES block permutations +fn permute_block(src u64, permutation []byte) u64 { + mut block := u64(0) + for position, n in permutation { + bit := (src >> u64(u8(n))) & 1 + block |= bit << u64((permutation.len - 1) - position) + } + return block +} + +// permuteInitial_block is equivalent to the permutation defined +// by initialPermutation. +fn permute_initial_block(b u64) u64 { + // block = b7 b6 b5 b4 b3 b2 b1 b0 (8 bytes) + mut block := b + mut b1 := block >> 48 + mut b2 := block << 48 + block ^= b1 ^ b2 ^ b1 << 48 ^ b2 >> 48 + + // block = b1 b0 b5 b4 b3 b2 b7 b6 + b1 = block >> 32 & 0xff00ff + b2 = (block & 0xff00ff00) + block ^= b1 << 32 ^ b2 ^ b1 << 8 ^ b2 << 24 // exchange b0 b4 with b3 b7 + + // block is now b1 b3 b5 b7 b0 b2 b4 b6, the permutation: + // ... 8 + // ... 24 + // ... 40 + // ... 56 + // 7 6 5 4 3 2 1 0 + // 23 22 21 20 19 18 17 16 + // ... 32 + // ... 48 + + // exchange 4,5,6,7 with 32,33,34,35 etc. + b1 = block & 0x0f0f00000f0f0000 + b2 = block & 0x0000f0f00000f0f0 + block ^= b1 ^ b2 ^ b1 >> 12 ^ b2 << 12 + + // block is the permutation: + // + // [+8] [+40] + // + // 7 6 5 4 + // 23 22 21 20 + // 3 2 1 0 + // 19 18 17 16 [+32] + + // exchange 0,1,4,5 with 18,19,22,23 + b1 = block & 0x3300330033003300 + b2 = block & 0x00cc00cc00cc00cc + block ^= b1 ^ b2 ^ b1 >> 6 ^ b2 << 6 + + // block is the permutation: + // 15 14 + // 13 12 + // 11 10 + // 9 8 + // 7 6 + // 5 4 + // 3 2 + // 1 0 [+16] [+32] [+64] + + // exchange 0,2,4,6 with 9,11,13,15: + b1 = block & 0xaaaaaaaa55555555 + block ^= b1 ^ b1 >> 33 ^ b1 << 33 + + // block is the permutation: + // 6 14 22 30 38 46 54 62 + // 4 12 20 28 36 44 52 60 + // 2 10 18 26 34 42 50 58 + // 0 8 16 24 32 40 48 56 + // 7 15 23 31 39 47 55 63 + // 5 13 21 29 37 45 53 61 + // 3 11 19 27 35 43 51 59 + // 1 9 17 25 33 41 49 57 + return block +} + +// permuteInitial_block is equivalent to the permutation defined +// by finalPermutation. +fn permute_final_block(b u64) u64 { + // Perform the same bit exchanges as permuteInitial_block + // but in reverse order. + mut block := b + mut b1 := block & 0xaaaaaaaa55555555 + block ^= b1 ^ b1 >> 33 ^ b1 << 33 + + b1 = block & 0x3300330033003300 + mut b2 := block & 0x00cc00cc00cc00cc + block ^= b1 ^ b2 ^ b1 >> 6 ^ b2 << 6 + + b1 = block & 0x0f0f00000f0f0000 + b2 = block & 0x0000f0f00000f0f0 + block ^= b1 ^ b2 ^ b1 >> 12 ^ b2 << 12 + + b1 = block >> 32 & 0xff00ff + b2 = (block & 0xff00ff00) + block ^= b1 << 32 ^ b2 ^ b1 << 8 ^ b2 << 24 + + b1 = block >> 48 + b2 = block << 48 + block ^= b1 ^ b2 ^ b1 << 48 ^ b2 >> 48 + return block +} + +// creates 16 28-bit blocks rotated according +// to the rotation schedule +fn ks_rotate(ain u32) []u32 { + mut out := []u32{len: 16} + mut last := ain + for i := 0; i < 16; i++ { + // 28-bit circular left shift + left := (last << (4 + ks_rotations[i])) >> 4 + right := (last << 4) >> (32 - ks_rotations[i]) + out[i] = left | right + last = out[i] + } + return out +} + +// Expand 48-bit input to 64-bit, with each 6-bit block padded by extra two bits at the top. +// By doing so, we can have the input blocks (four bits each), and the key blocks (six bits each) well-aligned without +// extra shifts/rotations for alignments. +fn unpack(x u64) u64 { + return ((x >> (6 * 1)) & 0xff) << (8 * 0) | ((x >> (6 * 3)) & 0xff) << (8 * 1) | ((x >> (6 * 5)) & 0xff) << (8 * 2) | ((x >> (6 * 7)) & 0xff) << (8 * 3) | ((x >> (6 * 0)) & 0xff) << (8 * 4) | ((x >> (6 * 2)) & 0xff) << (8 * 5) | ((x >> (6 * 4)) & 0xff) << (8 * 6) | ((x >> (6 * 6)) & 0xff) << (8 * 7) +} diff --git a/vlib/crypto/des/const.v b/vlib/crypto/des/const.v new file mode 100644 index 0000000000..cb103efc41 --- /dev/null +++ b/vlib/crypto/des/const.v @@ -0,0 +1,154 @@ +// The source code refers to the go standard library, which can be merged with AES later + +// Package des implements the Data Encryption Standard (DES) and the +// Triple Data Encryption Algorithm (TDEA) as defined +// in U.S. Federal Information Processing Standards Publication 46-3. +// +// DES is cryptographically broken and should not be used for secure +// applications. + +module des + +// Used to perform an initial permutation of a 64-bit input block. +// const initial_permutation = [byte(6), 14, 22, 30, 38, 46, 54, 62, 4, 12, 20, 28, 36, 44, 52, 60, +// 2, 10, 18, 26, 34, 42, 50, 58, 0, 8, 16, 24, 32, 40, 48, 56, 7, 15, 23, 31, 39, 47, 55, 63, +// 5, 13, 21, 29, 37, 45, 53, 61, 3, 11, 19, 27, 35, 43, 51, 59, 1, 9, 17, 25, 33, 41, 49, 57] + +// // Used to perform a final permutation of a 4-bit preoutput block. This is the +// // inverse of initialPermutation +// const final_permutation = [byte(24), 56, 16, 48, 8, 40, 0, 32, 25, 57, 17, 49, 9, 41, 1, 33, 26, +// 58, 18, 50, 10, 42, 2, 34, 27, 59, 19, 51, 11, 43, 3, 35, 28, 60, 20, 52, 12, 44, 4, 36, 29, +// 61, 21, 53, 13, 45, 5, 37, 30, 62, 22, 54, 14, 46, 6, 38, 31, 63, 23, 55, 15, 47, 7, 39] + +// // Used to expand an input block of 32 bits, producing an output block of 48 +// // bits. +// const expansion_function = [byte(0), 31, 30, 29, 28, 27, 28, 27, 26, 25, 24, 23, 24, 23, 22, 21, +// 20, 19, 20, 19, 18, 17, 16, 15, 16, 15, 14, 13, 12, 11, 12, 11, 10, 9, 8, 7, 8, 7, 6, 5, 4, +// 3, 4, 3, 2, 1, 0, 31] + +// // Yields a 32-bit output from a 32-bit input +// const permutation_function = [byte(16), 25, 12, 11, 3, 20, 4, 15, 31, 17, 9, 6, 27, 14, 1, 22, +// 30, 24, 8, 18, 0, 5, 29, 23, 13, 19, 2, 26, 10, 21, 28, 7] + +// Used in the key schedule to select 56 bits +// from a 64-bit input. +const permuted_choice1 = [byte(7), 15, 23, 31, 39, 47, 55, 63, 6, 14, 22, 30, 38, 46, 54, 62, 5, + 13, 21, 29, 37, 45, 53, 61, 4, 12, 20, 28, 1, 9, 17, 25, 33, 41, 49, 57, 2, 10, 18, 26, 34, + 42, 50, 58, 3, 11, 19, 27, 35, 43, 51, 59, 36, 44, 52, 60] + +// Used in the key schedule to produce each subkey by selecting 48 bits from +// the 56-bit input +const permuted_choice2 = [byte(42), 39, 45, 32, 55, 51, 53, 28, 41, 50, 35, 46, 33, 37, 44, 52, + 30, 48, 40, 49, 29, 36, 43, 54, 15, 4, 25, 19, 9, 1, 26, 16, 5, 11, 23, 8, 12, 7, 17, 0, 22, + 3, 10, 14, 6, 20, 27, 24] + +// 8 S-boxes composed of 4 rows and 16 columns +// Used in the DES cipher function +// const s_boxes = [ +// [ +// [u8(14), 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7], +// [u8(0), 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8], +// [u8(4), 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0], +// [u8(15), 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13], +// ], +// [ +// [u8(15), 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10], +// [u8(3), 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5], +// [u8(0), 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15], +// [u8(13), 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], +// ], +// [ +// [u8(10), 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8], +// [u8(13), 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1], +// [u8(13), 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7], +// [u8(1), 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], +// ], +// [ +// [u8(7), 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15], +// [u8(13), 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9], +// [u8(10), 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4], +// [u8(3), 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14], +// ], +// [ +// [u8(2), 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9], +// [u8(14), 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6], +// [u8(4), 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14], +// [u8(11), 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3], +// ], +// [ +// [u8(12), 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11], +// [u8(10), 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8], +// [u8(9), 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6], +// [u8(4), 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13], +// ], +// [ +// [u8(4), 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1], +// [u8(13), 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6], +// [u8(1), 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2], +// [u8(6), 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], +// ], +// [ +// [u8(13), 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7], +// [u8(1), 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2], +// [u8(7), 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8], +// [u8(2), 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], +// ], +// ] + +const feistel_box = [ + [u32(16843776), 0, 65536, 16843780, 16842756, 66564, 4, 65536, 1024, 16843776, 16843780, 1024, + 16778244, 16842756, 16777216, 4, 1028, 16778240, 16778240, 66560, 66560, 16842752, 16842752, + 16778244, 65540, 16777220, 16777220, 65540, 0, 1028, 66564, 16777216, 65536, 16843780, + 4, 16842752, 16843776, 16777216, 16777216, 1024, 16842756, 65536, 66560, 16777220, 1024, + 4, 16778244, 66564, 16843780, 65540, 16842752, 16778244, 16777220, 1028, 66564, 16843776, + 1028, 16778240, 16778240, 0, 65540, 66560, 0, 16842756], + [u32(2148565024), 2147516416, 32768, 1081376, 1048576, 32, 2148532256, 2147516448, 2147483680, + 2148565024, 2148564992, 2147483648, 2147516416, 1048576, 32, 2148532256, 1081344, 1048608, + 2147516448, 0, 2147483648, 32768, 1081376, 2148532224, 1048608, 2147483680, 0, 1081344, + 32800, 2148564992, 2148532224, 32800, 0, 1081376, 2148532256, 1048576, 2147516448, 2148532224, + 2148564992, 32768, 2148532224, 2147516416, 32, 2148565024, 1081376, 32, 32768, 2147483648, + 32800, 2148564992, 1048576, 2147483680, 1048608, 2147516448, 2147483680, 1048608, 1081344, + 0, 2147516416, 32800, 2147483648, 2148532256, 2148565024, 1081344], + [u32(520), 134349312, 0, 134348808, 134218240, 0, 131592, 134218240, 131080, 134217736, 134217736, + 131072, 134349320, 131080, 134348800, 520, 134217728, 8, 134349312, 512, 131584, 134348800, + 134348808, 131592, 134218248, 131584, 131072, 134218248, 8, 134349320, 512, 134217728, + 134349312, 134217728, 131080, 520, 131072, 134349312, 134218240, 0, 512, 131080, 134349320, + 134218240, 134217736, 512, 0, 134348808, 134218248, 131072, 134217728, 134349320, 8, 131592, + 131584, 134217736, 134348800, 134218248, 520, 134348800, 131592, 8, 134348808, 131584], + [u32(8396801), 8321, 8321, 128, 8396928, 8388737, 8388609, 8193, 0, 8396800, 8396800, 8396929, + 129, 0, 8388736, 8388609, 1, 8192, 8388608, 8396801, 128, 8388608, 8193, 8320, 8388737, + 1, 8320, 8388736, 8192, 8396928, 8396929, 129, 8388736, 8388609, 8396800, 8396929, 129, + 0, 0, 8396800, 8320, 8388736, 8388737, 1, 8396801, 8321, 8321, 128, 8396929, 129, 1, 8192, + 8388609, 8193, 8396928, 8388737, 8193, 8320, 8388608, 8396801, 128, 8388608, 8192, 8396928], + [u32(256), 34078976, 34078720, 1107296512, 524288, 256, 1073741824, 34078720, 1074266368, 524288, + 33554688, 1074266368, 1107296512, 1107820544, 524544, 1073741824, 33554432, 1074266112, + 1074266112, 0, 1073742080, 1107820800, 1107820800, 33554688, 1107820544, 1073742080, 0, + 1107296256, 34078976, 33554432, 1107296256, 524544, 524288, 1107296512, 256, 33554432, + 1073741824, 34078720, 1107296512, 1074266368, 33554688, 1073741824, 1107820544, 34078976, + 1074266368, 256, 33554432, 1107820544, 1107820800, 524544, 1107296256, 1107820800, 34078720, + 0, 1074266112, 1107296256, 524544, 33554688, 1073742080, 524288, 0, 1074266112, 34078976, + 1073742080], + [u32(536870928), 541065216, 16384, 541081616, 541065216, 16, 541081616, 4194304, 536887296, + 4210704, 4194304, 536870928, 4194320, 536887296, 536870912, 16400, 0, 4194320, 536887312, + 16384, 4210688, 536887312, 16, 541065232, 541065232, 0, 4210704, 541081600, 16400, 4210688, + 541081600, 536870912, 536887296, 16, 541065232, 4210688, 541081616, 4194304, 16400, 536870928, + 4194304, 536887296, 536870912, 16400, 536870928, 541081616, 4210688, 541065216, 4210704, + 541081600, 0, 541065232, 16, 16384, 541065216, 4210704, 16384, 4194320, 536887312, 0, + 541081600, 536870912, 4194320, 536887312], + [u32(2097152), 69206018, 67110914, 0, 2048, 67110914, 2099202, 69208064, 69208066, 2097152, + 0, 67108866, 2, 67108864, 69206018, 2050, 67110912, 2099202, 2097154, 67110912, 67108866, + 69206016, 69208064, 2097154, 69206016, 2048, 2050, 69208066, 2099200, 2, 67108864, 2099200, + 67108864, 2099200, 2097152, 67110914, 67110914, 69206018, 69206018, 2, 2097154, 67108864, + 67110912, 2097152, 69208064, 2050, 2099202, 69208064, 2050, 67108866, 69208066, 69206016, + 2099200, 0, 2, 69208066, 0, 2099202, 69206016, 2048, 67108866, 67110912, 2048, 2097154], + [u32(268439616), 4096, 262144, 268701760, 268435456, 268439616, 64, 268435456, 262208, 268697600, + 268701760, 266240, 268701696, 266304, 4096, 64, 268697600, 268435520, 268439552, 4160, + 266240, 262208, 268697664, 268701696, 4160, 0, 0, 268697664, 268435520, 268439552, 266304, + 262144, 266304, 262144, 268701696, 4096, 64, 268697664, 4096, 266304, 268439552, 64, + 268435520, 268697600, 268697664, 268435456, 262144, 268439616, 0, 268701760, 262208, + 268435520, 268697600, 268439552, 268439616, 0, 268701760, 266240, 266240, 4160, 4160, 262208, + 268435456, 268701696], +] + +// Size of left rotation per round in each half of the key schedule +const ks_rotations = [u8(1), 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1] diff --git a/vlib/crypto/des/des.v b/vlib/crypto/des/des.v new file mode 100644 index 0000000000..0aeca8bfd0 --- /dev/null +++ b/vlib/crypto/des/des.v @@ -0,0 +1,169 @@ +// The source code refers to the go standard library + +module des + +import crypto.cipher +import crypto.internal.subtle +import encoding.binary + +const block_size = 8 + +// A tripleDesCipher is an instance of TripleDES encryption. +struct TripleDesCipher { + block_size int = des.block_size +mut: + cipher1 DesCipher + cipher2 DesCipher + cipher3 DesCipher +} + +// DesCipher is an instance of DES encryption. +struct DesCipher { + block_size int = des.block_size +mut: + subkeys [16]u64 +} + +// NewCipher creates and returns a new cipher.Block. +pub fn new_cipher(key []byte) cipher.Block { + if key.len != 8 { + panic('crypto.aes: invalid key size') + } + + mut c := DesCipher{} + c.generate_subkeys(key) + return c +} + +// creates 16 56-bit subkeys from the original key +fn (mut c DesCipher) generate_subkeys(key_bytes []byte) { + // feistel_box_once.do(initFeistel_box) + + // apply PC1 permutation to key + key := binary.big_endian_u64(key_bytes) + permuted_key := permute_block(key, permuted_choice1[..]) + + // rotate halves of permuted key according to the rotation schedule + left_rotations := ks_rotate(u32(permuted_key >> 28)) + right_rotations := ks_rotate(u32(permuted_key << 4) >> 4) + + // generate subkeys + for i := 0; i < 16; i++ { + // combine halves to form 56-bit input to PC2 + pc2_input := u64(left_rotations[i]) << 28 | u64(right_rotations[i]) + // apply PC2 permutation to 7 byte input + c.subkeys[i] = unpack(permute_block(pc2_input, permuted_choice2[..])) + } +} + +pub fn (c &DesCipher) encrypt(mut dst []byte, src []byte) { + if src.len < des.block_size { + panic('crypto/des: input not full block') + } + if dst.len < des.block_size { + panic('crypto/des: output not full block') + } + if subtle.inexact_overlap(dst[..des.block_size], src[..des.block_size]) { + panic('crypto/des: invalid buffer overlap') + } + encrypt_block(c.subkeys[..], mut dst, src) +} + +pub fn (c &DesCipher) decrypt(mut dst []byte, src []byte) { + if src.len < des.block_size { + panic('crypto/des: input not full block') + } + if dst.len < des.block_size { + panic('crypto/des: output not full block') + } + if subtle.inexact_overlap(dst[..des.block_size], src[..des.block_size]) { + panic('crypto/des: invalid buffer overlap') + } + decrypt_block(c.subkeys[..], mut dst, src) +} + +// NewTripleDesCipher creates and returns a new cipher.Block. +pub fn new_triple_des_cipher(key []byte) cipher.Block { + if key.len != 24 { + panic('crypto.des: invalid key size') + } + mut c := TripleDesCipher{} + c.cipher1.generate_subkeys(key[..8]) + c.cipher2.generate_subkeys(key[8..16]) + c.cipher3.generate_subkeys(key[16..]) + return c +} + +pub fn (c &TripleDesCipher) encrypt(mut dst []byte, src []byte) { + if src.len < des.block_size { + panic('crypto/des: input not full block') + } + if dst.len < des.block_size { + panic('crypto/des: output not full block') + } + if subtle.inexact_overlap(dst[..des.block_size], src[..des.block_size]) { + panic('crypto/des: invalid buffer overlap') + } + + mut b := binary.big_endian_u64(src) + b = permute_initial_block(b) + mut left, mut right := u32(b >> 32), u32(b) + + left = (left << 1) | (left >> 31) + right = (right << 1) | (right >> 31) + + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher1.subkeys[2 * i], c.cipher1.subkeys[2 * i + 1]) + } + for i := 0; i < 8; i++ { + right, left = feistel(right, left, c.cipher2.subkeys[15 - 2 * i], c.cipher2.subkeys[15 - ( + 2 * i + 1)]) + } + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher3.subkeys[2 * i], c.cipher3.subkeys[2 * i + 1]) + } + + left = (left << 31) | (left >> 1) + right = (right << 31) | (right >> 1) + + pre_output := (u64(right) << 32) | u64(left) + binary.big_endian_put_u64(mut dst, permute_final_block(pre_output)) +} + +pub fn (c &TripleDesCipher) decrypt(mut dst []byte, src []byte) { + if src.len < des.block_size { + panic('crypto/des: input not full block') + } + if dst.len < des.block_size { + panic('crypto/des: output not full block') + } + if subtle.inexact_overlap(dst[..des.block_size], src[..des.block_size]) { + panic('crypto/des: invalid buffer overlap') + } + + mut b := binary.big_endian_u64(src) + b = permute_initial_block(b) + + mut left, mut right := u32(b >> 32), u32(b) + + left = (left << 1) | (left >> 31) + right = (right << 1) | (right >> 31) + + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher3.subkeys[15 - 2 * i], c.cipher3.subkeys[15 - ( + 2 * i + 1)]) + } + for i := 0; i < 8; i++ { + right, left = feistel(right, left, c.cipher2.subkeys[2 * i], c.cipher2.subkeys[2 * i + 1]) + } + for i := 0; i < 8; i++ { + left, right = feistel(left, right, c.cipher1.subkeys[15 - 2 * i], c.cipher1.subkeys[15 - ( + 2 * i + 1)]) + } + + left = (left << 31) | (left >> 1) + right = (right << 31) | (right >> 1) + + pre_output := (u64(right) << 32) | u64(left) + binary.big_endian_put_u64(mut dst, permute_final_block(pre_output)) +} diff --git a/vlib/crypto/des/des_test.v b/vlib/crypto/des/des_test.v new file mode 100644 index 0000000000..13f3182bf9 --- /dev/null +++ b/vlib/crypto/des/des_test.v @@ -0,0 +1,51 @@ +import crypto.des + +const ( + key = '123456789012345678901234'.bytes() + iv = 'abcdegfh'.bytes() + str = 'aaaaaaaa' +) + +fn test_triple_des() { + mut src := str.bytes() + + triple_des_en(mut src, key, iv) + assert src.hex() == '45902cf00aa1df46' + + triple_des_de(mut src, key, iv) + assert src.bytestr() == str + println('test_triple_des ok') +} + +fn test_des() { + mut src := str.bytes() + + des_en(mut src, key[..8], iv) + assert src.hex() == '72dca13c37223cf0' + + des_de(mut src, key[..8], iv) + assert src.bytestr() == str + + println('test_des ok') +} + +fn des_en(mut src []byte, key []byte, iv []byte) { + block := des.new_cipher(key) + block.encrypt(mut src, src.clone()) +} + +fn des_de(mut src []byte, key []byte, iv []byte) { + block := des.new_cipher(key) + block.decrypt(mut src, src.clone()) +} + +fn triple_des_en(mut src []byte, key []byte, iv []byte) { + block := des.new_triple_des_cipher(key) + block.encrypt(mut src, src.clone()) +} + +fn triple_des_de(mut src []byte, key []byte, iv []byte) { + block := des.new_triple_des_cipher(key) + inbuf := src.clone() + block.decrypt(mut src, inbuf) +}