use core::fmt;
use super::hash::{Hash, HashContext, LegacySha1, Sha256, Sha384, Sha512};
use crate::low::{Zeroable, ct, ct_equal, zeroise_value};
use crate::mid::{sha1, sha2};
#[derive(Debug)]
pub struct Algorithm {
id: Id,
output_len: usize,
}
#[derive(Clone, Copy, Debug)]
enum Id {
Sha1,
Sha256,
Sha384,
Sha512,
Sha512_256,
}
impl Algorithm {
pub fn output_len(&self) -> usize {
self.output_len
}
}
pub static SHA1: Algorithm = Algorithm {
id: Id::Sha1,
output_len: sha1::Sha1Context::OUTPUT_SZ,
};
pub static SHA256: Algorithm = Algorithm {
id: Id::Sha256,
output_len: sha2::Sha256Context::OUTPUT_SZ,
};
pub static SHA384: Algorithm = Algorithm {
id: Id::Sha384,
output_len: sha2::Sha384Context::OUTPUT_SZ,
};
pub static SHA512: Algorithm = Algorithm {
id: Id::Sha512,
output_len: sha2::Sha512Context::OUTPUT_SZ,
};
pub static SHA512_256: Algorithm = Algorithm {
id: Id::Sha512_256,
output_len: sha2::Sha512_256Context::OUTPUT_SZ,
};
pub fn digest(algorithm: &'static Algorithm, data: &[u8]) -> Digest {
match algorithm.id {
Id::Sha1 => LegacySha1::hash(data),
Id::Sha256 => Sha256::hash(data),
Id::Sha384 => Sha384::hash(data),
Id::Sha512 => Sha512::hash(data),
Id::Sha512_256 => Digest::new(
&SHA512_256,
&sha2::Sha512_256Context::truncate(&sha2::Sha512_256Context::INITIAL.finish_with(data)),
),
}
}
#[derive(Clone)]
pub struct Context(State);
#[derive(Clone)]
#[repr(u8)]
enum State {
Sha1(<LegacySha1 as Hash>::Context),
Sha256(<Sha256 as Hash>::Context),
Sha384(<Sha384 as Hash>::Context),
Sha512(<Sha512 as Hash>::Context),
Sha512_256(sha2::Sha512_256Context),
}
impl State {
fn new(algorithm: &'static Algorithm) -> Self {
match algorithm.id {
Id::Sha1 => Self::Sha1(LegacySha1::new()),
Id::Sha256 => Self::Sha256(Sha256::new()),
Id::Sha384 => Self::Sha384(Sha384::new()),
Id::Sha512 => Self::Sha512(Sha512::new()),
Id::Sha512_256 => Self::Sha512_256(sha2::Sha512_256Context::new()),
}
}
fn update(&mut self, data: &[u8]) {
match self {
Self::Sha1(ctx) => ctx.update(data),
Self::Sha256(ctx) => ctx.update(data),
Self::Sha384(ctx) => ctx.update(data),
Self::Sha512(ctx) => ctx.update(data),
Self::Sha512_256(ctx) => ctx.update(data),
}
}
fn finish(&self) -> Digest {
match self {
Self::Sha1(ctx) => ctx.finish(),
Self::Sha256(ctx) => ctx.finish(),
Self::Sha384(ctx) => ctx.finish(),
Self::Sha512(ctx) => ctx.finish(),
Self::Sha512_256(ctx) => Digest::new(&SHA512_256, &ctx.finish()),
}
}
}
impl Zeroable for State {}
impl Context {
pub fn new(algorithm: &'static Algorithm) -> Self {
Self(State::new(algorithm))
}
pub fn update(&mut self, data: &[u8]) {
self.0.update(data);
}
pub fn finish(self) -> Digest {
self.0.finish()
}
}
impl Drop for Context {
fn drop(&mut self) {
zeroise_value(&mut self.0);
}
}
#[derive(Clone)]
pub struct Digest {
algorithm: &'static Algorithm,
value: [u8; MAX_OUTPUT_LEN],
}
pub(super) const MAX_OUTPUT_LEN: usize = sha2::Sha512Context::OUTPUT_SZ;
impl Digest {
pub(crate) fn new(algorithm: &'static Algorithm, value: &[u8]) -> Self {
let mut digest = Self::zeroed(algorithm);
digest.as_mut().copy_from_slice(value);
digest
}
pub(crate) fn zeroed(algorithm: &'static Algorithm) -> Self {
Self {
algorithm,
value: [0; MAX_OUTPUT_LEN],
}
}
pub(crate) fn as_mut(&mut self) -> &mut [u8] {
&mut self.value[..self.algorithm.output_len]
}
pub(crate) fn ct_equal(&self, other: &[u8]) -> bool {
ct::into_public(ct_equal(self.as_ref(), other))
}
}
impl AsRef<[u8]> for Digest {
fn as_ref(&self) -> &[u8] {
&self.value[..self.algorithm.output_len]
}
}
impl fmt::Debug for Digest {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Digest")
.field("algorithm", &self.algorithm.id)
.field("value", &self.as_ref())
.finish()
}
}
#[cfg(test)]
#[cfg_attr(coverage_nightly, coverage(off))]
mod tests {
use super::*;
use crate::test::*;
fn check_meta(algorithm: &Algorithm, meta: &str) {
assert_eq!(meta, format!("L = {}", algorithm.output_len()));
}
#[test]
fn cavp() {
#[derive(Debug)]
struct Cavp {
algorithm: &'static Algorithm,
message: Vec<u8>,
message_bytes: usize,
}
impl CavpSink for Cavp {
fn on_meta(&mut self, meta: &str) {
check_meta(self.algorithm, meta);
}
fn on_value(&mut self, name: &str, value: Value<'_>) {
match name {
"Len" => self.message_bytes = (value.int() / 8) as usize,
"Msg" => {
self.message = value.bytes();
self.message.truncate(self.message_bytes);
}
"MD" => {
let digest = digest(self.algorithm, &self.message);
assert_eq!(digest.as_ref(), value.bytes());
}
_ => todo!("{self:?} value {name} = {value:?}"),
}
}
}
for (file, algorithm) in [
("../thirdparty/cavp-sha1/SHA1ShortMsg.rsp", &SHA1),
("../thirdparty/cavp/sha2/SHA256ShortMsg.rsp", &SHA256),
("../thirdparty/cavp/sha2/SHA256LongMsg.rsp", &SHA256),
("../thirdparty/cavp/sha2/SHA384ShortMsg.rsp", &SHA384),
("../thirdparty/cavp/sha2/SHA384LongMsg.rsp", &SHA384),
("../thirdparty/cavp/sha2/SHA512ShortMsg.rsp", &SHA512),
("../thirdparty/cavp/sha2/SHA512LongMsg.rsp", &SHA512),
(
"../thirdparty/cavp/sha2/SHA512_256ShortMsg.rsp",
&SHA512_256,
),
("../thirdparty/cavp/sha2/SHA512_256LongMsg.rsp", &SHA512_256),
] {
process_cavp(
file,
&mut Cavp {
algorithm,
message: Vec::new(),
message_bytes: 0,
},
);
}
}
#[test]
fn cavp_monte_carlo() {
#[derive(Debug)]
struct Cavp {
algorithm: &'static Algorithm,
seed: Vec<u8>,
}
impl CavpSink for Cavp {
fn on_meta(&mut self, meta: &str) {
check_meta(self.algorithm, meta);
}
fn on_value(&mut self, name: &str, value: Value<'_>) {
match name {
"Seed" => self.seed = value.bytes(),
"COUNT" => {}
"MD" => {
self.seed = monte_carlo_1000(self.algorithm, &self.seed);
assert_eq!(self.seed, value.bytes());
}
_ => todo!("{self:?} value {name} = {value:?}"),
}
}
}
fn monte_carlo_1000(algorithm: &'static Algorithm, seed: &[u8]) -> Vec<u8> {
let mut window = [seed.to_vec(), seed.to_vec(), seed.to_vec()];
for _ in 0..1000 {
let mut ctx = Context::new(algorithm);
ctx.update(&window[0]);
ctx.update(&window[1]);
ctx.update(&window[2]);
let r = ctx.finish();
window.rotate_left(1);
window[2].copy_from_slice(r.as_ref());
}
window[2].clone()
}
if std::env::var_os("SLOW_TESTS").is_some() {
for (file, algorithm) in [
("../thirdparty/cavp-sha1/SHA1Monte.rsp", &SHA1),
("../thirdparty/cavp/sha2/SHA256Monte.rsp", &SHA256),
("../thirdparty/cavp/sha2/SHA384Monte.rsp", &SHA384),
("../thirdparty/cavp/sha2/SHA512Monte.rsp", &SHA512),
("../thirdparty/cavp/sha2/SHA512_256Monte.rsp", &SHA512_256),
] {
process_cavp(
file,
&mut Cavp {
algorithm,
seed: Vec::new(),
},
);
}
}
}
}