use std::fmt::Debug;
use crate::error::Result;
use crate::hash;
use crate::hmac::*;
use crate::mechanism::*;
use crate::misc::zeromem;
use crate::pkcs11::*;
use constant_time_eq::constant_time_eq;
const MAX_BSZ: usize = 160;
const IPAD_INIT: [u8; MAX_BSZ] = [0x36; MAX_BSZ];
const OPAD_INIT: [u8; MAX_BSZ] = [0x5c; MAX_BSZ];
fn hmac_mech_to_hash_mech(
mech: CK_MECHANISM_TYPE,
) -> Result<CK_MECHANISM_TYPE> {
Ok(match mech {
CKM_SHA_1_HMAC | CKM_SHA_1_HMAC_GENERAL => CKM_SHA_1,
CKM_SHA224_HMAC | CKM_SHA224_HMAC_GENERAL => CKM_SHA224,
CKM_SHA256_HMAC | CKM_SHA256_HMAC_GENERAL => CKM_SHA256,
CKM_SHA384_HMAC | CKM_SHA384_HMAC_GENERAL => CKM_SHA384,
CKM_SHA512_HMAC | CKM_SHA512_HMAC_GENERAL => CKM_SHA512,
CKM_SHA3_224_HMAC | CKM_SHA3_224_HMAC_GENERAL => CKM_SHA3_224,
CKM_SHA3_256_HMAC | CKM_SHA3_256_HMAC_GENERAL => CKM_SHA3_256,
CKM_SHA3_384_HMAC | CKM_SHA3_384_HMAC_GENERAL => CKM_SHA3_384,
CKM_SHA3_512_HMAC | CKM_SHA3_512_HMAC_GENERAL => CKM_SHA3_512,
CKM_SHA512_224_HMAC | CKM_SHA512_224_HMAC_GENERAL => CKM_SHA512_224,
CKM_SHA512_256_HMAC | CKM_SHA512_256_HMAC_GENERAL => CKM_SHA512_256,
_ => return Err(CKR_MECHANISM_INVALID)?,
})
}
#[derive(Debug)]
pub struct HMACOperation {
mech: CK_MECHANISM_TYPE,
key: Vec<u8>,
hashlen: usize,
blocklen: usize,
outputlen: usize,
state: [u8; MAX_BSZ],
ipad: [u8; MAX_BSZ],
opad: [u8; MAX_BSZ],
inner: Box<dyn Digest>,
finalized: bool,
in_use: bool,
#[allow(dead_code)]
signature: Option<Vec<u8>>,
}
impl Drop for HMACOperation {
fn drop(&mut self) {
zeromem(self.key.as_mut_slice());
zeromem(&mut self.state);
zeromem(&mut self.ipad);
zeromem(&mut self.opad);
}
}
impl HMACOperation {
pub fn new(
mech: CK_MECHANISM_TYPE,
mut key: HmacKey,
outputlen: usize,
signature: Option<&[u8]>,
) -> Result<HMACOperation> {
let hash = hmac_mech_to_hash_mech(mech)?;
let hashlen = hash::hash_size(hash);
let blocklen = hash::block_size(hash);
let op = hash::internal_hash_op(hash)?;
let mut hmac = HMACOperation {
mech: mech,
key: key.take(),
hashlen: hashlen,
blocklen: blocklen,
outputlen: outputlen,
state: [0u8; MAX_BSZ],
ipad: IPAD_INIT,
opad: OPAD_INIT,
inner: op,
finalized: false,
in_use: false,
signature: match signature {
Some(s) => {
if s.len() != outputlen {
return Err(CKR_SIGNATURE_LEN_RANGE)?;
}
Some(s.to_vec())
}
None => None,
},
};
hmac.init()?;
Ok(hmac)
}
fn init(&mut self) -> Result<()> {
if self.key.len() <= self.blocklen {
self.state[0..self.key.len()].copy_from_slice(self.key.as_slice());
} else {
self.inner
.digest(self.key.as_slice(), &mut self.state[..self.hashlen])?;
}
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 {
ipad[i] ^= state[i];
opad[i] ^= state[i];
}
self.inner.reset()?;
self.inner.digest_update(ipad)?;
Ok(())
}
fn begin(&mut self) -> Result<()> {
if self.in_use {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
Ok(())
}
fn update(&mut self, data: &[u8]) -> Result<()> {
if self.finalized {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
self.in_use = true;
let ret = self.inner.digest_update(data);
if ret.is_err() {
self.finalized = true;
}
ret
}
fn finalize(&mut self, output: &mut [u8]) -> Result<()> {
if self.finalized {
return Err(CKR_OPERATION_NOT_INITIALIZED)?;
}
self.in_use = true;
self.finalized = true;
if output.len() != self.outputlen {
return Err(CKR_GENERAL_ERROR)?;
}
self.inner.digest_final(&mut self.state[..self.hashlen])?;
self.inner.reset()?;
self.inner.digest_update(&self.opad[..self.blocklen])?;
self.inner.digest_update(&self.state[..self.hashlen])?;
self.inner.digest_final(&mut self.state[..self.hashlen])?;
output.copy_from_slice(&self.state[..output.len()]);
Ok(())
}
fn reinit(&mut self) -> Result<()> {
zeromem(&mut self.state);
self.ipad.copy_from_slice(&IPAD_INIT);
self.opad.copy_from_slice(&OPAD_INIT);
self.inner.reset()?;
self.finalized = false;
self.in_use = false;
self.init()
}
}
impl MechOperation for HMACOperation {
fn mechanism(&self) -> Result<CK_MECHANISM_TYPE> {
Ok(self.mech)
}
fn finalized(&self) -> bool {
self.finalized
}
fn reset(&mut self) -> Result<()> {
self.reinit()
}
#[cfg(feature = "fips")]
fn fips_approved(&self) -> Option<bool> {
self.fips_approved
}
}
impl Mac for HMACOperation {
fn mac(&mut self, data: &[u8], mac: &mut [u8]) -> Result<()> {
self.begin()?;
self.update(data)?;
self.finalize(mac)
}
fn mac_update(&mut self, data: &[u8]) -> Result<()> {
self.update(data)
}
fn mac_final(&mut self, mac: &mut [u8]) -> Result<()> {
self.finalize(mac)
}
fn mac_len(&self) -> Result<usize> {
Ok(self.outputlen)
}
}
impl Sign for HMACOperation {
fn sign(&mut self, data: &[u8], signature: &mut [u8]) -> Result<()> {
self.begin()?;
self.update(data)?;
self.finalize(signature)
}
fn sign_update(&mut self, data: &[u8]) -> Result<()> {
self.update(data)
}
fn sign_final(&mut self, signature: &mut [u8]) -> Result<()> {
self.finalize(signature)
}
fn signature_len(&self) -> Result<usize> {
Ok(self.outputlen)
}
}
impl Verify for HMACOperation {
fn verify(&mut self, data: &[u8], signature: &[u8]) -> Result<()> {
self.begin()?;
self.update(data)?;
Verify::verify_final(self, signature)
}
fn verify_update(&mut self, data: &[u8]) -> Result<()> {
self.update(data)
}
fn verify_final(&mut self, signature: &[u8]) -> Result<()> {
let mut verify: Vec<u8> = vec![0; self.outputlen];
self.finalize(verify.as_mut_slice())?;
if !constant_time_eq(&verify, signature) {
return Err(CKR_SIGNATURE_INVALID)?;
}
Ok(())
}
fn signature_len(&self) -> Result<usize> {
Ok(self.outputlen)
}
}
impl VerifySignature for HMACOperation {
fn verify(&mut self, data: &[u8]) -> Result<()> {
self.begin()?;
self.update(data)?;
VerifySignature::verify_final(self)
}
fn verify_update(&mut self, data: &[u8]) -> Result<()> {
self.update(data)
}
fn verify_final(&mut self) -> Result<()> {
let mut verify: Vec<u8> = vec![0; self.outputlen];
self.finalize(verify.as_mut_slice())?;
match &self.signature {
Some(sig) => {
if !constant_time_eq(&verify, sig.as_slice()) {
return Err(CKR_SIGNATURE_INVALID)?;
}
Ok(())
}
None => Err(CKR_GENERAL_ERROR)?,
}
}
}