refactor for sha256 software emulation native in the engine512 crate

This commit is contained in:
bunnie 2021-08-05 22:07:21 +08:00
parent d68b6639cf
commit 40b671ffac
6 changed files with 952 additions and 570 deletions

View file

@ -73,4 +73,11 @@ pub(crate) enum SusResOps {
SuspendResume,
/// exit the thread
Quit,
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FallbackStrategy {
HardwareThenSoftware,
WaitForHardware,
SoftwareOnly,
}

View file

@ -1,575 +1,12 @@
#![cfg_attr(target_os = "none", no_std)]
pub mod api;
use api::*;
use xous::{send_message, Message};
use num_traits::ToPrimitive;
use xous_ipc::Buffer;
use core::sync::atomic::{AtomicU32, Ordering};
/// we have to make the HW_CONN static because the Digest crate assumes you can clone objects
/// and recycle them. However, it's not a problem for every server to have a unique connection
/// to the hasher service, if that's what it comes down to. The burden for tracking connections is on the
/// connector's side, not on the server's side; so when the connecting process that calls this
/// library dies, this static data dies with it.
static HW_CONN: AtomicU32 = AtomicU32::new(0);
/// a unique-enough random ID number to prove we own our connection to the hashing engine hardware
static TOKEN: [AtomicU32; 3] = [AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0)];
use digest::{BlockInput, FixedOutputDirty, Reset, Update};
use digest::generic_array::GenericArray;
use digest::consts::{U28, U32, U48, U64, U128};
use block_buffer::BlockBuffer;
use core::slice::from_ref;
type BlockSize = U128;
mod soft;
mod api;
pub use api::*;
mod consts;
use soft::*;
use consts::*;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum FallbackStrategy {
HardwareThenSoftware,
WaitForHardware,
SoftwareOnly,
}
mod sha256;
mod sha512;
/*
Software emulation vendored from https://github.com/RustCrypto/hashes/tree/master/sha2/src
License is Apache 2.0
*/
/// Structure that keeps state of the software-emulated Sha-512 operation and
/// contains the logic necessary to perform the final calculations.
#[derive(Clone)]
struct Engine512 {
len: u128,
buffer: BlockBuffer<BlockSize>,
state: [u64; 8],
}
impl Engine512 {
fn new(h: &[u64; STATE_LEN]) -> Engine512 {
Engine512 {
len: 0,
buffer: Default::default(),
state: *h,
}
}
fn update(&mut self, input: &[u8]) {
self.len += (input.len() as u128) << 3;
let s = &mut self.state;
self.buffer.input_blocks(input, |b| compress512(s, b));
}
fn finish(&mut self) {
let s = &mut self.state;
self.buffer
.len128_padding_be(self.len, |d| compress512(s, from_ref(d)));
}
fn reset(&mut self, h: &[u64; STATE_LEN]) {
self.len = 0;
self.buffer.reset();
self.state = *h;
}
}
// a macro for common communications libraries for SHA2 hardware interfacing
// you can't reference fields in a trait. looks like a macro is the accepted way
// of not having to repeat this code over and over again.
macro_rules! sha512_comms {
() => {
pub(crate) fn ensure_conn(&self) -> u32 {
if HW_CONN.load(Ordering::Relaxed) == 0 {
let xns = xous_names::XousNames::new().unwrap();
HW_CONN.store(xns.request_connection_blocking(api::SERVER_NAME_SHA512).expect("Can't connect to Sha512 server"), Ordering::Relaxed);
let trng = trng::Trng::new(&xns).expect("Can't connect to TRNG server");
let id1 = trng.get_u64().unwrap();
let id2 = trng.get_u32().unwrap();
TOKEN[0].store((id1 >> 32) as u32, Ordering::Relaxed);
TOKEN[1].store(id1 as u32, Ordering::Relaxed);
TOKEN[2].store(id2, Ordering::Relaxed);
}
HW_CONN.load(Ordering::Relaxed)
}
pub fn is_idle(&self) -> Result<bool, xous::Error> {
let response = send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::IsIdle.to_usize().unwrap(), 0, 0, 0, 0)
).expect("Couldn't make IsIdle query");
if let xous::Result::Scalar1(result) = response {
if result != 0 {
Ok(true)
} else {
Ok(false)
}
} else {
Err(xous::Error::InternalError)
}
}
pub fn acquire_suspend_lock(&self) -> Result<bool, xous::Error> {
let response = send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::AcquireSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
).expect("Couldn't issue AcquireSuspendLock message");
if let xous::Result::Scalar1(result) = response {
if result != 0 {
Ok(true)
} else {
Ok(false)
}
} else {
Err(xous::Error::InternalError)
}
}
pub fn abort_suspend(&self) -> Result<(), xous::Error> {
// we ignore the result and just turn it into () once we get anything back, as abort_suspend "can't fail"
send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::AbortSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
pub(crate) fn try_acquire_hw(&mut self, config: Sha2Config) {
if !self.in_progress && (self.strategy != FallbackStrategy::SoftwareOnly) {
loop {
let conn = self.ensure_conn(); // also ensures the ID
let response = send_message(conn,
Message::new_blocking_scalar(Opcode::AcquireExclusive.to_usize().unwrap(),
TOKEN[0].load(Ordering::Relaxed) as usize,
TOKEN[1].load(Ordering::Relaxed) as usize,
TOKEN[2].load(Ordering::Relaxed) as usize,
config.to_usize().unwrap(),
)
).expect("couldn't send AcquireExclusive message to Sha2 hardware!");
if let xous::Result::Scalar1(result) = response {
if result != 0 {
self.use_soft = false;
self.in_progress = true;
break;
} else {
if self.strategy == FallbackStrategy::HardwareThenSoftware {
self.use_soft = true;
self.in_progress = true;
break;
} else {
// this is hardware-exclusive mode, we block until we can get the hardware
xous::yield_slice();
}
}
} else {
log::error!("AcquireExclusive had an unexpected error: {:?}", response);
panic!("Internal error in AcquireExclusive");
}
}
} else if self.strategy == FallbackStrategy::SoftwareOnly {
self.use_soft = true;
self.in_progress = true;
}
}
pub(crate) fn reset_hw(&mut self) {
send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::Reset.to_usize().unwrap(),
TOKEN[0].load(Ordering::Relaxed) as usize,
TOKEN[1].load(Ordering::Relaxed) as usize,
TOKEN[2].load(Ordering::Relaxed) as usize,
0,
)).expect("couldn't send reset to hardware");
// reset internal flags
self.length = 0;
self.in_progress = false;
self.use_soft = true;
}
};
}
/// The SHA-512 hash algorithm with the SHA-512 initial hash value.
#[derive(Clone)]
pub struct Sha512 {
/// software fallback engine
engine: Engine512,
/// whether or not this current hasher instance will use software or hardware acceleration
use_soft: bool,
/// specifies the strategy for fallback in case multiple hashes are initiated simultaneously
strategy: FallbackStrategy,
/// track if a hash is in progress
in_progress: bool,
/// track the length of the message processed so far
length: u64,
}
impl Sha512 {
// use this function instead of default for more control over configuration of the hardware engine
pub fn new(maybe_strategy: Option<FallbackStrategy>) -> Self {
let strategy = if let Some(strat) = maybe_strategy {
strat
} else {
FallbackStrategy::HardwareThenSoftware
};
Sha512 {
use_soft: false,
strategy,
engine: Engine512::new(&H512),
in_progress: false,
length: 0,
}
}
// make all the boilerplate comms code shared between all sizes of digest
sha512_comms!();
}
impl Default for Sha512 {
fn default() -> Self {
Sha512::new(Some(FallbackStrategy::HardwareThenSoftware))
// xns should Drop here and release the connection allocated by it automatically
}
}
impl Drop for Sha512 {
fn drop(&mut self) {
// normally, we would de-allocate a connection but because the Digest API assumes that
// all instances are fungible we can't do that, as the connection needs to be persistent
// between invocations of the object.
if !self.use_soft {
self.reset_hw();
}
}
}
impl BlockInput for Sha512 {
type BlockSize = BlockSize;
}
impl Update for Sha512 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.try_acquire_hw(Sha2Config::Sha512);
// split the incoming blocks to page size and send to the engine
if self.use_soft {
self.engine.update(input.as_ref());
} else {
for chunk in input.as_ref().chunks(3968) {
// one SHA512 block (128 bytes) short of 4096 to give space for struct overhead in page remap handling
let mut update = Sha2Update {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
buffer: [0; 3968],
len: 0,
};
self.length += (chunk.len() as u64) * 8; // we need to keep track of length in bits
for (&src, dest) in chunk.iter().zip(&mut update.buffer) {
*dest = src;
}
update.len = chunk.len() as u16;
let buf = Buffer::into_buf(update).expect("couldn't map chunk into IPC buffer");
buf.lend(self.ensure_conn(), Opcode::Update.to_u32().unwrap()).expect("hardware rejected our hash chunk!");
}
}
}
}
impl FixedOutputDirty for Sha512 {
type OutputSize = U64;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
if self.use_soft {
self.engine.finish();
let s = self.engine.state;
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
} else {
let result = Sha2Finalize {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
result: Sha2Result::Uninitialized,
length_in_bits: None,
};
let mut buf = Buffer::into_buf(result).expect("couldn't map memory for the return buffer");
buf.lend_mut(self.ensure_conn(), Opcode::Finalize.to_u32().unwrap()).expect("couldn't finalize");
let returned: Sha2Finalize = buf.to_original().expect("couldn't decode return buffer");
match returned.result {
Sha2Result::Sha512Result(s) => {
log::info!("bits hashed: {}", self.length);
if self.length != returned.length_in_bits.expect("hardware did not return a length field!") {
panic!("Sha512 hardware did not hash as many bits as we had expected!")
}
for (dest, &src) in out.chunks_exact_mut(1).zip(s.iter()) {
dest.copy_from_slice(&[src])
}
}
Sha2Result::Sha512Trunc256Result(_) => {
panic!("Sha512 hardware returned the wrong type of buffer!");
}
Sha2Result::SuspendError => {
panic!("Hardware was suspended during Sha512 operation, result is invalid.");
}
Sha2Result::Uninitialized => {
panic!("Hardware didn't copy Sha512 hash result to the return buffer.");
}
Sha2Result::IdMismatch => {
panic!("Hardware is not currently processing our block, finalize call has no meaning.");
}
}
}
}
}
impl Reset for Sha512 {
fn reset(&mut self) {
if self.use_soft {
self.engine.reset(&H512);
} else {
self.reset_hw();
}
}
}
/// The SHA-512 hash algorithm with the SHA-512/256 initial hash value. The
/// result is truncated to 256 bits.
/// TODO: this is a software-only implementation; this needs to be extended to have hardware
/// support as the constants for this mode are supported in-hardware, once we have validated
/// that the core hardware API even works...
#[derive(Clone)]
pub struct Sha512Trunc256 {
engine: Engine512,
/// whether or not this current hasher instance will use software or hardware acceleration
use_soft: bool,
/// specifies the strategy for fallback in case multiple hashes are initiated simultaneously
strategy: FallbackStrategy,
/// track if a hash is in progress
in_progress: bool,
/// track the length of the message processed so far
length: u64,
}
impl Sha512Trunc256 {
// use this function instead of default for more control over configuration of the hardware engine
pub fn new(maybe_strategy: Option<FallbackStrategy>) -> Self {
let strategy = if let Some(strat) = maybe_strategy {
strat
} else {
FallbackStrategy::HardwareThenSoftware
};
Sha512Trunc256 {
use_soft: false,
strategy,
engine: Engine512::new(&H512_TRUNC_256),
in_progress: false,
length: 0,
}
}
// make all the boilerplate comms code shared between all sizes of digest
sha512_comms!();
}
impl Drop for Sha512Trunc256 {
fn drop(&mut self) {
if !self.use_soft {
self.reset_hw();
}
}
}
impl Default for Sha512Trunc256 {
fn default() -> Self {
Sha512Trunc256::new(Some(FallbackStrategy::HardwareThenSoftware))
}
}
impl BlockInput for Sha512Trunc256 {
type BlockSize = BlockSize;
}
impl Update for Sha512Trunc256 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.try_acquire_hw(Sha2Config::Sha512Trunc256);
if self.use_soft {
self.engine.update(input.as_ref());
} else {
for chunk in input.as_ref().chunks(3968) {
// one SHA512 block (128 bytes) short of 4096 to give space for struct overhead in page remap handling
let mut update = Sha2Update {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
buffer: [0; 3968],
len: 0,
};
self.length += (chunk.len() as u64) * 8; // we need to keep track of length in bits
for (&src, dest) in chunk.iter().zip(&mut update.buffer) {
*dest = src;
}
update.len = chunk.len() as u16;
let buf = Buffer::into_buf(update).expect("couldn't map chunk into IPC buffer");
buf.lend(self.ensure_conn(), Opcode::Update.to_u32().unwrap()).expect("hardware rejected our hash chunk!");
}
}
}
}
impl FixedOutputDirty for Sha512Trunc256 {
type OutputSize = U32;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
if self.use_soft {
self.engine.finish();
let s = &self.engine.state[..4];
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
} else {
let result = Sha2Finalize {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
result: Sha2Result::Uninitialized,
length_in_bits: None,
};
let mut buf = Buffer::into_buf(result).expect("couldn't map memory for the return buffer");
buf.lend_mut(self.ensure_conn(), Opcode::Finalize.to_u32().unwrap()).expect("couldn't finalize");
let returned: Sha2Finalize = buf.to_original().expect("couldn't decode return buffer");
match returned.result {
Sha2Result::Sha512Trunc256Result(s) => {
if self.length != returned.length_in_bits.expect("hardware did not return a length field!") {
panic!("Sha512 hardware did not hash as many bits as we had expected!")
}
for (dest, &src) in out.chunks_exact_mut(1).zip(s.iter()) {
dest.copy_from_slice(&[src])
}
}
Sha2Result::Sha512Result(_) => {
panic!("Sha512 hardware returned the wrong type of buffer!");
}
Sha2Result::SuspendError => {
panic!("Hardware was suspended during Sha512 operation, result is invalid.");
}
Sha2Result::Uninitialized => {
panic!("Hardware didn't copy Sha512 hash result to the return buffer.");
}
Sha2Result::IdMismatch => {
panic!("Hardware is not currently processing our block, finalize call has no meaning.");
}
}
}
}
}
impl Reset for Sha512Trunc256 {
fn reset(&mut self) {
if self.use_soft {
self.engine.reset(&H512_TRUNC_256);
} else {
self.reset_hw();
}
}
}
//////////////////////////////////////////////
//////////////// the following modes are software-only emulation, as we currently do not have the constants in hardware to support these.
//////////////////////////////////////////////
/// The SHA-512 hash algorithm with the SHA-384 initial hash value. The result
/// is truncated to 384 bits.
#[derive(Clone)]
pub struct Sha384 {
engine: Engine512,
}
impl Default for Sha384 {
fn default() -> Self {
Sha384 {
engine: Engine512::new(&H384),
}
}
}
impl BlockInput for Sha384 {
type BlockSize = BlockSize;
}
impl Update for Sha384 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
}
}
impl FixedOutputDirty for Sha384 {
type OutputSize = U48;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.engine.finish();
let s = &self.engine.state[..6];
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
}
}
impl Reset for Sha384 {
fn reset(&mut self) {
self.engine.reset(&H384);
}
}
/// The SHA-512 hash algorithm with the SHA-512/224 initial hash value.
/// The result is truncated to 224 bits.
#[derive(Clone)]
pub struct Sha512Trunc224 {
engine: Engine512,
}
impl Default for Sha512Trunc224 {
fn default() -> Self {
Sha512Trunc224 {
engine: Engine512::new(&H512_TRUNC_224),
}
}
}
impl BlockInput for Sha512Trunc224 {
type BlockSize = BlockSize;
}
impl Update for Sha512Trunc224 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
}
}
impl FixedOutputDirty for Sha512Trunc224 {
type OutputSize = U28;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.engine.finish();
let s = &self.engine.state;
for (chunk, v) in out.chunks_exact_mut(8).zip(s[..3].iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
out[24..28].copy_from_slice(&s[3].to_be_bytes()[..4]);
}
}
impl Reset for Sha512Trunc224 {
fn reset(&mut self) {
self.engine.reset(&H512_TRUNC_224);
}
}
opaque_debug::implement!(Sha384);
opaque_debug::implement!(Sha512);
opaque_debug::implement!(Sha512Trunc224);
opaque_debug::implement!(Sha512Trunc256);
digest::impl_write!(Sha384);
digest::impl_write!(Sha512);
digest::impl_write!(Sha512Trunc224);
digest::impl_write!(Sha512Trunc256);
/// Raw SHA-512 compression function.
///
/// This is a low-level "hazmat" API which provides direct access to the core
/// functionality of SHA-512.
#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
pub fn compress512(state: &mut [u64; 8], blocks: &[GenericArray<u8, U128>]) {
// SAFETY: GenericArray<u8, U128> and [u8; 128] have
// exactly the same memory layout
#[allow(unsafe_code)]
let blocks: &[[u8; 128]] = unsafe { &*(blocks as *const _ as *const [[u8; 128]]) };
compress(state, blocks)
}
pub use digest::{self, Digest};
pub use sha256::{Sha224, Sha256};
pub use sha512::{Sha384, Sha512, Sha512Trunc224, Sha512Trunc256};

