Skip to content

Commit

Permalink
Improve error handling for 'Key::try_from()'.
Browse files Browse the repository at this point in the history
Introduces a custom error type, 'KeyError', instead of using a 'String'
as an error type. Also adds documentation alluding to the existence of
the 'TryFrom' implementation.
  • Loading branch information
SergioBenitez committed Jul 15, 2022
1 parent 0ca7012 commit ba60a06
Showing 1 changed file with 66 additions and 15 deletions.
81 changes: 66 additions & 15 deletions src/secure/key.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use core::convert::{TryFrom, TryInto};
use std::convert::TryFrom;

const SIGNING_KEY_LEN: usize = 32;
const ENCRYPTION_KEY_LEN: usize = 32;
Expand Down Expand Up @@ -28,19 +28,6 @@ impl PartialEq for Key {
}
}

impl TryFrom<&[u8]> for Key {
type Error = String;
fn try_from(key: &[u8]) -> Result<Self, Self::Error> {
if key.len() < 64 {
Err(format!("bad key length: expected >= 64 bytes, found {}", key.len()))
} else {
let mut output = Key::zero();
output.0.copy_from_slice(&key[..COMBINED_KEY_LENGTH]);
Ok(output)
}
}
}

impl Key {
// An empty key structure, to be filled.
const fn zero() -> Self {
Expand All @@ -56,6 +43,9 @@ impl Key {
///
/// Panics if `key` is less than 64 bytes in length.
///
/// For a non-panicking version, use [`Key::try_from()`] or generate a key with
/// [`Key::generate()`] or [`Key::try_generate()`].
///
/// # Example
///
/// ```rust
Expand All @@ -68,8 +58,9 @@ impl Key {
///
/// let key = Key::from(key);
/// ```
#[inline]
pub fn from(key: &[u8]) -> Key {
key.try_into().unwrap()
Key::try_from(key).unwrap()
}

/// Derives new signing/encryption keys from a master key.
Expand Down Expand Up @@ -194,6 +185,66 @@ impl Key {
}
}

/// An error indicating an issue with generating or constructing a key.
#[cfg_attr(all(nightly, doc), doc(cfg(any(feature = "private", feature = "signed"))))]
#[derive(Debug)]
#[non_exhaustive]
pub enum KeyError {
/// Too few bytes (`.0`) were provided to generate a key.
///
/// See [`Key::from()`] for minimum requirements.
TooShort(usize),
}

impl std::error::Error for KeyError { }

impl std::fmt::Display for KeyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
KeyError::TooShort(n) => {
write!(f, "key material is too short: expected >= {} bytes, got {} bytes",
COMBINED_KEY_LENGTH, n)
}
}
}
}

impl TryFrom<&[u8]> for Key {
type Error = KeyError;

/// A fallible version of [`Key::from()`].
///
/// Succeeds when [`Key::from()`] succeds and returns an error where
/// [`Key::from()`] panics, namely, if `key` is too short.
///
/// # Example
///
/// ```rust
/// # use std::convert::TryFrom;
/// use cookie::Key;
///
/// # /*
/// let key = { /* a cryptographically random key >= 64 bytes */ };
/// # */
/// # let key: &Vec<u8> = &(0..64).collect();
/// # let key: &[u8] = &key[..];
/// assert!(Key::try_from(key).is_ok());
///
/// // A key that's far too short to use.
/// let key = &[1, 2, 3, 4][..];
/// assert!(Key::try_from(key).is_err());
/// ```
fn try_from(key: &[u8]) -> Result<Self, Self::Error> {
if key.len() < COMBINED_KEY_LENGTH {
Err(KeyError::TooShort(key.len()))
} else {
let mut output = Key::zero();
output.0.copy_from_slice(&key[..COMBINED_KEY_LENGTH]);
Ok(output)
}
}
}

#[cfg(test)]
mod test {
use super::Key;
Expand Down

0 comments on commit ba60a06

Please sign in to comment.