feat: v0.1.0

This commit is contained in:
2023-09-04 00:10:23 +08:00
parent 147f29a9dd
commit e7c311eacb
8 changed files with 230 additions and 17 deletions

View File

@@ -1,6 +1,6 @@
[package]
name = "sm4-gcm"
version = "0.0.0"
version = "0.1.0"
edition = "2021"
authors = ["Hatter Jiang"]
repository = "https://git.hatter.ink/hatter/sm4-gcm"

View File

@@ -1,9 +1,21 @@
# sm4-gcm
PENGING...
Encrypt & Decrypt test code:
```rust
fn main() {
let key = Sm4Key([0u8; 16]);
let nonce = [0u8; 12];
let plaintext = b"Hello World!";
let ciphertext = sm4_gcm::sm4_gcm_encrypt(&key, &nonce, plaintext);
println!("Encrypted: {}", hex::encode(&ciphertext));
let decrypted = sm4_gcm::sm4_gcm_decrypt(&key, &nonce, &ciphertext).unwrap();
println!("Decrypted: {}", String::from_utf8_lossy(&decrypted));
}
```
BC test code:
Generate test vector BC test code:
```java
public static void encryptGcmNoPadding(String key, String data, String nonce, String associatedData) throws Exception {
Cipher cipher = Cipher.getInstance("SM4/GCM/NoPadding", BouncyCastleProvider.PROVIDER_NAME);
@@ -20,3 +32,8 @@ public static void encryptGcmNoPadding(String key, String data, String nonce, St
```
Benchmark @MacBook Pro (Retina, 15-inch, Late 2013/2 GHz Quad-Core Intel Core i7)
```text
$ cargo run --release --example bench
SM4/GCM encrypt : 65.69 M/s
```

View File