View file

@ -0,0 +1,155 @@
//! SHA-256
mod soft;
use soft::compress;
use crate::consts::{H224, H256, STATE_LEN};
use block_buffer::BlockBuffer;
use core::slice::from_ref;
use digest::consts::{U28, U32, U64};
use digest::generic_array::GenericArray;
use digest::{BlockInput, FixedOutputDirty, Reset, Update};
type BlockSize = U64;
/// Structure that keeps state of the Sha-256 operation and
/// contains the logic necessary to perform the final calculations.
#[derive(Clone)]
struct Engine256 {
len: u64,
buffer: BlockBuffer<BlockSize>,
state: [u32; 8],
}
impl Engine256 {
fn new(h: &[u32; STATE_LEN]) -> Engine256 {
Engine256 {
len: 0,
buffer: Default::default(),
state: *h,
}
}
fn update(&mut self, input: &[u8]) {
// Assumes that input.len() can be converted to u64 without overflow
self.len += (input.len() as u64) << 3;
let s = &mut self.state;
self.buffer.input_blocks(input, |b| compress256(s, b));
}
fn finish(&mut self) {
let s = &mut self.state;
let l = self.len;
self.buffer
.len64_padding_be(l, |b| compress256(s, from_ref(b)));
}
fn reset(&mut self, h: &[u32; STATE_LEN]) {
self.len = 0;
self.buffer.reset();
self.state = *h;
}
}
/// The SHA-256 hash algorithm with the SHA-256 initial hash value.
#[derive(Clone)]
pub struct Sha256 {
engine: Engine256,
}
impl Default for Sha256 {
fn default() -> Self {
Sha256 {
engine: Engine256::new(&H256),
}
}
}
impl BlockInput for Sha256 {
type BlockSize = BlockSize;
}
impl Update for Sha256 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
}
}
impl FixedOutputDirty for Sha256 {
type OutputSize = U32;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.engine.finish();
let s = self.engine.state;
for (chunk, v) in out.chunks_exact_mut(4).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
}
}
impl Reset for Sha256 {
fn reset(&mut self) {
self.engine.reset(&H256);
}
}
/// The SHA-256 hash algorithm with the SHA-224 initial hash value. The result
/// is truncated to 224 bits.
#[derive(Clone)]
pub struct Sha224 {
engine: Engine256,
}
impl Default for Sha224 {
fn default() -> Self {
Sha224 {
engine: Engine256::new(&H224),
}
}
}
impl BlockInput for Sha224 {
type BlockSize = BlockSize;
}
impl Update for Sha224 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
}
}
impl FixedOutputDirty for Sha224 {
type OutputSize = U28;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.engine.finish();
let s = &self.engine.state[..7];
for (chunk, v) in out.chunks_exact_mut(4).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
}
}
impl Reset for Sha224 {
fn reset(&mut self) {
self.engine.reset(&H224);
}
}
opaque_debug::implement!(Sha224);
opaque_debug::implement!(Sha256);
digest::impl_write!(Sha224);
digest::impl_write!(Sha256);
/// Raw SHA-256 compression function.
///
/// This is a low-level "hazmat" API which provides direct access to the core
/// functionality of SHA-256.
#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
pub fn compress256(state: &mut [u32; 8], blocks: &[GenericArray<u8, U64>]) {
// SAFETY: GenericArray<u8, U64> and [u8; 64] have
// exactly the same memory layout
#[allow(unsafe_code)]
let blocks: &[[u8; 64]] = unsafe { &*(blocks as *const _ as *const [[u8; 64]]) };
compress(state, blocks)
}

