feat: v0.1.0
This commit is contained in:
@@ -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"
|
||||
|
||||
21
README.md
21
README.md
@@ -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
|
||||
```
|
||||
|
||||
@@ -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
12
examples/encrypt.rs
Normal 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));
|
||||
}
|
||||
157
src/decryptor.rs
157
src/decryptor.rs
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
15
src/lib.rs
15
src/lib.rs
@@ -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());
|
||||
}
|
||||
}
|
||||
17
src/util.rs
17
src/util.rs
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user