@@ -1,6 +1,6 @@
use benchmark_simple::{Bench, Options};
use sm4_gcm::Sm4GcmStreamEncryptor;
use sm4_gcm::{Sm4GcmStreamEncryptor, Sm4Key};
fn main() {
let bench = Bench::new();
@@ -21,9 +21,9 @@ fn main() {
fn test_sm4_encrypt(m: &mut [u8]) {
let key = [0u8; 16];
let key = Sm4Key([0u8; 16]);
let nonce = [0u8; 12];
let mut encryptor = Sm4GcmStreamEncryptor::new(key, &nonce);
let mut encryptor = Sm4GcmStreamEncryptor::new(&key, &nonce);
encryptor.update(m);
encryptor.finalize();

12
examples/encrypt.rs Normal file
View File

@@ -0,0 +1,12 @@
use sm4_gcm::Sm4Key;
fn main() {
let key = Sm4Key([0u8; 16]);
let nonce = [0u8; 12];
let plaintext = b"Hello World!";
let ciphertext = sm4_gcm::sm4_gcm_encrypt(&key, &nonce, plaintext);
println!("Encrypted: {}", hex::encode(&ciphertext));
let decrypted = sm4_gcm::sm4_gcm_decrypt(&key, &nonce, &ciphertext).unwrap();
println!("Decrypted: {}", String::from_utf8_lossy(&decrypted));
}

View File

@@ -0,0 +1,157 @@
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, Sm4Key, u8to128};
pub fn sm4_gcm_decrypt(key: &Sm4Key, nonce: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, String> {
sm4_gcm_aad_decrypt(key, nonce, &[], ciphertext)
}
pub fn sm4_gcm_aad_decrypt(key: &Sm4Key, nonce: &[u8], aad: &[u8], ciphertext: &[u8]) -> Result<Vec<u8>, String> {
let mut decryptor = Sm4GcmStreamDecryptor::new(key, nonce);
if aad.len() > 0 {
decryptor.init_adata(aad);
}
let mut msg1 = decryptor.update(ciphertext);
let msg2 = decryptor.finalize()?;
msg1.extend_from_slice(&msg2);
Ok(msg1)
}
pub struct Sm4GcmStreamDecryptor {
cipher: Sm4,
message_buffer: Vec<u8>,
ghash: GHash,
init_nonce: u128,
encryption_nonce: u128,
adata_len: usize,
message_len: usize,
}
impl Sm4GcmStreamDecryptor {
pub fn new(key: &Sm4Key, nonce: &[u8]) -> Self {
let mut key = GenericArray::from(key.0);
let cipher = Sm4::new(&key);
let mut ghash_key = ghash::Key::default();
cipher.encrypt_block(&mut ghash_key);
let ghash = GHash::new(&ghash_key);
ghash_key.zeroize();
key.zeroize();
let mut s = Self {
cipher,
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, bytes: &[u8]) -> Vec<u8> {
self.message_buffer.extend_from_slice(bytes);
let message_buffer_slice = self.message_buffer.as_slice();
let message_buffer_len = message_buffer_slice.len();
if message_buffer_len < 32 {
return Vec::with_capacity(0);
}
let blocks_count = (message_buffer_len / 16) - 1;
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::<Sm4Block>::clone_from_slice(&ctr));
}
self.cipher.encrypt_blocks(&mut blocks);
let encrypted_message = &message_buffer_slice[0..blocks_count * BLOCK_SIZE];
self.ghash.update_padded(encrypted_message);
let mut plaintext_message = encrypted_message.to_vec();
for i in 0..blocks_count {
let chunk = &mut plaintext_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.message_buffer = message_buffer_slice[blocks_count * BLOCK_SIZE..].to_vec();
self.message_len += plaintext_message.len();
plaintext_message
}
pub fn finalize(&mut self) -> Result<Vec<u8>, String> {
let mut plaintext_message = Vec::with_capacity(16);
let message_buffer_len = self.message_buffer.len();
if message_buffer_len > 16 {
// last block and this block len is less than 128 bits
self.encryption_nonce = inc_32(self.encryption_nonce);
let mut ctr = self.encryption_nonce.to_be_bytes();
let block = Block::<Sm4Block>::from_mut_slice(&mut ctr);
self.cipher.encrypt_block(block);
let chunk = &self.message_buffer[0..message_buffer_len - 16];
let msb = msb_s(chunk.len() * 8, block.as_slice());
let y = u8to128(chunk) ^ u8to128(&msb);
plaintext_message.extend_from_slice(&y.to_be_bytes()[16 - chunk.len()..16]);
self.ghash.update_padded(&self.message_buffer[0..message_buffer_len - 16]);
self.message_len += plaintext_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.calculate_tag();
let message_tag = &self.message_buffer[message_buffer_len - 16..];
if message_tag != tag.as_slice() {
Err(format!("Tag mismatch, expected: {:2x}, actual: {:2x}",
u8to128(&tag), u8to128(message_tag)))
} else {
Ok(plaintext_message)
}
}
fn calculate_tag(&mut self) -> Vec<u8> {
let mut bs = self.init_nonce.to_be_bytes().clone();
let block = Block::<Sm4Block>::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::<Sm4Block>::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)
}
}

View File

@@ -5,13 +5,13 @@ 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};
use crate::util::{BLOCK_SIZE, inc_32, msb_s, normalize_nonce, Sm4Block, Sm4Key, u8to128};
pub fn sm4_gcm_encrypt(key: [u8; 16], nonce: &[u8], message: &[u8]) -> Vec<u8> {
pub fn sm4_gcm_encrypt(key: &Sm4Key, nonce: &[u8], message: &[u8]) -> Vec<u8> {
sm4_gcm_aad_encrypt(key, nonce, &[], message)
}
pub fn sm4_gcm_aad_encrypt(key: [u8; 16], nonce: &[u8], aad: &[u8], message: &[u8]) -> Vec<u8> {
pub fn sm4_gcm_aad_encrypt(key: &Sm4Key, nonce: &[u8], aad: &[u8], message: &[u8]) -> Vec<u8> {
let mut encryptor = Sm4GcmStreamEncryptor::new(key, nonce);
if aad.len() > 0 {
encryptor.init_adata(aad);
@@ -34,17 +34,18 @@ pub struct Sm4GcmStreamEncryptor {
}
impl Sm4GcmStreamEncryptor {
pub fn new(key: [u8; 16], nonce: &[u8]) -> Self {
let key = GenericArray::from(key);
let aes = Sm4::new(&key);
pub fn new(key: &Sm4Key, nonce: &[u8]) -> Self {
let mut key = GenericArray::from(key.0);
let cipher = Sm4::new(&key);
let mut ghash_key = ghash::Key::default();
aes.encrypt_block(&mut ghash_key);
cipher.encrypt_block(&mut ghash_key);
let ghash = GHash::new(&ghash_key);
ghash_key.zeroize();
key.zeroize();
let mut s = Self {
cipher: aes,
cipher,
message_buffer: vec![],
ghash,
init_nonce: 0,

View File

@@ -1,7 +1,11 @@
pub use decryptor::Sm4GcmStreamDecryptor;
pub use encryptor::Sm4GcmStreamEncryptor;
pub use crate::decryptor::sm4_gcm_aad_decrypt;
pub use crate::decryptor::sm4_gcm_decrypt;
pub use crate::encryptor::sm4_gcm_aad_encrypt;
pub use crate::encryptor::sm4_gcm_encrypt;
pub use crate::util::Sm4Key;
mod util;
mod encryptor;
@@ -9,13 +13,14 @@ mod decryptor;
// Test vectors are all from BC
#[test]
fn test_encrypt() {
fn test_sm4_gcm() {
let data = vec![
([0u8; 16], [0u8; 12], &[][..], &b"A"[..], "3c0a0922976fa15e835bc96750e730d967"),
([0u8; 16], [0u8; 12], &[][..], &b"hello world"[..], "1587c6137e306fed6a6a5f49539b6dd6fe2b7872c3279636db07c2"),
([0u8; 16], [0xffu8; 12], &[][..], &b"Hello World!"[..], "cba3523bdf74096f3de1f9160a5adb7bf385dea4d50c910e663ec75a"),
([0xffu8; 16], [0xffu8; 12], &[][..], &b"Hello World!"[..], "99eb1206b5b2a9f9c7d7ec4a81de507f5d79938a10ccd91da68d2fb1"),
([0xffu8; 16], [0xffu8; 12], &[0xaau8, 0xbbu8, 0xccu8][..], &b"Hello World!"[..], "99eb1206b5b2a9f9c7d7ec4a7be091388b3049363189e64a47d20c19"),
([0xffu8; 16], [0xffu8; 12], &[0u8, 1u8, 2u8, 3u8][..], &b"Hello World!"[..], "99eb1206b5b2a9f9c7d7ec4ac157a74de0381b3aa170385a113d4f31"),
([0u8; 16], [0u8; 12], &[][..],
&b"Hello World!Hello World!Hello World!Hello World!Hello World!Hello World!"[..],
"3587c6137e304fed6a6a5fc0f78e01e5ea4b604843929848601d4b1600e35c1\
@@ -24,7 +29,11 @@ fn test_encrypt() {
];
for (key, nonce, aad, message, expected) in data {
let encrypted = hex::encode(sm4_gcm_aad_encrypt(key, &nonce, aad, message));
assert_eq!(expected, &encrypted);
let encrypted = sm4_gcm_aad_encrypt(&Sm4Key(key), &nonce, aad, message);
let encrypted_hex = hex::encode(&encrypted);
assert_eq!(expected, &encrypted_hex);
let decrypted = sm4_gcm_aad_decrypt(&Sm4Key(key), &nonce, aad, &encrypted).unwrap();
assert_eq!(message, decrypted.as_slice());
}
}

View File

@@ -1,5 +1,8 @@
use std::error::Error;
use sm4::cipher::BlockSizeUser;
use sm4::cipher::consts::U16;
use zeroize::Zeroize;
pub(crate) struct Sm4Block {}
@@ -9,6 +12,20 @@ impl BlockSizeUser for Sm4Block {
pub(crate) const BLOCK_SIZE: usize = 16;
pub struct Sm4Key(pub [u8; 16]);
impl Sm4Key {
pub fn from_slice(key: &[u8]) -> Result<Self, Box<dyn Error>> {
Ok(Self(key.try_into()?))
}
}
impl Drop for Sm4Key {
fn drop(&mut self) {
self.0.zeroize();
}
}
// R = 11100001 || 0(120)
const R: u128 = 0b11100001 << 120;