|
|
@@ -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"
|
|
|
+ );
|
|
|
+}
|