Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nonce simplification #19

Merged
merged 5 commits into from Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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