|
@@ -8,8 +8,8 @@ mod sign_stream;
|
|
|
pub use sign_stream::SignStream;
|
|
|
|
|
|
use crate::{
|
|
|
- fmt, io, BigArray, BlockMeta, BlockPath, Deserialize, Epoch, Formatter, Hashable, Principal,
|
|
|
- Principaled, Serialize, Writecap, WritecapBody,
|
|
|
+ btensure, bterr, fmt, io, BigArray, BlockMeta, BlockPath, Deserialize, Epoch, Formatter,
|
|
|
+ Hashable, Principal, Principaled, Result, Serialize, Writecap, WritecapBody,
|
|
|
};
|
|
|
|
|
|
use btserde::{self, from_vec, to_vec, write_to};
|
|
@@ -34,7 +34,6 @@ use std::{
|
|
|
fmt::Display,
|
|
|
io::{Read, Write},
|
|
|
marker::PhantomData,
|
|
|
- num::TryFromIntError,
|
|
|
};
|
|
|
use strum_macros::{Display, EnumDiscriminants, FromRepr};
|
|
|
use zeroize::ZeroizeOnDrop;
|
|
@@ -80,27 +79,37 @@ pub enum Error {
|
|
|
BlockNotEncrypted,
|
|
|
InvalidHashFormat,
|
|
|
InvalidSignature,
|
|
|
- IncorrectSize { expected: usize, actual: usize },
|
|
|
- IndexOutOfBounds { index: usize, limit: usize },
|
|
|
- IndivisibleSize { divisor: usize, actual: usize },
|
|
|
- InvalidOffset { actual: usize, limit: usize },
|
|
|
+ IncorrectSize {
|
|
|
+ expected: usize,
|
|
|
+ actual: usize,
|
|
|
+ },
|
|
|
+ IndexOutOfBounds {
|
|
|
+ index: usize,
|
|
|
+ limit: usize,
|
|
|
+ },
|
|
|
+ IndivisibleSize {
|
|
|
+ divisor: usize,
|
|
|
+ actual: usize,
|
|
|
+ },
|
|
|
+ InvalidOffset {
|
|
|
+ actual: usize,
|
|
|
+ limit: usize,
|
|
|
+ },
|
|
|
HashCmpFailure,
|
|
|
RootHashNotVerified,
|
|
|
SignatureMismatch(Box<SignatureMismatch>),
|
|
|
- WritecapAuthzErr(WritecapAuthzErr),
|
|
|
- Serde(btserde::Error),
|
|
|
- Io(std::io::Error),
|
|
|
- Custom(Box<dyn std::fmt::Debug + Send + Sync>),
|
|
|
+ /// This variant is used to convey errors that originated in an underlying library.
|
|
|
+ Library(Box<dyn ::std::error::Error + Send + Sync + 'static>),
|
|
|
}
|
|
|
|
|
|
impl Error {
|
|
|
- pub(crate) fn custom<E: std::fmt::Debug + Send + Sync + 'static>(err: E) -> Error {
|
|
|
- Error::Custom(Box::new(err))
|
|
|
- }
|
|
|
-
|
|
|
fn signature_mismatch(expected: Principal, actual: Principal) -> Error {
|
|
|
Error::SignatureMismatch(Box::new(SignatureMismatch { expected, actual }))
|
|
|
}
|
|
|
+
|
|
|
+ fn library<E: std::error::Error + Send + Sync + 'static>(err: E) -> Error {
|
|
|
+ Error::Library(Box::new(err))
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
impl Display for Error {
|
|
@@ -141,60 +150,19 @@ impl Display for Error {
|
|
|
"expected a signature from {expected} but found one from {actual}"
|
|
|
)
|
|
|
}
|
|
|
- Error::WritecapAuthzErr(err) => err.fmt(f),
|
|
|
- Error::Serde(err) => err.fmt(f),
|
|
|
- Error::Io(err) => err.fmt(f),
|
|
|
- Error::Custom(cus) => cus.fmt(f),
|
|
|
+ Error::Library(err) => err.fmt(f),
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
impl std::error::Error for Error {}
|
|
|
|
|
|
-impl From<WritecapAuthzErr> for Error {
|
|
|
- fn from(err: WritecapAuthzErr) -> Self {
|
|
|
- Error::WritecapAuthzErr(err)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
impl From<ErrorStack> for Error {
|
|
|
fn from(error: ErrorStack) -> Error {
|
|
|
- Error::custom(error)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl From<btserde::Error> for Error {
|
|
|
- fn from(error: btserde::Error) -> Error {
|
|
|
- Error::Serde(error)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl From<std::io::Error> for Error {
|
|
|
- fn from(error: std::io::Error) -> Error {
|
|
|
- Error::Io(error)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl From<TryFromIntError> for Error {
|
|
|
- fn from(err: TryFromIntError) -> Self {
|
|
|
- Error::custom(err)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl From<Error> for io::Error {
|
|
|
- fn from(err: Error) -> Self {
|
|
|
- io::Error::new(io::ErrorKind::Other, err)
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-impl From<crate::Error> for Error {
|
|
|
- fn from(err: crate::Error) -> Self {
|
|
|
- Error::custom(err)
|
|
|
+ Error::library(error)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-pub(crate) type Result<T> = std::result::Result<T, Error>;
|
|
|
-
|
|
|
#[derive(Debug)]
|
|
|
pub struct SignatureMismatch {
|
|
|
pub actual: Principal,
|
|
@@ -269,10 +237,10 @@ impl<H> Op for OsslHashOp<H> {
|
|
|
|
|
|
fn finish_into(mut self, buf: &mut [u8]) -> Result<usize> {
|
|
|
if buf.len() < self.kind.len() {
|
|
|
- return Err(Error::IncorrectSize {
|
|
|
+ return Err(bterr!(Error::IncorrectSize {
|
|
|
expected: self.kind.len(),
|
|
|
actual: buf.len(),
|
|
|
- });
|
|
|
+ }));
|
|
|
}
|
|
|
let digest = self.hasher.finish()?;
|
|
|
let slice = digest.as_ref();
|
|
@@ -315,7 +283,7 @@ impl<T, Op: HashOp> HashStream<T, Op> {
|
|
|
/// written is returned.
|
|
|
pub fn finish_into(self, buf: &mut [u8]) -> Result<usize> {
|
|
|
if self.update_failed {
|
|
|
- return Err(Error::custom(
|
|
|
+ return Err(bterr!(
|
|
|
"HashStream::finish_into can't produce result due to HashOp update failure",
|
|
|
));
|
|
|
}
|
|
@@ -325,7 +293,7 @@ impl<T, Op: HashOp> HashStream<T, Op> {
|
|
|
/// Finish this hash operation and return the resulting hash.
|
|
|
pub fn finish(self) -> Result<Op::Hash> {
|
|
|
if self.update_failed {
|
|
|
- return Err(Error::custom(
|
|
|
+ return Err(bterr!(
|
|
|
"HashStream::finish can't produce result due to HashOp update failure",
|
|
|
));
|
|
|
}
|
|
@@ -336,7 +304,7 @@ impl<T, Op: HashOp> HashStream<T, Op> {
|
|
|
impl<T: Read, Op: HashOp> Read for HashStream<T, Op> {
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
if self.update_failed {
|
|
|
- return Err(Error::custom(
|
|
|
+ return Err(bterr!(
|
|
|
"HashStream::read can't continue due to previous HashOp update failure",
|
|
|
)
|
|
|
.into());
|
|
@@ -546,12 +514,13 @@ impl HashKind {
|
|
|
}
|
|
|
|
|
|
pub fn digest<'a, I: Iterator<Item = &'a [u8]>>(self, dest: &mut [u8], parts: I) -> Result<()> {
|
|
|
- if dest.len() != self.len() {
|
|
|
- return Err(Error::IncorrectSize {
|
|
|
+ btensure!(
|
|
|
+ dest.len() == self.len(),
|
|
|
+ Error::IncorrectSize {
|
|
|
expected: self.len(),
|
|
|
actual: dest.len(),
|
|
|
- });
|
|
|
- }
|
|
|
+ }
|
|
|
+ );
|
|
|
let mut hasher = Hasher::new(self.into())?;
|
|
|
for part in parts {
|
|
|
hasher.update(part)?;
|
|
@@ -563,7 +532,8 @@ impl HashKind {
|
|
|
}
|
|
|
|
|
|
impl TryFrom<MessageDigest> for HashKind {
|
|
|
- type Error = Error;
|
|
|
+ type Error = crate::Error;
|
|
|
+
|
|
|
fn try_from(value: MessageDigest) -> Result<Self> {
|
|
|
let nid = value.type_();
|
|
|
if Nid::SHA256 == nid {
|
|
@@ -571,10 +541,7 @@ impl TryFrom<MessageDigest> for HashKind {
|
|
|
} else if Nid::SHA512 == nid {
|
|
|
Ok(HashKind::Sha2_512)
|
|
|
} else {
|
|
|
- Err(Error::custom(format!(
|
|
|
- "Unsupported MessageDigest with NID: {:?}",
|
|
|
- nid
|
|
|
- )))
|
|
|
+ Err(bterr!("Unsupported MessageDigest with NID: {:?}", nid))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -634,7 +601,8 @@ impl AsMut<[u8]> for VarHash {
|
|
|
}
|
|
|
|
|
|
impl TryFrom<MessageDigest> for VarHash {
|
|
|
- type Error = Error;
|
|
|
+ type Error = crate::Error;
|
|
|
+
|
|
|
fn try_from(value: MessageDigest) -> Result<Self> {
|
|
|
let kind: HashKind = value.try_into()?;
|
|
|
Ok(kind.into())
|
|
@@ -659,11 +627,12 @@ impl Hash for VarHash {
|
|
|
}
|
|
|
|
|
|
impl TryFrom<&str> for VarHash {
|
|
|
- type Error = Error;
|
|
|
+ type Error = crate::Error;
|
|
|
+
|
|
|
fn try_from(string: &str) -> Result<VarHash> {
|
|
|
let mut split: Vec<&str> = string.split(Self::HASH_SEP).collect();
|
|
|
if split.len() != 2 {
|
|
|
- return Err(Error::InvalidHashFormat);
|
|
|
+ return Err(bterr!(Error::InvalidHashFormat));
|
|
|
};
|
|
|
let second = split.pop().ok_or(Error::InvalidHashFormat)?;
|
|
|
let first = split
|
|
@@ -703,12 +672,13 @@ impl Op for VarHashOp {
|
|
|
}
|
|
|
|
|
|
fn finish_into(mut self, buf: &mut [u8]) -> Result<usize> {
|
|
|
- if buf.len() < self.kind.len() {
|
|
|
- return Err(Error::IncorrectSize {
|
|
|
+ btensure!(
|
|
|
+ buf.len() >= self.kind.len(),
|
|
|
+ bterr!(Error::IncorrectSize {
|
|
|
expected: self.kind.len(),
|
|
|
actual: buf.len(),
|
|
|
- });
|
|
|
- }
|
|
|
+ })
|
|
|
+ );
|
|
|
let digest = self.hasher.finish()?;
|
|
|
let slice = digest.as_ref();
|
|
|
buf.copy_from_slice(slice);
|
|
@@ -805,12 +775,13 @@ impl AeadKeyKind {
|
|
|
|
|
|
fn array_from<const N: usize>(slice: &[u8]) -> Result<[u8; N]> {
|
|
|
let slice_len = slice.len();
|
|
|
- if N != slice_len {
|
|
|
- return Err(Error::IncorrectSize {
|
|
|
+ btensure!(
|
|
|
+ N == slice_len,
|
|
|
+ Error::IncorrectSize {
|
|
|
actual: slice_len,
|
|
|
expected: N,
|
|
|
- });
|
|
|
- }
|
|
|
+ }
|
|
|
+ );
|
|
|
let mut array = [0u8; N];
|
|
|
array.copy_from_slice(slice);
|
|
|
Ok(array)
|
|
@@ -1469,7 +1440,7 @@ impl Verifier for AsymKey<Public, Sign> {
|
|
|
if verifier.verify(signature)? {
|
|
|
Ok(())
|
|
|
} else {
|
|
|
- Err(Error::InvalidSignature)
|
|
|
+ Err(bterr!(Error::InvalidSignature))
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -1854,7 +1825,7 @@ impl<'a> VerifyOp for OsslVerifyOp<'a> {
|
|
|
fn finish(self, sig: &[u8]) -> Result<()> {
|
|
|
match self.verifier.verify(sig) {
|
|
|
Ok(true) => Ok(()),
|
|
|
- Ok(false) => Err(Error::InvalidSignature),
|
|
|
+ Ok(false) => Err(bterr!(Error::InvalidSignature)),
|
|
|
Err(err) => Err(err.into()),
|
|
|
}
|
|
|
}
|
|
@@ -1879,11 +1850,11 @@ impl<T: Read, Op: VerifyOp> VerifyRead<T, Op> {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- pub fn finish(self, sig: &[u8]) -> std::result::Result<T, (T, Error)> {
|
|
|
+ pub fn finish(self, sig: &[u8]) -> std::result::Result<T, (T, crate::Error)> {
|
|
|
if self.update_failed {
|
|
|
return Err((
|
|
|
self.inner,
|
|
|
- Error::custom("VerifyRead::finish: update_failed was true"),
|
|
|
+ bterr!("VerifyRead::finish: update_failed was true"),
|
|
|
));
|
|
|
}
|
|
|
match self.op.finish(sig) {
|
|
@@ -1896,7 +1867,7 @@ impl<T: Read, Op: VerifyOp> VerifyRead<T, Op> {
|
|
|
impl<T: Read, Op: VerifyOp> Read for VerifyRead<T, Op> {
|
|
|
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
|
if self.update_failed {
|
|
|
- return Err(Error::custom("VerifyRead::read update previously failed").into());
|
|
|
+ return Err(bterr!("VerifyRead::read update previously failed").into());
|
|
|
}
|
|
|
let read = self.inner.read(buf)?;
|
|
|
if read > 0 {
|
|
@@ -2035,14 +2006,14 @@ impl BlockMeta {
|
|
|
let writecap = body
|
|
|
.writecap
|
|
|
.as_ref()
|
|
|
- .ok_or(crate::Error::MissingWritecap)?;
|
|
|
+ .ok_or(crate::BlockError::MissingWritecap)?;
|
|
|
writecap.assert_valid_for(path)?;
|
|
|
let signed_by = body.signing_key.principal();
|
|
|
if writecap.body.issued_to != signed_by {
|
|
|
- return Err(Error::signature_mismatch(
|
|
|
+ return Err(bterr!(Error::signature_mismatch(
|
|
|
writecap.body.issued_to.clone(),
|
|
|
signed_by,
|
|
|
- ));
|
|
|
+ )));
|
|
|
}
|
|
|
body.signing_key.ser_verify(&body, self.sig.as_slice())
|
|
|
}
|
|
@@ -2077,10 +2048,10 @@ impl Writecap {
|
|
|
let now = Epoch::now();
|
|
|
for _ in 0..CHAIN_LEN_LIMIT {
|
|
|
if !writecap.body.path.contains(path) {
|
|
|
- return Err(WritecapAuthzErr::UnauthorizedPath.into());
|
|
|
+ return Err(bterr!(WritecapAuthzErr::UnauthorizedPath));
|
|
|
}
|
|
|
if writecap.body.expires <= now {
|
|
|
- return Err(WritecapAuthzErr::Expired.into());
|
|
|
+ return Err(bterr!(WritecapAuthzErr::Expired));
|
|
|
}
|
|
|
if let Some(prev) = &prev {
|
|
|
if prev
|
|
@@ -2089,12 +2060,12 @@ impl Writecap {
|
|
|
.principal_of_kind(writecap.body.issued_to.kind())
|
|
|
!= writecap.body.issued_to
|
|
|
{
|
|
|
- return Err(WritecapAuthzErr::NotChained.into());
|
|
|
+ return Err(bterr!(WritecapAuthzErr::NotChained));
|
|
|
}
|
|
|
}
|
|
|
sig_input_buf.clear();
|
|
|
write_to(&writecap.body, &mut sig_input_buf)
|
|
|
- .map_err(|e| WritecapAuthzErr::Serde(e.to_string()))?;
|
|
|
+ .map_err(|e| bterr!(WritecapAuthzErr::Serde(e.to_string())))?;
|
|
|
writecap.body.signing_key.verify(
|
|
|
std::iter::once(sig_input_buf.as_slice()),
|
|
|
writecap.signature.as_slice(),
|
|
@@ -2115,12 +2086,12 @@ impl Writecap {
|
|
|
{
|
|
|
return Ok(());
|
|
|
} else {
|
|
|
- return Err(WritecapAuthzErr::RootDoesNotOwnPath.into());
|
|
|
+ return Err(bterr!(WritecapAuthzErr::RootDoesNotOwnPath));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- Err(WritecapAuthzErr::ChainTooLong(CHAIN_LEN_LIMIT).into())
|
|
|
+ Err(bterr!(WritecapAuthzErr::ChainTooLong(CHAIN_LEN_LIMIT)))
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -2186,23 +2157,28 @@ mod tests {
|
|
|
let mut writecap = make_writecap(vec!["apps", "verse"]);
|
|
|
writecap.signature = Signature::empty(Sign::RSA_PSS_3072_SHA_256);
|
|
|
let result = writecap.assert_valid_for(&writecap.body.path);
|
|
|
- if let Err(Error::InvalidSignature) = result {
|
|
|
- Ok(())
|
|
|
- } else {
|
|
|
- Err(Error::custom(format!("unexpected result {:?}", result)))
|
|
|
+ if let Err(ref err) = result {
|
|
|
+ if let Some(err) = err.downcast_ref::<Error>() {
|
|
|
+ if let Error::InvalidSignature = err {
|
|
|
+ return Ok(());
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
+ Err(bterr!("unexpected result {:?}", result))
|
|
|
}
|
|
|
|
|
|
fn assert_authz_err<T: std::fmt::Debug>(
|
|
|
expected: WritecapAuthzErr,
|
|
|
result: Result<T>,
|
|
|
) -> Result<()> {
|
|
|
- if let Err(Error::WritecapAuthzErr(actual)) = &result {
|
|
|
- if *actual == expected {
|
|
|
- return Ok(());
|
|
|
+ if let Some(err) = result.as_ref().err() {
|
|
|
+ if let Some(actual) = err.downcast_ref::<WritecapAuthzErr>() {
|
|
|
+ if *actual == expected {
|
|
|
+ return Ok(());
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
- Err(Error::custom(format!("unexpected result: {:?}", result)))
|
|
|
+ Err(bterr!("unexpected result: {:?}", result))
|
|
|
}
|
|
|
|
|
|
#[test]
|