Browse Source

test: derive rate-limit epoch bases with RFC 9380

Michele Orrù 2 weeks ago
parent
commit
4861abd581
3 changed files with 19 additions and 9 deletions
  1. 0 2
      Cargo.lock
  2. 1 4
      Cargo.toml
  3. 18 3
      tests/rate_limiting.rs

+ 0 - 2
Cargo.lock

@@ -260,7 +260,6 @@ dependencies = [
  "serde",
  "serde_bytes",
  "serde_with",
- "sha2 0.10.9",
  "sha2 0.11.0",
  "sigma-compiler",
  "thiserror",
@@ -386,7 +385,6 @@ dependencies = [
  "cfg-if",
  "cpufeatures 0.2.17",
  "curve25519-dalek-derive",
- "digest 0.10.7",
  "fiat-crypto",
  "group",
  "rand_core",

+ 1 - 4
Cargo.toml

@@ -25,14 +25,11 @@ thiserror = "2"
 
 [dev-dependencies]
 chrono = "0.4"
-curve25519-dalek = { version = "4", features = [ "group", "rand_core", "digest" ] }
+curve25519-dalek = { version = "4", features = [ "group", "rand_core" ] }
 # Used for testing co-habitating with a second PrimeGroup.
 p256 = { version = "0.13", features = [ "arithmetic" ] }
 # Enable both test group implementations regardless of the cmz feature set.
 hash2group = { version = "0.1.1", features = [ "curve25519-dalek", "p256" ] }
-# curve25519-dalek 4's legacy hash_from_bytes API used by the rate-limiting
-# fixture is tied to digest/sha2 0.10.
-sha2_10 = { package = "sha2", version = "0.10" }
 
 [patch.crates-io]
 cmz-derive = { path = "cmz-derive" }

+ 18 - 3
tests/rate_limiting.rs

@@ -4,12 +4,19 @@ use chrono::Utc;
 use cmz::*;
 use curve25519_dalek::ristretto::RistrettoPoint;
 use group::{Group, GroupEncoding};
+use hash2group::{rfc9380::ExpandMsgXmd, FromHash};
 use rand::{CryptoRng, RngCore};
-use sha2_10::Sha512;
+use sha2::Sha512;
 use std::collections::HashSet;
 
 type G = RistrettoPoint;
 
+const EPOCH_BASE_DST: &[u8] = b"CMZ_RATE_LIMIT_EPOCH_XMD:SHA-512_RO_V1_";
+
+fn epoch_base(epoch: &[u8]) -> G {
+    <G as FromHash<ExpandMsgXmd<Sha512>>>::from_hash(EPOCH_BASE_DST, epoch)
+}
+
 CMZ! { Cred: key }
 CMZ! { PresNum: pres_num }
 
@@ -49,7 +56,7 @@ impl RateLimitClient {
         let mut P = PresNum::using_pubkey(&self.presnum_pubkey);
         P.pres_num = Some(pres_num.into());
         P.fake_MAC(rng);
-        let Epoch_base = RistrettoPoint::hash_from_bytes::<Sha512>(epoch);
+        let Epoch_base = epoch_base(epoch);
         let VRF_output = (self.cred.key.unwrap() + P.pres_num.unwrap()).invert() * Epoch_base;
         let params = pres_cred::Params {
             max_pres: 5u32.into(),
@@ -87,7 +94,7 @@ impl RateLimitServer {
         epoch: &[u8],
         msg: &[u8],
     ) -> Result<(), CMZError> {
-        let Epoch_base = RistrettoPoint::hash_from_bytes::<Sha512>(epoch);
+        let Epoch_base = epoch_base(epoch);
         // Separate the message into the VRF output and the request
         let VRF_output = G::from_bytes(&msg[..32].try_into().unwrap()).unwrap();
 
@@ -188,3 +195,11 @@ fn test_rate_limiting() -> Result<(), CMZError> {
 
     Ok(())
 }
+
+#[test]
+fn epoch_base_derivation_is_stable() {
+    assert_eq!(
+        hex::encode(epoch_base(b"Epoch 2026-08-16").to_bytes()),
+        "cabdff610ae6898b12cc86be131d101913b807180f8ffe741319a065d3c2d576"
+    );
+}