Skip to content

Commit

Permalink
Nonce simplification (#19)
Browse files Browse the repository at this point in the history
  • Loading branch information
expede committed Mar 27, 2024
1 parent 7f680d3 commit 53c49d2
Show file tree
Hide file tree
Showing 6 changed files with 17 additions and 149 deletions.
156 changes: 12 additions & 144 deletions src/crypto/nonce.rs
Expand Up @@ -4,11 +4,7 @@

use enum_as_inner::EnumAsInner;
use getrandom::getrandom;
use libipld_core::{
ipld::Ipld,
multibase::Base::Base32HexLower,
multihash::{Hasher, Sha2_256},
};
use libipld_core::{ipld::Ipld, multibase::Base::Base32HexLower};
use serde::{Deserialize, Serialize};
use std::fmt;

Expand All @@ -21,9 +17,6 @@ use proptest::prelude::*;
/// Known [`Nonce`] types
#[derive(Clone, Debug, EnumAsInner, Serialize, Deserialize)]
pub enum Nonce {
/// 96-bit, 12-byte nonce
Nonce12([u8; 12]),

/// 128-bit, 16-byte nonce
Nonce16([u8; 16]),

Expand All @@ -34,48 +27,15 @@ pub enum Nonce {
impl PartialEq for Nonce {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Nonce::Nonce12(a), Nonce::Nonce12(b)) => a == b,
(Nonce::Nonce16(a), Nonce::Nonce16(b)) => a == b,
(Nonce::Custom(a), Nonce::Custom(b)) => a == b,
(Nonce::Custom(a), Nonce::Nonce12(b)) => {
if a.len() == 12 {
a.as_slice() == b
} else {
false
}
}
(Nonce::Custom(a), Nonce::Nonce16(b)) => {
if a.len() == 16 {
a.as_slice() == b
} else {
false
}
}
(Nonce::Nonce12(a), Nonce::Custom(b)) => {
if b.len() == 12 {
a == b.as_slice()
} else {
false
}
}
(Nonce::Nonce16(a), Nonce::Custom(b)) => {
if b.len() == 16 {
a == b.as_slice()
} else {
false
}
}
(Nonce::Custom(a), Nonce::Nonce16(b)) => a.as_slice() == b,
(Nonce::Nonce16(a), Nonce::Custom(b)) => a == b.as_slice(),
_ => false,
}
}
}

impl From<[u8; 12]> for Nonce {
fn from(s: [u8; 12]) -> Self {
Nonce::Nonce12(s)
}
}

