mirror of
https://github.com/saymrwulf/xous-core.git
synced 2026-11-11 07:40:10 +00:00
refactor for sha256 software emulation native in the engine512 crate
This commit is contained in:
parent
d68b6639cf
commit
40b671ffac
6 changed files with 952 additions and 570 deletions
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -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};
|
||||
|
|
|
|||
155
services/engine-sha512/src/sha256.rs
Normal file
155
services/engine-sha512/src/sha256.rs
Normal 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)
|
||||
}
|
||||
218
services/engine-sha512/src/sha256/soft.rs
Normal file
218
services/engine-sha512/src/sha256/soft.rs
Normal 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;
|
||||
}
|
||||
565
services/engine-sha512/src/sha512.rs
Executable file
565
services/engine-sha512/src/sha512.rs
Executable 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)
|
||||
}
|
||||
0
services/engine-sha512/src/soft.rs → services/engine-sha512/src/sha512/soft.rs
Normal file → Executable file
0
services/engine-sha512/src/soft.rs → services/engine-sha512/src/sha512/soft.rs
Normal file → Executable file
Loading…
Reference in a new issue