View file

@ -0,0 +1,218 @@
#![allow(clippy::many_single_char_names)]
use crate::consts::BLOCK_LEN;
use core::convert::TryInto;
#[inline(always)]
fn shl(v: [u32; 4], o: u32) -> [u32; 4] {
[v[0] >> o, v[1] >> o, v[2] >> o, v[3] >> o]
}
#[inline(always)]
fn shr(v: [u32; 4], o: u32) -> [u32; 4] {
[v[0] << o, v[1] << o, v[2] << o, v[3] << o]
}
#[inline(always)]
fn or(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
[a[0] | b[0], a[1] | b[1], a[2] | b[2], a[3] | b[3]]
}
#[inline(always)]
fn xor(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
[a[0] ^ b[0], a[1] ^ b[1], a[2] ^ b[2], a[3] ^ b[3]]
}
#[inline(always)]
fn add(a: [u32; 4], b: [u32; 4]) -> [u32; 4] {
[
a[0].wrapping_add(b[0]),
a[1].wrapping_add(b[1]),
a[2].wrapping_add(b[2]),
a[3].wrapping_add(b[3]),
]
}
fn sha256load(v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
[v3[3], v2[0], v2[1], v2[2]]
}
fn sha256swap(v0: [u32; 4]) -> [u32; 4] {
[v0[2], v0[3], v0[0], v0[1]]
}
fn sha256msg1(v0: [u32; 4], v1: [u32; 4]) -> [u32; 4] {
// sigma 0 on vectors
#[inline]
fn sigma0x4(x: [u32; 4]) -> [u32; 4] {
let t1 = or(shl(x, 7), shr(x, 25));
let t2 = or(shl(x, 18), shr(x, 14));
let t3 = shl(x, 3);
xor(xor(t1, t2), t3)
}
add(v0, sigma0x4(sha256load(v0, v1)))
}
fn sha256msg2(v4: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
macro_rules! sigma1 {
($a:expr) => {
$a.rotate_right(17) ^ $a.rotate_right(19) ^ ($a >> 10)
};
}
let [x3, x2, x1, x0] = v4;
let [w15, w14, _, _] = v3;
let w16 = x0.wrapping_add(sigma1!(w14));
let w17 = x1.wrapping_add(sigma1!(w15));
let w18 = x2.wrapping_add(sigma1!(w16));
let w19 = x3.wrapping_add(sigma1!(w17));
[w19, w18, w17, w16]
}
fn sha256_digest_round_x2(cdgh: [u32; 4], abef: [u32; 4], wk: [u32; 4]) -> [u32; 4] {
macro_rules! big_sigma0 {
($a:expr) => {
($a.rotate_right(2) ^ $a.rotate_right(13) ^ $a.rotate_right(22))
};
}
macro_rules! big_sigma1 {
($a:expr) => {
($a.rotate_right(6) ^ $a.rotate_right(11) ^ $a.rotate_right(25))
};
}
macro_rules! bool3ary_202 {
($a:expr, $b:expr, $c:expr) => {
$c ^ ($a & ($b ^ $c))
};
} // Choose, MD5F, SHA1C
macro_rules! bool3ary_232 {
($a:expr, $b:expr, $c:expr) => {
($a & $b) ^ ($a & $c) ^ ($b & $c)
};
} // Majority, SHA1M
let [_, _, wk1, wk0] = wk;
let [a0, b0, e0, f0] = abef;
let [c0, d0, g0, h0] = cdgh;
// a round
let x0 = big_sigma1!(e0)
.wrapping_add(bool3ary_202!(e0, f0, g0))
.wrapping_add(wk0)
.wrapping_add(h0);
let y0 = big_sigma0!(a0).wrapping_add(bool3ary_232!(a0, b0, c0));
let (a1, b1, c1, d1, e1, f1, g1, h1) = (
x0.wrapping_add(y0),
a0,
b0,
c0,
x0.wrapping_add(d0),
e0,
f0,
g0,
);
// a round
let x1 = big_sigma1!(e1)
.wrapping_add(bool3ary_202!(e1, f1, g1))
.wrapping_add(wk1)
.wrapping_add(h1);
let y1 = big_sigma0!(a1).wrapping_add(bool3ary_232!(a1, b1, c1));
let (a2, b2, _, _, e2, f2, _, _) = (
x1.wrapping_add(y1),
a1,
b1,
c1,
x1.wrapping_add(d1),
e1,
f1,
g1,
);
[a2, b2, e2, f2]
}
fn schedule(v0: [u32; 4], v1: [u32; 4], v2: [u32; 4], v3: [u32; 4]) -> [u32; 4] {
let t1 = sha256msg1(v0, v1);
let t2 = sha256load(v2, v3);
let t3 = add(t1, t2);
sha256msg2(t3, v3)
}
macro_rules! rounds4 {
($abef:ident, $cdgh:ident, $rest:expr, $i:expr) => {{
let t1 = add($rest, crate::consts::K32X4[$i]);
$cdgh = sha256_digest_round_x2($cdgh, $abef, t1);
let t2 = sha256swap(t1);
$abef = sha256_digest_round_x2($abef, $cdgh, t2);
}};
}
macro_rules! schedule_rounds4 {
(
$abef:ident, $cdgh:ident,
$w0:expr, $w1:expr, $w2:expr, $w3:expr, $w4:expr,
$i: expr
) => {{
$w4 = schedule($w0, $w1, $w2, $w3);
rounds4!($abef, $cdgh, $w4, $i);
}};
}
/// Process a block with the SHA-256 algorithm.
fn sha256_digest_block_u32(state: &mut [u32; 8], block: &[u32; 16]) {
let mut abef = [state[0], state[1], state[4], state[5]];
let mut cdgh = [state[2], state[3], state[6], state[7]];
// Rounds 0..64
let mut w0 = [block[3], block[2], block[1], block[0]];
let mut w1 = [block[7], block[6], block[5], block[4]];
let mut w2 = [block[11], block[10], block[9], block[8]];
let mut w3 = [block[15], block[14], block[13], block[12]];
let mut w4;
rounds4!(abef, cdgh, w0, 0);
rounds4!(abef, cdgh, w1, 1);
rounds4!(abef, cdgh, w2, 2);
rounds4!(abef, cdgh, w3, 3);
schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 4);
schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 5);
schedule_rounds4!(abef, cdgh, w2, w3, w4, w0, w1, 6);
schedule_rounds4!(abef, cdgh, w3, w4, w0, w1, w2, 7);
schedule_rounds4!(abef, cdgh, w4, w0, w1, w2, w3, 8);
schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 9);
schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 10);
schedule_rounds4!(abef, cdgh, w2, w3, w4, w0, w1, 11);
schedule_rounds4!(abef, cdgh, w3, w4, w0, w1, w2, 12);
schedule_rounds4!(abef, cdgh, w4, w0, w1, w2, w3, 13);
schedule_rounds4!(abef, cdgh, w0, w1, w2, w3, w4, 14);
schedule_rounds4!(abef, cdgh, w1, w2, w3, w4, w0, 15);
let [a, b, e, f] = abef;
let [c, d, g, h] = cdgh;
state[0] = state[0].wrapping_add(a);
state[1] = state[1].wrapping_add(b);
state[2] = state[2].wrapping_add(c);
state[3] = state[3].wrapping_add(d);
state[4] = state[4].wrapping_add(e);
state[5] = state[5].wrapping_add(f);
state[6] = state[6].wrapping_add(g);
state[7] = state[7].wrapping_add(h);
}
pub fn compress(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
let mut block_u32 = [0u32; BLOCK_LEN];
// since LLVM can't properly use aliasing yet it will make
// unnecessary state stores without this copy
let mut state_cpy = *state;
for block in blocks {
for (o, chunk) in block_u32.iter_mut().zip(block.chunks_exact(4)) {
*o = u32::from_be_bytes(chunk.try_into().unwrap());
}
sha256_digest_block_u32(&mut state_cpy, &block_u32);
}
*state = state_cpy;
}

