add hardware support for sha512/256

This commit is contained in:
bunnie 2021-05-21 04:35:01 +08:00
parent 21a5ddb40f
commit 5526bc4100
3 changed files with 212 additions and 102 deletions

View file

@ -76,15 +76,117 @@ impl Engine512 {
}
}
// 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");
}
}
}
}
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,
/// software fallback engine
engine: Engine512,
/// track if a hash is in progress
in_progress: bool,
/// track the length of the message processed so far
@ -106,53 +208,8 @@ impl Sha512 {
length: 0,
}
}
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(|_| ())
}
// make all the boilerplate comms code shared between all sizes of digest
sha512_comms!();
}
impl Default for Sha512 {
@ -167,6 +224,9 @@ impl Drop for Sha512 {
// 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();
}
}
}
@ -176,38 +236,7 @@ impl BlockInput for Sha512 {
impl Update for Sha512 {
fn update(&mut self, input: impl AsRef<[u8]>) {
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,
Sha2Config::Sha512.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");
}
}
}
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());
@ -282,17 +311,7 @@ impl Reset for Sha512 {
if self.use_soft {
self.engine.reset(&H512);
} else {
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;
self.reset_hw();
}
}
}
@ -307,13 +326,46 @@ impl Reset for Sha512 {
#[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: true,
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 {
engine: Engine512::new(&H512_TRUNC_256),
}
Sha512Trunc256::new(Some(FallbackStrategy::HardwareThenSoftware))
}
}
@ -323,7 +375,26 @@ impl BlockInput for Sha512Trunc256 {
impl Update for Sha512Trunc256 {
fn update(&mut self, input: impl AsRef<[u8]>) {
self.engine.update(input.as_ref());
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!");
}
}
}
}
@ -331,17 +402,55 @@ impl FixedOutputDirty for Sha512Trunc256 {
type OutputSize = U32;
fn finalize_into_dirty(&mut self, out: &mut digest::Output<Self>) {
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());
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) {
self.engine.reset(&H512_TRUNC_256);
if self.use_soft {
self.engine.reset(&H512_TRUNC_256);
} else {
self.reset_hw();
}
}
}

View file

@ -257,6 +257,7 @@ fn xmain() -> ! {
Some(Opcode::AcquireExclusive) => msg_blocking_scalar_unpack!(msg, id0, id1, id2, flags, {
if client_id.is_none() && !SUSPEND_PENDING.load(Ordering::Relaxed) {
client_id = Some([id0 as u32, id1 as u32, id2 as u32]);
//log::trace!("giving {:x?} an exclusive lock", client_id);
mode = Some(FromPrimitive::from_usize(flags).unwrap());
SUSPEND_FAILURE.store(false, Ordering::Relaxed);
HASH_IN_PROGRESS.store(true, Ordering::Relaxed);

View file

@ -179,7 +179,7 @@ impl<'a> ShellCmdApi<'a> for Sha {
];
let mut pass: bool = true;
let mut hasher = engine_sha512::Sha512Trunc256::new();
let mut hasher = engine_sha512::Sha512Trunc256::new(Some(FallbackStrategy::WaitForHardware));
hasher.update(K_DATA);
let digest = hasher.finalize();