diff --git a/blah-types/benches/crypto_ops.rs b/blah-types/benches/crypto_ops.rs index 43c4783..5dda47d 100644 --- a/blah-types/benches/crypto_ops.rs +++ b/blah-types/benches/crypto_ops.rs @@ -10,6 +10,7 @@ use rand::{Rng, SeedableRng, rngs::SmallRng}; use sha2::{Digest, Sha256}; const SEED: u64 = 0xDEAD_BEEF_BEEF_DEAD; +const FIXED_NONCE: u32 = 0x42; const MOCK_PRIV_KEY1: [u8; 32] = *b"this is the testing private key1"; const MOCK_PRIV_KEY2: [u8; 32] = *b"that is the 2nd testing privkey."; @@ -77,18 +78,14 @@ fn bench_msg_sign_verify(c: &mut Criterion) { let msg = avg_msg(); c.bench_function("msg-sign", |b| { - // FIXME: Provide a deterministic signing method using a given nonce? - let fixed_nonce_rng = &mut SmallRng::seed_from_u64(SEED); b.iter(|| { black_box(msg.clone()) - .sign_msg_with(&id_key, &act_key_priv, timestamp, fixed_nonce_rng) + .sign_msg_with(&id_key, &act_key_priv, timestamp, FIXED_NONCE) .unwrap() }) }); - - let rng = &mut SmallRng::seed_from_u64(SEED); let signed = msg - .sign_msg_with(&id_key, &act_key_priv, timestamp, rng) + .sign_msg_with(&id_key, &act_key_priv, timestamp, FIXED_NONCE) .unwrap(); c.bench_function("msg-verify", |b| { diff --git a/blah-types/src/crypto.rs b/blah-types/src/crypto.rs index 2495dce..eb2508a 100644 --- a/blah-types/src/crypto.rs +++ b/blah-types/src/crypto.rs @@ -87,20 +87,23 @@ pub struct Signee { } pub trait SignExt: Sized { + /// A convenient shortcut method of [`Signed::new`] for method chaining. fn sign_msg_with( self, id_key: &PubKey, act_key: &SigningKey, timestamp: u64, - rng: &mut (impl RngCore + ?Sized), + nonce: u32, ) -> Result, SignatureError>; + /// A convenient shortcut method of [`SignExt::sign_msg`] using the current + /// timestamp and a random nonce from [`rand::rng`]. fn sign_msg( self, id_key: &PubKey, act_key: &SigningKey, ) -> Result, SignatureError> { - self.sign_msg_with(id_key, act_key, get_timestamp(), &mut rand::rng()) + self.sign_msg_with(id_key, act_key, get_timestamp(), rand::rng().next_u32()) } } @@ -110,9 +113,9 @@ impl SignExt for T { id_key: &PubKey, act_key: &SigningKey, timestamp: u64, - rng: &mut (impl RngCore + ?Sized), + nonce: u32, ) -> Result, SignatureError> { - Signed::new(id_key, act_key, timestamp, rng, self) + Signed::new(id_key, act_key, timestamp, nonce, self) } } @@ -138,15 +141,19 @@ impl Signed { /// Sign the payload with the given `key`. /// /// This operation only fail when serialization of `payload` fails. + /// + /// This function is pure and portable, if the serialization of `payload` is + /// pure and portable. That is, it always returns the bit-identical bytes if + /// it returns `Ok` as long as the arguments are bit-identical. pub fn new( id_key: &PubKey, act_key: &SigningKey, timestamp: u64, - rng: &mut (impl RngCore + ?Sized), + nonce: u32, payload: T, ) -> Result { let signee = Signee { - nonce: rng.next_u32(), + nonce, payload, timestamp, user: UserKey { diff --git a/blah-types/src/identity.rs b/blah-types/src/identity.rs index 836a39f..1937191 100644 --- a/blah-types/src/identity.rs +++ b/blah-types/src/identity.rs @@ -274,9 +274,10 @@ mod tests { #[test] fn id_desc_verify() { + // Insecure but deterministic mock values. const TIMESTAMP: u64 = 42; + const NONCE: u32 = 42; - let rng = &mut rand::rngs::mock::StepRng::new(42, 1); let id_url = "https://example.com".parse::().unwrap(); let id_priv = SigningKey::from_bytes(&[42; 32]); let id_key = PubKey::from(id_priv.verifying_key()); @@ -287,7 +288,7 @@ mod tests { preferred_chat_server_urls: Vec::new(), id_urls: vec![id_url], } - .sign_msg_with(&id_key, &id_priv, TIMESTAMP, rng) + .sign_msg_with(&id_key, &id_priv, TIMESTAMP, NONCE) .unwrap(), }; @@ -319,7 +320,7 @@ mod tests { expire_time: TIMESTAMP + 1, comment: String::new(), } - .sign_msg_with(&id_key, &id_priv, TIMESTAMP, rng) + .sign_msg_with(&id_key, &id_priv, TIMESTAMP, NONCE) .unwrap(), ); id_desc.verify(None, TIMESTAMP).unwrap(); @@ -339,7 +340,7 @@ mod tests { comment: String::new(), } // Self-signed. - .sign_msg_with(&id_key, &act_priv, TIMESTAMP, rng) + .sign_msg_with(&id_key, &act_priv, TIMESTAMP, NONCE) .unwrap(), ); assert_err!( @@ -353,7 +354,7 @@ mod tests { comment: String::new(), } // Wrong id_key. - .sign_msg_with(&act_pub, &act_priv, TIMESTAMP, rng) + .sign_msg_with(&act_pub, &act_priv, TIMESTAMP, NONCE) .unwrap(); assert_err!( id_desc.verify(None, TIMESTAMP), @@ -366,7 +367,7 @@ mod tests { expire_time: u64::MAX, comment: String::new(), } - .sign_msg_with(&id_key, &id_priv, TIMESTAMP, rng) + .sign_msg_with(&id_key, &id_priv, TIMESTAMP, NONCE) .unwrap(); assert_err!( id_desc.verify(None, TIMESTAMP), @@ -379,7 +380,7 @@ mod tests { expire_time: TIMESTAMP + 1, comment: String::new(), } - .sign_msg_with(&id_key, &id_priv, TIMESTAMP, rng) + .sign_msg_with(&id_key, &id_priv, TIMESTAMP, NONCE) .unwrap(); id_desc.verify(None, TIMESTAMP).unwrap(); diff --git a/blah-types/src/msg.rs b/blah-types/src/msg.rs index 6ff2db7..f84124a 100644 --- a/blah-types/src/msg.rs +++ b/blah-types/src/msg.rs @@ -500,7 +500,8 @@ mod tests { #[test] fn canonical_msg() { - let mut fake_rng = rand::rngs::mock::StepRng::new(0x42, 1); + const NONCE: u32 = 0x42; + let id_key = SigningKey::from_bytes(&[0x42; 32]); let act_key = SigningKey::from_bytes(&[0x43; 32]); let timestamp = 0xDEAD_BEEF; @@ -508,12 +509,7 @@ mod tests { rich_text: RichText::from("hello"), room: Id(42), } - .sign_msg_with( - &id_key.verifying_key().into(), - &act_key, - timestamp, - &mut fake_rng, - ) + .sign_msg_with(&id_key.verifying_key().into(), &act_key, timestamp, NONCE) .unwrap(); let json = serde_jcs::to_string(&msg).unwrap();