Skip to content

Commit

Permalink
Improve native HMAC speed by another 100%
Browse files Browse the repository at this point in the history
The allocations and zip/iterations were causing another huge performance
hit (at least in debug mode).
Switch HMAC code to use slices instead, and recode the iterations in the
init code to be simpler.

Signed-off-by: Simo Sorce <[email protected]>
  • Loading branch information
simo5 committed Dec 20, 2024
1 parent 9af34b5 commit 6dab1a0
Showing 1 changed file with 40 additions and 41 deletions.
81 changes: 40 additions & 41 deletions src/native/hmac.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ use crate::misc::zeromem;

use constant_time_eq::constant_time_eq;

/* max algo right now is SHA3_224 with 144 byts blocksize,
* use slightly larger for good measure (and alignment) */
const IPAD_INIT: [u8; 160] = [0x36; 160];
const OPAD_INIT: [u8; 160] = [0x5c; 160];

/* HMAC spec From FIPS 198-1 */

#[derive(Debug)]
Expand All @@ -22,19 +27,19 @@ pub struct HMACOperation {
hashlen: usize,
blocklen: usize,
outputlen: usize,
state: Vec<u8>,
ipad: Vec<u8>,
opad: Vec<u8>,
state: [u8; 160],
ipad: [u8; 160],
opad: [u8; 160],
inner: Operation,
finalized: bool,
in_use: bool,
}

impl Drop for HMACOperation {
fn drop(&mut self) {
zeromem(self.state.as_mut_slice());
zeromem(self.ipad.as_mut_slice());
zeromem(self.opad.as_mut_slice());
zeromem(&mut self.state);
zeromem(&mut self.ipad);
zeromem(&mut self.opad);
}
}

Expand All @@ -44,16 +49,19 @@ impl HMACOperation {
key: HmacKey,
outputlen: usize,
) -> Result<HMACOperation> {
let hash = hmac_mech_to_hash_mech(mech)?;
let hashlen = hash::hash_size(hash);
let blocklen = hash::block_size(hash);
let mut hmac = HMACOperation {
mech: mech,
key: key,
hash: hmac_mech_to_hash_mech(mech)?,
hashlen: 0usize,
blocklen: 0usize,
hash: hash,
hashlen: hashlen,
blocklen: blocklen,
outputlen: outputlen,
state: Vec::new(),
ipad: Vec::new(),
opad: Vec::new(),
state: [0u8; 160],
ipad: IPAD_INIT,
opad: OPAD_INIT,
inner: Operation::Empty,
finalized: false,
in_use: false,
Expand All @@ -66,41 +74,35 @@ impl HMACOperation {
/* The hash mechanism is unimportant here,
* what matters is the psecdef algorithm */
let hashop = hash::internal_hash_op(self.hash)?;
self.hashlen = hash::hash_size(self.hash);
self.blocklen = hash::block_size(self.hash);
self.inner = Operation::Digest(hashop);

/* K0 */
if self.key.raw.len() <= self.blocklen {
self.state.extend_from_slice(self.key.raw.as_slice());
self.state[0..self.key.raw.len()]
.copy_from_slice(self.key.raw.as_slice());
} else {
self.state.resize(self.hashlen, 0);
match &mut self.inner {
Operation::Digest(op) => op.digest(
self.key.raw.as_slice(),
self.state.as_mut_slice(),
&mut self.state[..self.hashlen],
)?,
_ => return Err(CKR_GENERAL_ERROR)?,
}
}
self.state.resize(self.blocklen, 0);
/* K0 ^ ipad */
self.ipad.resize(self.blocklen, 0x36);
self.ipad
.iter_mut()
.zip(self.state.iter())
.for_each(|(i1, i2)| *i1 ^= *i2);
/* K0 ^ opad */
self.opad.resize(self.blocklen, 0x5c);
self.opad
.iter_mut()
.zip(self.state.iter())
.for_each(|(i1, i2)| *i1 ^= *i2);
let ipad = &mut self.ipad[..self.blocklen];
let opad = &mut self.opad[..self.blocklen];
let state = &self.state[..self.blocklen];
for i in 0..self.blocklen {
/* K0 ^ ipad */
ipad[i] ^= state[i];
/* K0 ^ opad */
opad[i] ^= state[i];
}
/* H((K0 ^ ipad) || .. ) */
match &mut self.inner {
Operation::Digest(op) => {
op.reset()?;
op.digest_update(self.ipad.as_slice())?;
op.digest_update(ipad)?;
}
_ => return Err(CKR_GENERAL_ERROR)?,
}
Expand Down Expand Up @@ -143,21 +145,20 @@ impl HMACOperation {
return Err(CKR_GENERAL_ERROR)?;
}

self.state.resize(self.hashlen, 0);
/* state = H((K0 ^ ipad) || text) */
match &mut self.inner {
Operation::Digest(op) => {
op.digest_final(self.state.as_mut_slice())?;
op.digest_final(&mut self.state[..self.hashlen])?;
}
_ => return Err(CKR_GENERAL_ERROR)?,
}
/* state = H((K0 ^ opad) || H((K0 ^ ipad) || text)) */
match &mut self.inner {
Operation::Digest(op) => {
op.reset()?;
op.digest_update(self.opad.as_slice())?;
op.digest_update(self.state.as_slice())?;
op.digest_final(self.state.as_mut_slice())?;
op.digest_update(&self.opad[..self.blocklen])?;
op.digest_update(&self.state[..self.hashlen])?;
op.digest_final(&mut self.state[..self.hashlen])?;
}
_ => return Err(CKR_GENERAL_ERROR)?,
}
Expand All @@ -167,11 +168,9 @@ impl HMACOperation {
}

fn reinit(&mut self) -> Result<()> {
self.hashlen = 0;
self.blocklen = 0;
self.state = Vec::new();
self.ipad = Vec::new();
self.opad = Vec::new();
zeromem(&mut self.state);
self.ipad.copy_from_slice(&IPAD_INIT);
self.opad.copy_from_slice(&OPAD_INIT);
self.inner = Operation::Empty;
self.finalized = false;
self.in_use = false;
Expand Down

0 comments on commit 6dab1a0

Please sign in to comment.