impl From<[u8; 16]> for Nonce {
fn from(s: [u8; 16]) -> Self {
Nonce::Nonce16(s)
Expand All @@ -85,7 +45,6 @@ impl From<[u8; 16]> for Nonce {
impl From<Nonce> for Vec<u8> {
fn from(nonce: Nonce) -> Self {
match nonce {
Nonce::Nonce12(nonce) => nonce.to_vec(),
Nonce::Nonce16(nonce) => nonce.to_vec(),
Nonce::Custom(nonce) => nonce,
}
Expand All @@ -94,10 +53,6 @@ impl From<Nonce> for Vec<u8> {

impl From<Vec<u8>> for Nonce {
fn from(nonce: Vec<u8>) -> Self {
if let Ok(twelve) = <[u8; 12]>::try_from(nonce.clone()) {
return twelve.into();
}

if let Ok(sixteen) = <[u8; 16]>::try_from(nonce.clone()) {
return sixteen.into();
}
Expand All @@ -107,44 +62,6 @@ impl From<Vec<u8>> for Nonce {
}

impl Nonce {
/// Generate a 96-bit, 12-byte nonce.
/// This is the minimum nonce size typically recommended.
///
/// # Arguments
///
/// * `salt` - A salt. This may be left empty, but is recommended to avoid collision.
///
/// # Example
///
/// ```rust
/// # use ucan::crypto::Nonce;
/// # use ucan::did::Did;
/// #
/// let mut salt = "did:example:123".as_bytes().to_vec();
/// let nonce = Nonce::generate_12(&mut salt);
///
/// assert_eq!(Vec::from(nonce).len(), 12);
/// ```
pub fn generate_12(salt: &mut Vec<u8>) -> Nonce {
salt.append(&mut [0].repeat(12));

let buf = salt.as_mut_slice();
getrandom(buf).expect("irrecoverable getrandom failure");

let mut hasher = Sha2_256::default();
hasher.update(buf);

let bytes = hasher
.finalize()
.chunks(12)
.next()
.expect("SHA2_256 is 32 bytes")
.try_into()
.expect("we set the length to 12 earlier");

Nonce::Nonce12(bytes)
}

/// Generate a 128-bit, 16-byte nonce
///
/// # Arguments
Expand All @@ -158,37 +75,20 @@ impl Nonce {
/// # use ucan::did::Did;
/// #
/// let mut salt = "did:example:123".as_bytes().to_vec();
/// let nonce = Nonce::generate_16(&mut salt);
/// let nonce = Nonce::generate_16();
///
/// assert_eq!(Vec::from(nonce).len(), 16);
/// ```
pub fn generate_16(salt: &mut Vec<u8>) -> Nonce {
salt.append(&mut [0].repeat(16));

let buf = salt.as_mut_slice();
getrandom(buf).expect("irrecoverable getrandom failure");

let mut hasher = Sha2_256::default();
hasher.update(buf);

let bytes = hasher
.finalize()
.chunks(16)
.next()
.expect("SHA2_256 is 32 bytes")
.try_into()
.expect("we set the length to 16 earlier");

Nonce::Nonce16(bytes)
pub fn generate_16() -> Nonce {
let mut buf = [0; 16];
getrandom(&mut buf).expect("irrecoverable getrandom failure");
Nonce::Nonce16(buf)
}
}

impl fmt::Display for Nonce {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Nonce::Nonce12(nonce) => {
write!(f, "{}", Base32HexLower.encode(nonce.as_slice()))
}
Nonce::Nonce16(nonce) => {
write!(f, "{}", Base32HexLower.encode(nonce.as_slice()))
}
Expand All @@ -202,7 +102,6 @@ impl fmt::Display for Nonce {
impl From<Nonce> for Ipld {
fn from(nonce: Nonce) -> Self {
match nonce {
Nonce::Nonce12(nonce) => Ipld::Bytes(nonce.to_vec()),
Nonce::Nonce16(nonce) => Ipld::Bytes(nonce.to_vec()),
Nonce::Custom(nonce) => Ipld::Bytes(nonce),
}
Expand All @@ -215,10 +114,6 @@ impl TryFrom<Ipld> for Nonce {
fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
if let Ipld::Bytes(v) = ipld {
match v.len() {
12 => Ok(Nonce::Nonce12(
v.try_into()
.expect("12 bytes because we checked in the match"),
)),
16 => Ok(Nonce::Nonce16(
v.try_into()
.expect("16 bytes because we checked in the match"),
Expand All @@ -238,7 +133,6 @@ impl Arbitrary for Nonce {

fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
prop_oneof![
any::<[u8; 12]>().prop_map(Nonce::Nonce12),
any::<[u8; 16]>().prop_map(Nonce::Nonce16),
any::<Vec<u8>>().prop_map(Nonce::Custom)
]
Expand Down Expand Up @@ -270,23 +164,13 @@ impl From<Nonce> for JsNonce {
#[cfg(target_arch = "wasm32")]
#[wasm_bindgen]
impl JsNonce {
/// Generate a 96-bit, 12-byte nonce.
/// This is the minimum nonce size typically recommended.
///
/// # Arguments
///
/// * `salt` - A salt. This may be left empty, but is recommended to avoid collision.
pub fn generate_12(mut salt: Vec<u8>) -> JsNonce {
Nonce::generate_12(&mut salt).into()
}

/// Generate a 128-bit, 16-byte nonce
///
/// # Arguments
///
/// * `salt` - A salt. This may be left empty, but is recommended to avoid collision.
pub fn generate_16(mut salt: Vec<u8>) -> JsNonce {
Nonce::generate_16(&mut salt).into()
pub fn generate_16() -> JsNonce {
Nonce::generate_16().into()
}

/// Directly lift a 12-byte `Uint8Array` into a [`JsNonce`]
Expand Down Expand Up @@ -316,26 +200,10 @@ impl JsNonce {
mod test {
use super::*;

// FIXME prop test with lots of inputs
#[test]
fn ipld_roundtrip_12() {
let gen = Nonce::generate_12(&mut vec![]);
let ipld = Ipld::from(gen.clone());

let inner = if let Nonce::Nonce12(nonce) = gen {
Ipld::Bytes(nonce.to_vec())
} else {
panic!("No conversion!")
};

assert_eq!(ipld, inner);
assert_eq!(gen, ipld.try_into().unwrap());
}

// FIXME prop test with lots of inputs
#[test]
fn ipld_roundtrip_16() {
let gen = Nonce::generate_16(&mut vec![]);
let gen = Nonce::generate_16();
let ipld = Ipld::from(gen.clone());

let inner = if let Nonce::Nonce16(nonce) = gen {
Expand All @@ -351,7 +219,7 @@ mod test {
// FIXME prop test with lots of inputs
// #[test]
// fn ser_de() {
// let gen = Nonce::generate_16(&mut vec![]);
// let gen = Nonce::generate_16();
// let ser = serde_json::to_string(&gen).unwrap();
// let de = serde_json::from_str(&ser).unwrap();

Expand Down
2 changes: 1 addition & 1 deletion src/delegation/agent.rs
Expand Up @@ -69,7 +69,7 @@ where
varsig_header: V,
) -> Result<Delegation<DID, V, C>, DelegateError<S::DelegationStoreError>> {
let mut salt = self.did.clone().to_string().into_bytes();
let nonce = Nonce::generate_12(&mut salt);
let nonce = Nonce::generate_16();

if *subject == self.did {
let payload: Payload<DID> = Payload {
Expand Down
2 changes: 1 addition & 1 deletion src/delegation/payload.rs
Expand Up @@ -70,7 +70,7 @@ pub struct Payload<DID: Did> {
///
/// [cryptograpgic nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce
/// [`Cid`]: libipld_core::cid::Cid ;
#[builder(default = "Nonce::generate_16(&mut vec![])")]
#[builder(default = "Nonce::generate_16()")]
pub nonce: Nonce,

/// The latest wall-clock time that the UCAN is valid until,
Expand Down
2 changes: 1 addition & 1 deletion src/invocation/agent.rs
Expand Up @@ -114,7 +114,7 @@ where
ability,
proofs,
metadata,
nonce: Nonce::generate_12(&mut vec![]),
nonce: Nonce::generate_16(),
cause,
expiration,
issued_at,
Expand Down
2 changes: 1 addition & 1 deletion src/invocation/payload.rs
Expand Up @@ -102,7 +102,7 @@ pub struct Payload<A, DID: Did> {
/// A [cryptographic nonce] to ensure that the UCAN's [`Cid`] is unique.
///
/// [cryptographic nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce
#[builder(default = "Nonce::generate_16(&mut vec![])")]
#[builder(default = "Nonce::generate_16()")]
pub nonce: Nonce,

/// An optional [Unix timestamp] (wall-clock time) at which this [`Invocation`]
Expand Down
2 changes: 1 addition & 1 deletion src/receipt/payload.rs
Expand Up @@ -79,7 +79,7 @@ pub struct Payload<T: Responds, DID: Did> {
///
/// [cryptographic nonce]: https://en.wikipedia.org/wiki/Cryptographic_nonce
/// [`Cid`]: libipld_core::cid::Cid
#[builder(default = "Nonce::generate_16(&mut vec![])")]
#[builder(default = "Nonce::generate_16()")]
pub nonce: Nonce,

/// An optional [Unix timestamp] (wall-clock time) at which the
Expand Down

0 comments on commit 53c49d2

Please sign in to comment.