View file

@ -0,0 +1,565 @@
//! SHA-512
mod soft;
use soft::compress;
use crate::consts::*;
use crate::api::*;
use xous::{send_message, Message};
use num_traits::ToPrimitive;
use xous_ipc::Buffer;
use core::sync::atomic::{AtomicU32, Ordering};
/// we have to make the HW_CONN static because the Digest crate assumes you can clone objects
/// and recycle them. However, it's not a problem for every server to have a unique connection
/// to the hasher service, if that's what it comes down to. The burden for tracking connections is on the
/// connector's side, not on the server's side; so when the connecting process that calls this
/// library dies, this static data dies with it.
static HW_CONN: AtomicU32 = AtomicU32::new(0);
/// a unique-enough random ID number to prove we own our connection to the hashing engine hardware
static TOKEN: [AtomicU32; 3] = [AtomicU32::new(0), AtomicU32::new(0), AtomicU32::new(0)];
use digest::{BlockInput, FixedOutputDirty, Reset, Update};
use digest::generic_array::GenericArray;
use digest::consts::{U28, U32, U48, U64, U128};
use block_buffer::BlockBuffer;
use core::slice::from_ref;
type BlockSize = U128;
/*
Software emulation vendored from https://github.com/RustCrypto/hashes/tree/master/sha2/src
License is Apache 2.0
*/
/// Structure that keeps state of the software-emulated Sha-512 operation and
/// contains the logic necessary to perform the final calculations.
#[derive(Clone)]
struct Engine512 {
len: u128,
buffer: BlockBuffer<BlockSize>,
state: [u64; 8],
}
impl Engine512 {
fn new(h: &[u64; STATE_LEN]) -> Engine512 {
Engine512 {
len: 0,
buffer: Default::default(),
state: *h,
}
}
fn update(&mut self, input: &[u8]) {
self.len += (input.len() as u128) << 3;
let s = &mut self.state;
self.buffer.input_blocks(input, |b| compress512(s, b));
}
fn finish(&mut self) {
let s = &mut self.state;
self.buffer
.len128_padding_be(self.len, |d| compress512(s, from_ref(d)));
}
fn reset(&mut self, h: &[u64; crate::consts::STATE_LEN]) {
self.len = 0;
self.buffer.reset();
self.state = *h;
}
}
// a macro for common communications libraries for SHA2 hardware interfacing
// you can't reference fields in a trait. looks like a macro is the accepted way
// of not having to repeat this code over and over again.
macro_rules! sha512_comms {
() => {
pub(crate) fn ensure_conn(&self) -> u32 {
if HW_CONN.load(Ordering::Relaxed) == 0 {
let xns = xous_names::XousNames::new().unwrap();
HW_CONN.store(xns.request_connection_blocking(crate::api::SERVER_NAME_SHA512).expect("Can't connect to Sha512 server"), Ordering::Relaxed);
let trng = trng::Trng::new(&xns).expect("Can't connect to TRNG server");
let id1 = trng.get_u64().unwrap();
let id2 = trng.get_u32().unwrap();
TOKEN[0].store((id1 >> 32) as u32, Ordering::Relaxed);
TOKEN[1].store(id1 as u32, Ordering::Relaxed);
TOKEN[2].store(id2, Ordering::Relaxed);
}
HW_CONN.load(Ordering::Relaxed)
}
pub fn is_idle(&self) -> Result<bool, xous::Error> {
let response = send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::IsIdle.to_usize().unwrap(), 0, 0, 0, 0)
).expect("Couldn't make IsIdle query");
if let xous::Result::Scalar1(result) = response {
if result != 0 {
Ok(true)
} else {
Ok(false)
}
} else {
Err(xous::Error::InternalError)
}
}
pub fn acquire_suspend_lock(&self) -> Result<bool, xous::Error> {
let response = send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::AcquireSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
).expect("Couldn't issue AcquireSuspendLock message");
if let xous::Result::Scalar1(result) = response {
if result != 0 {
Ok(true)
} else {
Ok(false)
}
} else {
Err(xous::Error::InternalError)
}
}
pub fn abort_suspend(&self) -> Result<(), xous::Error> {
// we ignore the result and just turn it into () once we get anything back, as abort_suspend "can't fail"
send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::AbortSuspendLock.to_usize().unwrap(), 0, 0, 0, 0)
).map(|_| ())
}
pub(crate) fn try_acquire_hw(&mut self, config: Sha2Config) {
if !self.in_progress && (self.strategy != FallbackStrategy::SoftwareOnly) {
loop {
let conn = self.ensure_conn(); // also ensures the ID
let response = send_message(conn,
Message::new_blocking_scalar(Opcode::AcquireExclusive.to_usize().unwrap(),
TOKEN[0].load(Ordering::Relaxed) as usize,
TOKEN[1].load(Ordering::Relaxed) as usize,
TOKEN[2].load(Ordering::Relaxed) as usize,
config.to_usize().unwrap(),
)
).expect("couldn't send AcquireExclusive message to Sha2 hardware!");
if let xous::Result::Scalar1(result) = response {
if result != 0 {
self.use_soft = false;
self.in_progress = true;
break;
} else {
if self.strategy == FallbackStrategy::HardwareThenSoftware {
self.use_soft = true;
self.in_progress = true;
break;
} else {
// this is hardware-exclusive mode, we block until we can get the hardware
xous::yield_slice();
}
}
} else {
log::error!("AcquireExclusive had an unexpected error: {:?}", response);
panic!("Internal error in AcquireExclusive");
}
}
} else if self.strategy == FallbackStrategy::SoftwareOnly {
self.use_soft = true;
self.in_progress = true;
}
}
pub(crate) fn reset_hw(&mut self) {
send_message(self.ensure_conn(),
Message::new_blocking_scalar(Opcode::Reset.to_usize().unwrap(),
TOKEN[0].load(Ordering::Relaxed) as usize,
TOKEN[1].load(Ordering::Relaxed) as usize,
TOKEN[2].load(Ordering::Relaxed) as usize,
0,
)).expect("couldn't send reset to hardware");
// reset internal flags
self.length = 0;
self.in_progress = false;
self.use_soft = true;
}
};
}
/// The SHA-512 hash algorithm with the SHA-512 initial hash value.
#[derive(Clone)]
pub struct Sha512 {
/// software fallback engine
engine: Engine512,
/// whether or not this current hasher instance will use software or hardware acceleration
use_soft: bool,
/// specifies the strategy for fallback in case multiple hashes are initiated simultaneously
strategy: FallbackStrategy,
/// track if a hash is in progress
in_progress: bool,
/// track the length of the message processed so far
length: u64,
}
impl Sha512 {
// use this function instead of default for more control over configuration of the hardware engine
pub fn new(maybe_strategy: Option<FallbackStrategy>) -> Self {
let strategy = if let Some(strat) = maybe_strategy {
strat
} else {
FallbackStrategy::HardwareThenSoftware
};
Sha512 {
use_soft: false,
strategy,
engine: Engine512::new(&H512),
in_progress: false,
length: 0,
}
}
// make all the boilerplate comms code shared between all sizes of digest
sha512_comms!();
}
impl Default for Sha512 {
fn default() -> Self {
Sha512::new(Some(FallbackStrategy::HardwareThenSoftware))
// xns should Drop here and release the connection allocated by it automatically
}
}
impl Drop for Sha512 {
fn drop(&mut self) {
// normally, we would de-allocate a connection but because the Digest API assumes that
// all instances are fungible we can't do that, as the connection needs to be persistent
// between invocations of the object.
if !self.use_soft {
self.reset_hw();
}
}
}
impl BlockInput for Sha512 {
type BlockSize = BlockSize;
}
impl Update for Sha512 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.try_acquire_hw(Sha2Config::Sha512);
// split the incoming blocks to page size and send to the engine
if self.use_soft {
self.engine.update(input.as_ref());
} else {
for chunk in input.as_ref().chunks(3968) {
// one SHA512 block (128 bytes) short of 4096 to give space for struct overhead in page remap handling
let mut update = Sha2Update {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
buffer: [0; 3968],
len: 0,
};
self.length += (chunk.len() as u64) * 8; // we need to keep track of length in bits
for (&src, dest) in chunk.iter().zip(&mut update.buffer) {
*dest = src;
}
update.len = chunk.len() as u16;
let buf = Buffer::into_buf(update).expect("couldn't map chunk into IPC buffer");
buf.lend(self.ensure_conn(), Opcode::Update.to_u32().unwrap()).expect("hardware rejected our hash chunk!");
}
}
}
}
impl FixedOutputDirty for Sha512 {
type OutputSize = U64;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
if self.use_soft {
self.engine.finish();
let s = self.engine.state;
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
} else {
let result = Sha2Finalize {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
result: Sha2Result::Uninitialized,
length_in_bits: None,
};
let mut buf = Buffer::into_buf(result).expect("couldn't map memory for the return buffer");
buf.lend_mut(self.ensure_conn(), Opcode::Finalize.to_u32().unwrap()).expect("couldn't finalize");
let returned: Sha2Finalize = buf.to_original().expect("couldn't decode return buffer");
match returned.result {
Sha2Result::Sha512Result(s) => {
log::info!("bits hashed: {}", self.length);
if self.length != returned.length_in_bits.expect("hardware did not return a length field!") {
panic!("Sha512 hardware did not hash as many bits as we had expected!")
}
for (dest, &src) in out.chunks_exact_mut(1).zip(s.iter()) {
dest.copy_from_slice(&[src])
}
}
Sha2Result::Sha512Trunc256Result(_) => {
panic!("Sha512 hardware returned the wrong type of buffer!");
}
Sha2Result::SuspendError => {
panic!("Hardware was suspended during Sha512 operation, result is invalid.");
}
Sha2Result::Uninitialized => {
panic!("Hardware didn't copy Sha512 hash result to the return buffer.");
}
Sha2Result::IdMismatch => {
panic!("Hardware is not currently processing our block, finalize call has no meaning.");
}
}
}
}
}
impl Reset for Sha512 {
fn reset(&mut self) {
if self.use_soft {
self.engine.reset(&H512);
} else {
self.reset_hw();
}
}
}
/// The SHA-512 hash algorithm with the SHA-512/256 initial hash value. The
/// result is truncated to 256 bits.
/// TODO: this is a software-only implementation; this needs to be extended to have hardware
/// support as the constants for this mode are supported in-hardware, once we have validated
/// that the core hardware API even works...
#[derive(Clone)]
pub struct Sha512Trunc256 {
engine: Engine512,
/// whether or not this current hasher instance will use software or hardware acceleration
use_soft: bool,
/// specifies the strategy for fallback in case multiple hashes are initiated simultaneously
strategy: FallbackStrategy,
/// track if a hash is in progress
in_progress: bool,
/// track the length of the message processed so far
length: u64,
}
impl Sha512Trunc256 {
// use this function instead of default for more control over configuration of the hardware engine
pub fn new(maybe_strategy: Option<FallbackStrategy>) -> Self {
let strategy = if let Some(strat) = maybe_strategy {
strat
} else {
FallbackStrategy::HardwareThenSoftware
};
Sha512Trunc256 {
use_soft: false,
strategy,
engine: Engine512::new(&H512_TRUNC_256),
in_progress: false,
length: 0,
}
}
// make all the boilerplate comms code shared between all sizes of digest
sha512_comms!();
}
impl Drop for Sha512Trunc256 {
fn drop(&mut self) {
if !self.use_soft {
self.reset_hw();
}
}
}
impl Default for Sha512Trunc256 {
fn default() -> Self {
Sha512Trunc256::new(Some(FallbackStrategy::HardwareThenSoftware))
}
}
impl BlockInput for Sha512Trunc256 {
type BlockSize = BlockSize;
}
impl Update for Sha512Trunc256 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.try_acquire_hw(Sha2Config::Sha512Trunc256);
if self.use_soft {
self.engine.update(input.as_ref());
} else {
for chunk in input.as_ref().chunks(3968) {
// one SHA512 block (128 bytes) short of 4096 to give space for struct overhead in page remap handling
let mut update = Sha2Update {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
buffer: [0; 3968],
len: 0,
};
self.length += (chunk.len() as u64) * 8; // we need to keep track of length in bits
for (&src, dest) in chunk.iter().zip(&mut update.buffer) {
*dest = src;
}
update.len = chunk.len() as u16;
let buf = Buffer::into_buf(update).expect("couldn't map chunk into IPC buffer");
buf.lend(self.ensure_conn(), Opcode::Update.to_u32().unwrap()).expect("hardware rejected our hash chunk!");
}
}
}
}
impl FixedOutputDirty for Sha512Trunc256 {
type OutputSize = U32;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
if self.use_soft {
self.engine.finish();
let s = &self.engine.state[..4];
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
} else {
let result = Sha2Finalize {
id: [TOKEN[0].load(Ordering::Relaxed), TOKEN[1].load(Ordering::Relaxed), TOKEN[2].load(Ordering::Relaxed)],
result: Sha2Result::Uninitialized,
length_in_bits: None,
};
let mut buf = Buffer::into_buf(result).expect("couldn't map memory for the return buffer");
buf.lend_mut(self.ensure_conn(), Opcode::Finalize.to_u32().unwrap()).expect("couldn't finalize");
let returned: Sha2Finalize = buf.to_original().expect("couldn't decode return buffer");
match returned.result {
Sha2Result::Sha512Trunc256Result(s) => {
if self.length != returned.length_in_bits.expect("hardware did not return a length field!") {
panic!("Sha512 hardware did not hash as many bits as we had expected!")
}
for (dest, &src) in out.chunks_exact_mut(1).zip(s.iter()) {
dest.copy_from_slice(&[src])
}
}
Sha2Result::Sha512Result(_) => {
panic!("Sha512 hardware returned the wrong type of buffer!");
}
Sha2Result::SuspendError => {
panic!("Hardware was suspended during Sha512 operation, result is invalid.");
}
Sha2Result::Uninitialized => {
panic!("Hardware didn't copy Sha512 hash result to the return buffer.");
}
Sha2Result::IdMismatch => {
panic!("Hardware is not currently processing our block, finalize call has no meaning.");
}
}
}
}
}
impl Reset for Sha512Trunc256 {
fn reset(&mut self) {
if self.use_soft {
self.engine.reset(&H512_TRUNC_256);
} else {
self.reset_hw();
}
}
}
//////////////////////////////////////////////
//////////////// the following modes are software-only emulation, as we currently do not have the constants in hardware to support these.
//////////////////////////////////////////////
/// The SHA-512 hash algorithm with the SHA-384 initial hash value. The result
/// is truncated to 384 bits.
#[derive(Clone)]
pub struct Sha384 {
engine: Engine512,
}
impl Default for Sha384 {
fn default() -> Self {
Sha384 {
engine: Engine512::new(&H384),
}
}
}
impl BlockInput for Sha384 {
type BlockSize = BlockSize;
}
impl Update for Sha384 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
}
}
impl FixedOutputDirty for Sha384 {
type OutputSize = U48;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.engine.finish();
let s = &self.engine.state[..6];
for (chunk, v) in out.chunks_exact_mut(8).zip(s.iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
}
}
impl Reset for Sha384 {
fn reset(&mut self) {
self.engine.reset(&H384);
}
}
/// The SHA-512 hash algorithm with the SHA-512/224 initial hash value.
/// The result is truncated to 224 bits.
#[derive(Clone)]
pub struct Sha512Trunc224 {
engine: Engine512,
}
impl Default for Sha512Trunc224 {
fn default() -> Self {
Sha512Trunc224 {
engine: Engine512::new(&H512_TRUNC_224),
}
}
}
impl BlockInput for Sha512Trunc224 {
type BlockSize = BlockSize;
}
impl Update for Sha512Trunc224 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
}
}
impl FixedOutputDirty for Sha512Trunc224 {
type OutputSize = U28;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
self.engine.finish();
let s = &self.engine.state;
for (chunk, v) in out.chunks_exact_mut(8).zip(s[..3].iter()) {
chunk.copy_from_slice(&v.to_be_bytes());
}
out[24..28].copy_from_slice(&s[3].to_be_bytes()[..4]);
}
}
impl Reset for Sha512Trunc224 {
fn reset(&mut self) {
self.engine.reset(&H512_TRUNC_224);
}
}
opaque_debug::implement!(Sha384);
opaque_debug::implement!(Sha512);
opaque_debug::implement!(Sha512Trunc224);
opaque_debug::implement!(Sha512Trunc256);
digest::impl_write!(Sha384);
digest::impl_write!(Sha512);
digest::impl_write!(Sha512Trunc224);
digest::impl_write!(Sha512Trunc256);
/// Raw SHA-512 compression function.
///
/// This is a low-level "hazmat" API which provides direct access to the core
/// functionality of SHA-512.
#[cfg_attr(docsrs, doc(cfg(feature = "compress")))]
pub fn compress512(state: &mut [u64; 8], blocks: &[GenericArray<u8, U128>]) {
// SAFETY: GenericArray<u8, U128> and [u8; 128] have
// exactly the same memory layout
#[allow(unsafe_code)]
let blocks: &[[u8; 128]] = unsafe { &*(blocks as *const _ as *const [[u8; 128]]) };
compress(state, blocks)
}