Skip to content

Commit

Permalink
feat(zuc128_mac): add Zuc128Mac
Browse files Browse the repository at this point in the history
  • Loading branch information
Nugine committed Jan 12, 2025
1 parent 9136c50 commit 893c0c5
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 88 deletions.
89 changes: 2 additions & 87 deletions src/eia3_128.rs
Original file line number Diff line number Diff line change
@@ -1,90 +1,5 @@
//! ZUC Confidentiality Algorithms
use crate::Zuc128Core;

use stdx::slice::SliceExt;

/// t xor keystream in EIA3
#[inline(always)]
fn eia3_xor_t(bits: &mut u32, key: &mut u64, t: &mut u32) {
let k = if *bits & 0x8000_0000 != 0 {
(*key >> 32) as u32
} else {
0
};
*t ^= k;
*bits <<= 1;
*key <<= 1;
}

/// ZUC128 MAC generation algorithm
/// ([GB/T 33133.3-2021](http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=C6D60AE0A7578E970EF2280ABD49F4F0))
///
/// Input:
/// - `ik`: 128bit integrity key
/// - `iv`: 128bit initial vector
/// - `length`: 32bit The number of bits to be encrypted/decrypted.
/// - `m`: the input message
///
/// Output:
/// - `u32`: MAC(Message Authentication Code)
///
/// # Panics
/// + Panics if `length` is greater than the length of `m`
/// + Panics if `length` is greater than `usize::MAX`.
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn zuc128_generate_mac(ik: &[u8; 16], iv: &[u8; 16], length: u32, m: &[u8]) -> u32 {
let bitlen = usize::try_from(length).expect("`length` is greater than `usize::MAX`");
assert!(
bitlen <= m.len() * 8,
"`length` is greater than the length of `m`"
);

let mut zuc = Zuc128Core::new(ik, iv);

let mut t: u32 = 0;

let mut key = {
let k0 = zuc.generate();
let k1 = zuc.generate();
(u64::from(k0) << 32) | u64::from(k1)
};

for chunk in m[..(bitlen / 32 * 4)].as_chunks_::<4>().0 {
let mut bits = u32::from_be_bytes(*chunk);

for _ in 0..32 {
eia3_xor_t(&mut bits, &mut key, &mut t);
}

key |= u64::from(zuc.generate());
}

if bitlen % 32 == 0 {
t ^= (key >> 32) as u32;
t ^= key as u32;
} else {
let i = bitlen / 32 * 4;

let mut bits = match (bitlen % 32) / 8 {
0 => u32::from_be_bytes([m[i], 0, 0, 0]),
1 => u32::from_be_bytes([m[i], m[i + 1], 0, 0]),
2 => u32::from_be_bytes([m[i], m[i + 1], m[i + 2], 0]),
3 => u32::from_be_bytes([m[i], m[i + 1], m[i + 2], m[i + 3]]),
_ => unreachable!(),
};

for _ in 0..(bitlen % 32) {
eia3_xor_t(&mut bits, &mut key, &mut t);
}

t ^= (key >> 32) as u32;
t ^= zuc.generate();
}

t
}
use crate::zuc128_generate_mac;

/// 128-EIA3: 3GPP confidentiality algorithm
/// ([EEA3-EIA3-specification](https://www.gsma.com/solutions-and-impact/technologies/security/wp-content/uploads/2019/05/EEA3_EIA3_specification_v1_8.pdf))
Expand Down Expand Up @@ -268,7 +183,7 @@ mod tests {
}
}

#[should_panic(expected = "`length` is greater than the length of `m`")]
#[should_panic(expected = "assertion failed: bitlen <= tail.len() * 8")]
#[test]
fn invalid_input() {
let x = &EXAMPLE2;
Expand Down
6 changes: 5 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@ mod zuc;
mod zuc128;
pub use self::zuc128::{Zuc128, Zuc128Core};

mod zuc128_mac;
pub use self::zuc128_mac::zuc128_generate_mac;
pub use self::zuc128_mac::Zuc128Mac;

mod eea3_128;
pub use eea3_128::{eea3_128_encrypt, zuc128_xor_encrypt};

mod eia3_128;
pub use eia3_128::{eia3_128_generate_mac, zuc128_generate_mac};
pub use eia3_128::eia3_128_generate_mac;

mod zuc256;
pub use self::zuc256::{Zuc256, Zuc256Core};
Expand Down
78 changes: 78 additions & 0 deletions src/zuc128_mac.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::mac::{MacCore, MacKeyPair};
use crate::zuc128::Zuc128Core;

use stdx::default::default;

/// ZUC128 MAC generation algorithm
/// ([GB/T 33133.3-2021](http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=C6D60AE0A7578E970EF2280ABD49F4F0))
///
/// Input:
/// - `ik`: 128bit integrity key
/// - `iv`: 128bit initial vector
/// - `length`: 32bit The number of bits to be encrypted/decrypted.
/// - `m`: the input message
///
/// Output:
/// - `u32`: MAC(Message Authentication Code)
///
/// # Panics
/// + Panics if `length` is greater than the bit length of `m`
/// + Panics if `length` is greater than `usize::MAX`.
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn zuc128_generate_mac(ik: &[u8; 16], iv: &[u8; 16], length: u32, m: &[u8]) -> u32 {
let bitlen = usize::try_from(length).expect("`length` is greater than `usize::MAX`");
Zuc128Mac::compute(ik, iv, m, bitlen)
}

/// ZUC128 MAC generator
/// ([GB/T 33133.3-2021](http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=C6D60AE0A7578E970EF2280ABD49F4F0))
pub struct Zuc128Mac(MacCore<Zuc128Core, u32>);

impl Zuc128Mac {
/// Create a new ZUC128 MAC generator
#[must_use]
pub fn new(ik: &[u8; 16], iv: &[u8; 16]) -> Self {
let mut zuc = Zuc128Core::new(ik, iv);
let key = u64::gen_key_pair(&mut zuc);

Self(MacCore {
zuc,
key,
tag: 0,
rem: default(),
cnt: 0,
})
}

/// Update the MAC generator with the bytes of a message
pub fn update(&mut self, msg: &[u8]) {
self.0.update(msg);
}

/// Finish the MAC generation and return the MAC
#[allow(clippy::cast_possible_truncation)]
#[must_use]
pub fn finish(mut self, tail: &[u8], bitlen: usize) -> u32 {
let final_bitlen = self.0.finish(tail, bitlen);

let mut tag = self.0.tag;
let key = self.0.key;

tag ^= key.high();

if final_bitlen == 0 {
tag ^= key as u32;
} else {
tag ^= self.0.zuc.generate();
}

tag
}

/// Compute the MAC of a message
#[must_use]
pub fn compute(ik: &[u8; 16], iv: &[u8; 16], msg: &[u8], bitlen: usize) -> u32 {
Self::new(ik, iv).finish(msg, bitlen)
}
}

0 comments on commit 893c0c5

Please sign in to comment.