From 8f372057b3a345f11f6c1eae29bf64dced845bb6 Mon Sep 17 00:00:00 2001 From: Hatter Jiang Date: Sun, 3 Sep 2023 23:09:17 +0800 Subject: [PATCH] feat: encrypt works --- Cargo.toml | 8 ++- examples/test.rs | 47 +++++++++++++++ src/encryptor.rs | 148 +++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 17 ++++++ src/main.rs | 3 - src/util.rs | 84 +++++++++++++++++++++++++++ 6 files changed, 303 insertions(+), 4 deletions(-) create mode 100644 examples/test.rs create mode 100644 src/encryptor.rs create mode 100644 src/lib.rs delete mode 100644 src/main.rs create mode 100644 src/util.rs diff --git a/Cargo.toml b/Cargo.toml index d73d697..3cc175c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,4 +11,10 @@ categories = ["cryptography"] [dependencies] -sm4 = "0.5.1" +ghash = { version = "0.5.0", features = ["zeroize"] } +sm4 = { version = "0.5.1", features = ["zeroize"] } +zeroize = "1.6.0" + +[dev-dependencies] +hex = "0.4.3" +benchmark-simple = "0.1.8" \ No newline at end of file diff --git a/examples/test.rs b/examples/test.rs new file mode 100644 index 0000000..e914e0a --- /dev/null +++ b/examples/test.rs @@ -0,0 +1,47 @@ +use benchmark_simple::{Bench, Options}; + +use sm4_gcm::Sm4GcmStreamEncryptor; + +fn main() { + let key = [0u8; 16]; + let nonce = [0u8; 12]; + let mut e = Sm4GcmStreamEncryptor::new(key, &nonce); + + println!("{}", hex::encode(&key)); + println!("{}", hex::encode(&nonce)); + + let a = e.update(b"hello world"); + let (b, t) = e.finalize(); + + let mut enc = a.clone(); + enc.extend_from_slice(&b); + enc.extend_from_slice(&t); + + println!("{}", hex::encode(&enc)); + + // ---------------------------------------------------------------------- + let bench = Bench::new(); + let mut m = vec![0xd0u8; 16384]; + + let options = &Options { + iterations: 1_000, + warmup_iterations: 1_00, + min_samples: 5, + max_samples: 10, + max_rsd: 1.0, + ..Default::default() + }; + + let res = bench.run(options, || test_sm4_encrypt(&mut m)); + println!("SM4/GCM encrypt : {}", res.throughput(m.len() as _)); +} + + +fn test_sm4_encrypt(m: &mut [u8]) { + let key = [0u8; 16]; + let nonce = [0u8; 12]; + let mut encryptor = Sm4GcmStreamEncryptor::new(key, &nonce); + + encryptor.update(m); + encryptor.finalize(); +} \ No newline at end of file diff --git a/src/encryptor.rs b/src/encryptor.rs new file mode 100644 index 0000000..2e87d9f --- /dev/null +++ b/src/encryptor.rs @@ -0,0 +1,148 @@ +use ghash::GHash; +use ghash::universal_hash::UniversalHash; +use sm4::cipher::{Block, BlockEncrypt, KeyInit}; +use sm4::cipher::generic_array::GenericArray; +use sm4::Sm4; +use zeroize::Zeroize; + +use crate::util::{BLOCK_SIZE, inc_32, msb_s, normalize_nonce, Sm4Block, u8to128}; + +pub fn sm4_gcm_encrypt(key: [u8; 16], nonce: &[u8], message: &[u8]) -> Vec { + sm4_gcm_aad_encrypt(key, nonce, &[], message) +} + +pub fn sm4_gcm_aad_encrypt(key: [u8; 16], nonce: &[u8], aad: &[u8], message: &[u8]) -> Vec { + let mut encryptor = Sm4GcmStreamEncryptor::new(key, nonce); + if aad.len() > 0 { + encryptor.init_adata(aad); + } + let mut enc1 = encryptor.update(message); + let (enc2, tag) = encryptor.finalize(); + enc1.extend_from_slice(&enc2); + enc1.extend_from_slice(&tag); + enc1 +} + +pub struct Sm4GcmStreamEncryptor { + cipher: Sm4, + message_buffer: Vec, + ghash: GHash, + init_nonce: u128, + encryption_nonce: u128, + adata_len: usize, + message_len: usize, +} + +impl Sm4GcmStreamEncryptor { + pub fn new(key: [u8; 16], nonce: &[u8]) -> Self { + let key = GenericArray::from(key); + let aes = Sm4::new(&key); + + let mut ghash_key = ghash::Key::default(); + aes.encrypt_block(&mut ghash_key); + let ghash = GHash::new(&ghash_key); + ghash_key.zeroize(); + + let mut s = Self { + cipher: aes, + message_buffer: vec![], + ghash, + init_nonce: 0, + encryption_nonce: 0, + adata_len: 0, + message_len: 0, + }; + let (_, normalized_nonce) = s.normalize_nonce(nonce); + s.init_nonce = normalized_nonce; + s.encryption_nonce = normalized_nonce; + s + } + + pub fn init_adata(&mut self, adata: &[u8]) { + if adata.len() > 0 { + self.adata_len += adata.len(); + self.ghash.update_padded(adata); + } + } + + pub fn update(&mut self, message: &[u8]) -> Vec { + self.message_buffer.extend_from_slice(message); + let message_buffer_slice = self.message_buffer.as_slice(); + if message_buffer_slice.len() < BLOCK_SIZE { + return Vec::with_capacity(0); + } + let blocks_count = message_buffer_slice.len() / BLOCK_SIZE; + let mut blocks = Vec::with_capacity(blocks_count); + for _ in 0..blocks_count { + self.encryption_nonce = inc_32(self.encryption_nonce); + let ctr = self.encryption_nonce.to_be_bytes(); + blocks.push(Block::::clone_from_slice(&ctr)); + } + self.cipher.encrypt_blocks(&mut blocks); + + let mut encrypted_message = message_buffer_slice[0..blocks_count * BLOCK_SIZE].to_vec(); + for i in 0..blocks_count { + let chunk = &mut encrypted_message[i * BLOCK_SIZE..(i + 1) * BLOCK_SIZE]; + let block = blocks[i].as_slice(); + for k in 0..BLOCK_SIZE { + chunk[k] ^= block[k]; + } + } + self.ghash.update_padded(&encrypted_message); + self.message_buffer = message_buffer_slice[blocks_count * BLOCK_SIZE..].to_vec(); + self.message_len += encrypted_message.len(); + + encrypted_message + } + + pub fn finalize(&mut self) -> (Vec, Vec) { + let mut final_encrypted_message = Vec::with_capacity(BLOCK_SIZE); + if !self.message_buffer.is_empty() { + // last block and this block len may less than 128 bits (16 bytes) + self.encryption_nonce = inc_32(self.encryption_nonce); + let mut ctr = self.encryption_nonce.to_be_bytes(); + let block = Block::::from_mut_slice(&mut ctr); + self.cipher.encrypt_block(block); + + let chunk = self.message_buffer.as_slice(); + let msb = msb_s(chunk.len() * 8, block.as_slice()); + let y = u8to128(chunk) ^ u8to128(&msb); + final_encrypted_message.extend_from_slice(&y.to_be_bytes()[16 - chunk.len()..16]); + self.ghash.update_padded(&final_encrypted_message); + self.message_len += final_encrypted_message.len(); + } + let adata_bit_len = self.adata_len * 8; + let message_bit_len = self.message_len * 8; + let mut adata_and_message_len = Vec::with_capacity(BLOCK_SIZE); + adata_and_message_len.extend_from_slice(&(adata_bit_len as u64).to_be_bytes()); + adata_and_message_len.extend_from_slice(&(message_bit_len as u64).to_be_bytes()); + self.ghash.update_padded(&adata_and_message_len); + + + let tag = self.compute_tag(); + + (final_encrypted_message, tag) + } + + fn compute_tag(&mut self) -> Vec { + let mut bs = self.init_nonce.to_be_bytes().clone(); + let block = Block::::from_mut_slice(&mut bs); + self.cipher.encrypt_block(block); + let ghash = self.ghash.clone().finalize(); + let tag_trunk = ghash.as_slice(); + let y = u8to128(&tag_trunk) ^ u8to128(&block.as_slice()); + y.to_be_bytes().to_vec() + } + + fn ghash_key(&mut self) -> u128 { + let mut block = [0u8; BLOCK_SIZE]; + let block = Block::::from_mut_slice(&mut block); + self.cipher.encrypt_block(block); + u8to128(&block.as_slice()) + } + + fn normalize_nonce(&mut self, nonce_bytes: &[u8]) -> (u128, u128) { + let ghash_key = self.ghash_key(); + normalize_nonce(ghash_key, nonce_bytes) + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4e1c28e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,17 @@ +mod util; +mod encryptor; + +pub use encryptor::Sm4GcmStreamEncryptor; +use crate::encryptor::sm4_gcm_aad_encrypt; + +#[test] +fn test_encrypt() { + let data = vec![ + ([0u8; 16], [0u8; 12], &[], &b"hello world", "1587c6137e306fed6a6a5f49539b6dd6fe2b7872c3279636db07c2") + ]; + + for (key, nonce, aad, message, expected) in data { + let encrypted = hex::encode(sm4_gcm_aad_encrypt(key, &nonce, aad, *message)); + assert_eq!(expected, &encrypted); + } +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs deleted file mode 100644 index e7a11a9..0000000 --- a/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..4b176ee --- /dev/null +++ b/src/util.rs @@ -0,0 +1,84 @@ +use sm4::cipher::BlockSizeUser; +use sm4::cipher::consts::U16; + +pub(crate) struct Sm4Block {} + +impl BlockSizeUser for Sm4Block { + type BlockSize = U16; +} + +pub(crate) const BLOCK_SIZE: usize = 16; + + +// R = 11100001 || 0(120) +const R: u128 = 0b11100001 << 120; + +pub(crate) fn gmul_128(x: u128, y: u128) -> u128 { + let mut z = 0u128; + let mut v = y; + for i in (0..128).rev() { + let xi = (x >> i) & 1; + if xi != 0 { + z ^= v; + } + v = match v & 1 == 0 { + true => { v >> 1 } + false => { (v >> 1) ^ R } + }; + } + z +} + +pub(crate) fn ghash(key: u128, messages: &[u128]) -> u128 { + let mut y = 0u128; + for message in messages { + let yi = gmul_128(y ^ message, key); + y = yi; + } + y +} + +pub(crate) fn normalize_nonce(ghash_key: u128, nonce_bytes: &[u8]) -> (u128, u128) { + let nonce = u8to128(nonce_bytes); + let normalized_nonce = match nonce_bytes.len() == 12 { + true => { + nonce << 32 | 0x00000001 + } + false => { + let mut iv_padding = vec![]; + // s = 128[len(iv) / 128] - len(iv) + let s = 128 * (((nonce_bytes.len() * 8) + 128 - 1) / 128) - (nonce_bytes.len() * 8); + iv_padding.push(nonce << s); + iv_padding.push((nonce_bytes.len() * 8) as u128); + ghash(ghash_key, &iv_padding) + } + }; + (ghash_key, normalized_nonce) +} + +pub(crate) fn msb_s(s: usize, bytes: &[u8]) -> Vec { + let mut result = vec![]; + let n = s / 8; + let remain = s % 8; + result.extend_from_slice(&bytes[0..n]); + if remain > 0 { + result.push(bytes[n] >> (8 - remain)); + } + result +} + +#[inline] +pub(crate) fn u8to128(bytes: &[u8]) -> u128 { + bytes.iter().rev().enumerate().fold(0, |acc, (i, &byte)| { + acc | (byte as u128) << (i * 8) + }) +} + +// incs(X)=MSBlen(X)-s(X) || [int(LSBs(X))+1 mod 2^s]s +#[inline] +pub(crate) fn inc_32(bits: u128) -> u128 { + let msb = bits >> 32; + let mut lsb = (bits & 0xffffffff) as u32; + lsb = lsb.wrapping_add(1); + msb << 32 | lsb as u128 +}