Bladeren bron

feat!: derive CMZ generator A automatically

Derive and memoize the auxiliary generator with RFC 9380 hash-to-group, while retaining explicit initialization as a pre-first-use compatibility override.

BREAKING CHANGE: CMZ credential point types must implement the encoding and SHA-512 hash-to-group bounds captured by CMZGroup.
Michele Orrù 3 weken geleden
bovenliggende
commit
c3a5b0fe18
11 gewijzigde bestanden met toevoegingen van 174 en 73 verwijderingen
  1. 16 0
      CHANGELOG.md
  2. 44 4
      Cargo.lock
  3. 15 1
      Cargo.toml
  4. 17 3
      README.md
  5. 59 34
      src/lib.rs
  6. 0 4
      tests/basic.rs
  7. 0 7
      tests/priv_serialize.rs
  8. 1 4
      tests/rate_limiting.rs
  9. 0 4
      tests/submodule.rs
  10. 22 9
      tests/two_groups.rs
  11. 0 3
      tests/wallet.rs

+ 16 - 0
CHANGELOG.md

@@ -5,6 +5,22 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 
+## [Unreleased]
+
+### Added
+
+- Derive the CMZ generator `A` automatically via a RFC 9380 hash-to-group of the generator.
+  Applications no longer need to call `cmz_group_init` at startup.
+- Add `CMZGroup`, `cmz_generator_a`, and curve features for the automatic
+  derivation. Ristretto support is enabled by default; P-256 and secp256k1 are
+  available through the `p256` and `k256` features.
+
+### Changes
+
+- CMZ credential point types must now implement
+  `group::prime::PrimeGroup + group::GroupEncoding`, and
+  `hash2group::FromHash<hash2group::rfc9380::ExpandMsgXmd<sha2::Sha512>>`.
+
 ## [0.3.0] - 2026-08-04
 ## [0.3.0] - 2026-08-04
 
 
 ### Changes
 ### Changes

+ 44 - 4
Cargo.lock

@@ -159,6 +159,12 @@ version = "3.20.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
 checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
 
 
+[[package]]
+name = "bytemuck"
+version = "1.25.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797"
+
 [[package]]
 [[package]]
 name = "byteorder"
 name = "byteorder"
 version = "1.5.0"
 version = "1.5.0"
@@ -245,6 +251,7 @@ dependencies = [
  "ff",
  "ff",
  "generic_static",
  "generic_static",
  "group",
  "group",
+ "hash2group",
  "hex",
  "hex",
  "lazy_static",
  "lazy_static",
  "p256",
  "p256",
@@ -253,7 +260,8 @@ dependencies = [
  "serde",
  "serde",
  "serde_bytes",
  "serde_bytes",
  "serde_with",
  "serde_with",
- "sha2",
+ "sha2 0.10.9",
+ "sha2 0.11.0",
  "sigma-compiler",
  "sigma-compiler",
  "thiserror",
  "thiserror",
 ]
 ]
@@ -302,6 +310,12 @@ version = "0.9.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
 
 
+[[package]]
+name = "const-oid"
+version = "0.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
+
 [[package]]
 [[package]]
 name = "core-foundation-sys"
 name = "core-foundation-sys"
 version = "0.8.7"
 version = "0.8.7"
@@ -498,7 +512,7 @@ version = "0.7.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
 checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
 dependencies = [
 dependencies = [
- "const-oid",
+ "const-oid 0.9.6",
  "pem-rfc7468",
  "pem-rfc7468",
  "zeroize",
  "zeroize",
 ]
 ]
@@ -519,7 +533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
 dependencies = [
 dependencies = [
  "block-buffer 0.10.4",
  "block-buffer 0.10.4",
- "const-oid",
+ "const-oid 0.9.6",
  "crypto-common 0.1.6",
  "crypto-common 0.1.6",
  "subtle",
  "subtle",
 ]
 ]
@@ -531,6 +545,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
 checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
 dependencies = [
 dependencies = [
  "block-buffer 0.12.1",
  "block-buffer 0.12.1",
+ "const-oid 0.10.2",
  "crypto-common 0.2.2",
  "crypto-common 0.2.2",
  "zeroize",
  "zeroize",
 ]
 ]
@@ -711,6 +726,20 @@ dependencies = [
  "subtle",
  "subtle",
 ]
 ]
 
 
+[[package]]
+name = "hash2group"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d84bb44683b8eecf4a6a2ea1788588995028b2acc0437439f78d330ffaa3474f"
+dependencies = [
+ "bytemuck",
+ "curve25519-dalek",
+ "digest 0.11.3",
+ "elliptic-curve",
+ "k256",
+ "p256",
+]
+
 [[package]]
 [[package]]
 name = "hash32"
 name = "hash32"
 version = "0.2.1"
 version = "0.2.1"
@@ -1007,7 +1036,7 @@ dependencies = [
  "ecdsa",
  "ecdsa",
  "elliptic-curve",
  "elliptic-curve",
  "primeorder",
  "primeorder",
- "sha2",
+ "sha2 0.10.9",
 ]
 ]
 
 
 [[package]]
 [[package]]
@@ -1349,6 +1378,17 @@ dependencies = [
  "digest 0.10.7",
  "digest 0.10.7",
 ]
 ]
 
 
+[[package]]
+name = "sha2"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "digest 0.11.3",
+]
+
 [[package]]
 [[package]]
 name = "shake"
 name = "shake"
 version = "0.1.0"
 version = "0.1.0"

+ 15 - 1
Cargo.toml

@@ -11,6 +11,7 @@ cmz-derive = "=0.3.0"
 ff = "0.13"
 ff = "0.13"
 generic_static = "0.2"
 generic_static = "0.2"
 group = "0.13"
 group = "0.13"
+hash2group = { version = "0.1.1", features = [ "rfc9380" ] }
 hex = { version = "0.4", features = [ "serde" ] }
 hex = { version = "0.4", features = [ "serde" ] }
 lazy_static = "1"
 lazy_static = "1"
 postcard = { version = "1", features = [ "alloc" ] }
 postcard = { version = "1", features = [ "alloc" ] }
@@ -18,6 +19,7 @@ rand = "0.8.5"
 serde = { version = "1", features = [ "derive" ] }
 serde = { version = "1", features = [ "derive" ] }
 serde_bytes = "0.11"
 serde_bytes = "0.11"
 serde_with = "3"
 serde_with = "3"
+sha2 = "0.11"
 sigma-compiler = "0.2.3"
 sigma-compiler = "0.2.3"
 thiserror = "2"
 thiserror = "2"
 
 
@@ -26,7 +28,11 @@ chrono = "0.4"
 curve25519-dalek = { version = "4", features = [ "group", "rand_core", "digest" ] }
 curve25519-dalek = { version = "4", features = [ "group", "rand_core", "digest" ] }
 # Used for testing co-habitating with a second PrimeGroup.
 # Used for testing co-habitating with a second PrimeGroup.
 p256 = { version = "0.13", features = [ "arithmetic" ] }
 p256 = { version = "0.13", features = [ "arithmetic" ] }
-sha2 = "0.10"
+# 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]
 [patch.crates-io]
 cmz-derive = { path = "cmz-derive" }
 cmz-derive = { path = "cmz-derive" }
@@ -40,6 +46,14 @@ sigma-proofs = { git = "https://github.com/sigma-rs/sigma-proofs", branch = "mu/
 spongefish = { git = "https://github.com/arkworks-rs/spongefish", branch = "mu/draft-v3" }
 spongefish = { git = "https://github.com/arkworks-rs/spongefish", branch = "mu/draft-v3" }
 
 
 [features]
 [features]
+# Hash-to-group implementations available to the automatic CMZ generator
+# derivation. Ristretto remains the default group used by the examples and
+# downstream applications; other groups are opt-in to avoid pulling every
+# curve implementation into every build.
+default = [ "curve25519-dalek" ]
+curve25519-dalek = [ "hash2group/curve25519-dalek" ]
+k256 = [ "hash2group/k256" ]
+p256 = [ "hash2group/p256" ]
 # Dump (to stdout or to a string) the value of the instance on both the
 # Dump (to stdout or to a string) the value of the instance on both the
 # prover's and verifier's side.  They should match.
 # prover's and verifier's side.  They should match.
 dump = [ "sigma-compiler/dump" ]
 dump = [ "sigma-compiler/dump" ]

+ 17 - 3
README.md

@@ -17,10 +17,24 @@ to create the credential.)
 Your application can have multiple different kinds of credentials, each
 Your application can have multiple different kinds of credentials, each
 with its own set of attributes.  All of the credentials in your
 with its own set of attributes.  All of the credentials in your
 application should use the _same_ `Scalar` and `Point` types.  You get
 application should use the _same_ `Scalar` and `Point` types.  You get
-these from a mathematical _group_, which must satisfy the trait
-[group::prime::PrimeGroup](https://docs.rs/group/0.13.0/group/prime/trait.PrimeGroup.html).
-A typical such group would be
+these from a mathematical _group_. The `Point` type must implement all of
+the following bounds:
+
+  - `group::prime::PrimeGroup`
+  - `group::GroupEncoding`
+  - `hash2group::FromHash<hash2group::rfc9380::ExpandMsgXmd<sha2::Sha512>>`
+
+`CMZGroup` is a blanket marker trait for exactly this combination of bounds.
+A typical point type satisfying them is
 [curve25519\_dalek::ristretto::RistrettoPoint](https://docs.rs/curve25519-dalek/4.1.3/curve25519_dalek/ristretto/struct.RistrettoPoint.html).
 [curve25519\_dalek::ristretto::RistrettoPoint](https://docs.rs/curve25519-dalek/4.1.3/curve25519_dalek/ristretto/struct.RistrettoPoint.html).
+The default `curve25519-dalek` feature supplies the hash-to-group
+implementation for `RistrettoPoint`; the optional `p256` and `k256` features
+supply it for `p256::ProjectivePoint` and `k256::ProjectivePoint`, respectively.
+
+CMZ requires a second generator `A`, which is derived automatically on first use
+by hashing the canonical encoding of `B` and caching it. Callers may override it
+by calling `cmz_group_init` or `cmz_group_init_with` before the first CMZ
+operation.
 
 
 To declare a credential type, use the `CMZ!` macro at the top level of
 To declare a credential type, use the `CMZ!` macro at the top level of
 your crate or module (outside of any function):
 your crate or module (outside of any function):

+ 59 - 34
src/lib.rs

@@ -11,11 +11,13 @@ use group::prime::PrimeGroup;
 use group::{Group, GroupEncoding};
 use group::{Group, GroupEncoding};
 #[cfg(feature = "wnaf_is_constant_time")]
 #[cfg(feature = "wnaf_is_constant_time")]
 use group::{WnafBase, WnafScalar};
 use group::{WnafBase, WnafScalar};
+use hash2group::{rfc9380::ExpandMsgXmd, FromHash};
 use lazy_static::lazy_static;
 use lazy_static::lazy_static;
 use rand::RngCore;
 use rand::RngCore;
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 use serde::{Deserialize, Deserializer, Serialize, Serializer};
 pub use serde_with::serde_as;
 pub use serde_with::serde_as;
 use serde_with::{DeserializeAs, SerializeAs};
 use serde_with::{DeserializeAs, SerializeAs};
+use sha2::Sha512;
 use sigma_compiler::*;
 use sigma_compiler::*;
 pub use sigma_compiler::{self};
 pub use sigma_compiler::{self};
 use thiserror::Error;
 use thiserror::Error;
@@ -111,6 +113,36 @@ pub struct CMZPubkey<G: PrimeGroup> {
     pub X: Vec<G>,
     pub X: Vec<G>,
 }
 }
 
 
+/// Domain separator for deriving the CMZ generator `A`.
+///
+/// This value is a public protocol parameter, it is the computationally-independent
+/// auxiliary group generator.
+/// Changing it changes `A`, and thus it is a breaking change for existing CMZ keys
+/// and credentials.
+pub const CMZ_GENERATOR_A_DST: &[u8] = b"CMZ_GENERATOR_A_XMD:SHA-512_RO_V1_";
+
+/// A prime-order group supporting encoding and hash into the group.
+///
+/// The blanket implementation covers groups supported by [`hash2group`].
+pub trait CMZGroup: PrimeGroup + GroupEncoding + FromHash<ExpandMsgXmd<Sha512>> {}
+
+impl<G> CMZGroup for G where G: PrimeGroup + GroupEncoding + FromHash<ExpandMsgXmd<Sha512>> {}
+
+/// Deterministically derive the CMZ generator `A` from the standard generator
+/// `B` using hash-to-group.
+pub fn cmz_generator_a<G: CMZGroup>() -> G {
+    let generator_b = G::generator();
+    let encoded_b = generator_b.to_bytes();
+    let generator_a =
+        <G as FromHash<ExpandMsgXmd<Sha512>>>::from_hash(CMZ_GENERATOR_A_DST, encoded_b.as_ref());
+
+    assert!(
+        !bool::from(generator_a.is_identity()) && generator_a != generator_b,
+        "hash-to-group produced an invalid CMZ generator A"
+    );
+    generator_a
+}
+
 // The size of the WNAF windows.  Larger sizes take more memory, but
 // The size of the WNAF windows.  Larger sizes take more memory, but
 // result in faster multiplications.
 // result in faster multiplications.
 #[cfg(feature = "wnaf_is_constant_time")]
 #[cfg(feature = "wnaf_is_constant_time")]
@@ -255,67 +287,59 @@ fn load_bp_with<G: Group>(init: impl FnOnce() -> CMZBasepoints<G>) -> &'static C
         .unwrap()
         .unwrap()
 }
 }
 
 
-/// The already-loaded `CMZBasepoints<G>`; panics if `cmz_group_init` has
-/// not been called for `G`.
-fn load_bp<G: Group>() -> &'static CMZBasepoints<G> {
-    load_bp_with::<G>(|| panic!("basepoints uninitialized"))
+/// Return the basepoints for `G`, deriving and memoizing `A` on first use.
+fn load_bp<G: CMZGroup>() -> &'static CMZBasepoints<G> {
+    load_bp_with::<G>(|| CMZBasepoints::init(cmz_generator_a::<G>()))
 }
 }
 
 
-/// Initialize the required second generator for a `PrimeGroup`.
+/// Override the automatically derived second generator for a `PrimeGroup`.
 ///
 ///
 /// CMZ credentials require two generators, `A` and `B`.  `B` is the
 /// CMZ credentials require two generators, `A` and `B`.  `B` is the
 /// "standard" generator.  A can be any other generator (that is, any
 /// "standard" generator.  A can be any other generator (that is, any
 /// other non-identity point in a prime-order group), but it is required
 /// other non-identity point in a prime-order group), but it is required
 /// that no one know the discrete log between `A` and `B`.  So you can't
 /// that no one know the discrete log between `A` and `B`.  So you can't
 /// generate `A` by multiplying `B` by some scalar, for example.  If your
 /// generate `A` by multiplying `B` by some scalar, for example.  If your
-/// group has a hash_from_bytes function, then you can use that to generate
-/// `A`. For example, if your group is a curve25519 group, you can
+/// group has a hash-to-group function, then you can use that to generate
+/// `A`. For the standard CMZ derivation, use [`cmz_generator_a`]:
 ///
 ///
 /// ```
 /// ```
-/// use curve25519_dalek::constants::RISTRETTO_BASEPOINT_POINT as B;
+/// use cmz::cmz_generator_a;
 /// use curve25519_dalek::ristretto::RistrettoPoint as G;
 /// use curve25519_dalek::ristretto::RistrettoPoint as G;
-/// use sha2::Sha512;
-/// let A = G::hash_from_bytes::<Sha512>(b"CMZ Generator A");
-/// assert_ne!(A, B);
+/// use group::Group;
+/// let A = cmz_generator_a::<G>();
+/// assert_ne!(A, G::generator());
 /// ```
 /// ```
 ///
 ///
 /// Otherwise, you're possibly on your own to generate an appropriate
 /// Otherwise, you're possibly on your own to generate an appropriate
-/// generator `A`.  Everyone who uses a given credential type with a
-/// given group will need to use the same `A`.  You need to call this
-/// before doing any operations with a credential.
+/// generator `A`. Everyone who uses a given credential type with a given
+/// group will need to use the same `A`.
+///
+/// CMZ automatically derives `A` using [`cmz_generator_a`], so normal callers
+/// do not need this function. This override exists for compatibility with
+/// deployments using older or application-specific public parameters. It must
+/// be called before the first CMZ operation for `G`; later calls retain the
+/// already-loaded value.
 pub fn cmz_group_init<G: PrimeGroup>(generator_A: G) {
 pub fn cmz_group_init<G: PrimeGroup>(generator_A: G) {
     load_bp_with(|| CMZBasepoints::<G>::init(generator_A));
     load_bp_with(|| CMZBasepoints::<G>::init(generator_A));
 }
 }
 
 
-/// [`cmz_group_init`], deferring construction of `generator_A` to the call
-/// that actually initializes the group.
+/// [`cmz_group_init`], deferring construction of a custom `generator_A` to the
+/// call that actually initializes the group.
 ///
 ///
-/// The basepoints are memoized, so calling `cmz_group_init` once per request
-/// is harmless -- except that its argument is evaluated first, every time.
-/// Callers typically derive `A` by hashing to the curve, which costs far more
-/// than the initialization it feeds:
+/// This is only needed for compatibility with custom public parameters. The
+/// default generator is derived automatically by [`cmz_generator_a`].
 ///
 ///
-/// ```no_run
-/// # use cmz::{cmz_group_init, cmz_group_init_with};
-/// # use curve25519_dalek::ristretto::RistrettoPoint as G;
-/// # use sha2::Sha512;
-/// // ~4.7us of hash-to-curve on every call, thrown away after the first:
-/// cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
-///
-/// // hashed once, on the call that initializes:
-/// cmz_group_init_with(|| G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
-/// ```
 pub fn cmz_group_init_with<G: PrimeGroup>(generator_A: impl FnOnce() -> G) {
 pub fn cmz_group_init_with<G: PrimeGroup>(generator_A: impl FnOnce() -> G) {
     load_bp_with(|| CMZBasepoints::<G>::init(generator_A()));
     load_bp_with(|| CMZBasepoints::<G>::init(generator_A()));
 }
 }
 
 
 /// Get the loaded CMZBasepoints for the given group
 /// Get the loaded CMZBasepoints for the given group
-pub fn cmz_basepoints<G: PrimeGroup>() -> &'static CMZBasepoints<G> {
+pub fn cmz_basepoints<G: CMZGroup>() -> &'static CMZBasepoints<G> {
     load_bp()
     load_bp()
 }
 }
 
 
 /// Compute a public key from a private key
 /// Compute a public key from a private key
-pub fn cmz_privkey_to_pubkey<G: PrimeGroup>(privkey: &CMZPrivkey<G>) -> CMZPubkey<G> {
+pub fn cmz_privkey_to_pubkey<G: CMZGroup>(privkey: &CMZPrivkey<G>) -> CMZPubkey<G> {
     let bp = load_bp::<G>();
     let bp = load_bp::<G>();
     let X0: Option<G> = if privkey.muCMZ {
     let X0: Option<G> = if privkey.muCMZ {
         Some(bp.mulB(&privkey.x0))
         Some(bp.mulB(&privkey.x0))
@@ -340,7 +364,7 @@ where
     type Scalar: PrimeField;
     type Scalar: PrimeField;
 
 
     /// The type of the coordinates of the MAC for this credential
     /// The type of the coordinates of the MAC for this credential
-    type Point: PrimeGroup;
+    type Point: CMZGroup;
 
 
     /// Produce a vector of strings containing the names of the
     /// Produce a vector of strings containing the names of the
     /// attributes of this credential.  (The MAC is not included.)
     /// attributes of this credential.  (The MAC is not included.)
@@ -490,7 +514,8 @@ where
 ///     use curve25519_dalek::ristretto::RistrettoPoint;
 ///     use curve25519_dalek::ristretto::RistrettoPoint;
 ///     type G = RistrettoPoint;
 ///     type G = RistrettoPoint;
 ///
 ///
-/// The group must implement the trait [`PrimeGroup`](https://docs.rs/group/latest/group/prime/trait.PrimeGroup.html).
+/// The group must implement [`CMZGroup`]. Implementations are available for
+/// supported hash-to-group curves through the corresponding crate features.
 #[macro_export]
 #[macro_export]
 macro_rules! CMZ {
 macro_rules! CMZ {
     ( $name: ident < $G: ident > : $( $id: ident ),+ ) => {
     ( $name: ident < $G: ident > : $( $id: ident ),+ ) => {

+ 0 - 4
tests/basic.rs

@@ -3,7 +3,6 @@ use curve25519_dalek::ristretto::RistrettoPoint;
 use curve25519_dalek::scalar::Scalar;
 use curve25519_dalek::scalar::Scalar;
 use group::Group;
 use group::Group;
 use rand::{CryptoRng, RngCore};
 use rand::{CryptoRng, RngCore};
-use sha2::Sha512;
 
 
 CMZ! { Basic<RistrettoPoint> :
 CMZ! { Basic<RistrettoPoint> :
     attr1,
     attr1,
@@ -24,9 +23,6 @@ A: Basic {
 #[test]
 #[test]
 fn test_basic() {
 fn test_basic() {
     let mut rng = rand::thread_rng();
     let mut rng = rand::thread_rng();
-    cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
-        b"CMZ Generator A",
-    ));
 
 
     let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
     let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
 
 

+ 0 - 7
tests/priv_serialize.rs

@@ -3,7 +3,6 @@ use curve25519_dalek::ristretto::RistrettoPoint;
 use curve25519_dalek::scalar::Scalar;
 use curve25519_dalek::scalar::Scalar;
 use group::Group;
 use group::Group;
 use rand::{CryptoRng, RngCore};
 use rand::{CryptoRng, RngCore};
-use sha2::Sha512;
 
 
 CMZ! { Basic<RistrettoPoint> :
 CMZ! { Basic<RistrettoPoint> :
     attr1,
     attr1,
@@ -22,9 +21,6 @@ A: Basic {
 #[test]
 #[test]
 fn test_default_serialize() {
 fn test_default_serialize() {
     let mut rng = rand::thread_rng();
     let mut rng = rand::thread_rng();
-    cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
-        b"CMZ Generator A",
-    ));
 
 
     let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
     let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
 
 
@@ -50,9 +46,6 @@ fn test_default_serialize() {
 #[test]
 #[test]
 fn test_priv_serialize() {
 fn test_priv_serialize() {
     let mut rng = rand::thread_rng();
     let mut rng = rand::thread_rng();
-    cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
-        b"CMZ Generator A",
-    ));
 
 
     let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
     let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
 
 

+ 1 - 4
tests/rate_limiting.rs

@@ -5,7 +5,7 @@ use cmz::*;
 use curve25519_dalek::ristretto::RistrettoPoint;
 use curve25519_dalek::ristretto::RistrettoPoint;
 use group::{Group, GroupEncoding};
 use group::{Group, GroupEncoding};
 use rand::{CryptoRng, RngCore};
 use rand::{CryptoRng, RngCore};
-use sha2::Sha512;
+use sha2_10::Sha512;
 use std::collections::HashSet;
 use std::collections::HashSet;
 
 
 type G = RistrettoPoint;
 type G = RistrettoPoint;
@@ -125,9 +125,6 @@ impl RateLimitServer {
 #[test]
 #[test]
 fn test_rate_limiting() -> Result<(), CMZError> {
 fn test_rate_limiting() -> Result<(), CMZError> {
     let mut rng = rand::thread_rng();
     let mut rng = rand::thread_rng();
-    cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
-        b"CMZ Generator A",
-    ));
 
 
     let (privkey, pubkey) = Cred::mucmz_gen_keys(&mut rng);
     let (privkey, pubkey) = Cred::mucmz_gen_keys(&mut rng);
 
 

+ 0 - 4
tests/submodule.rs

@@ -5,7 +5,6 @@ use curve25519_dalek::ristretto::RistrettoPoint;
 use curve25519_dalek::scalar::Scalar;
 use curve25519_dalek::scalar::Scalar;
 use group::Group;
 use group::Group;
 use rand::{CryptoRng, RngCore};
 use rand::{CryptoRng, RngCore};
-use sha2::Sha512;
 
 
 pub mod cred {
 pub mod cred {
     use super::*;
     use super::*;
@@ -30,9 +29,6 @@ pub mod submod {
     #[test]
     #[test]
     fn test_submodule() -> Result<(), CMZError> {
     fn test_submodule() -> Result<(), CMZError> {
         let mut rng = rand::thread_rng();
         let mut rng = rand::thread_rng();
-        cmz_group_init(RistrettoPoint::hash_from_bytes::<Sha512>(
-            b"CMZ Generator A",
-        ));
 
 
         let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
         let (privkey, pubkey) = Basic::cmz14_gen_keys(&mut rng);
 
 

+ 22 - 9
tests/two_groups.rs

@@ -1,21 +1,34 @@
 //! Basepoint derivation is keyed by group type, so two groups can coexist.
 //! Basepoint derivation is keyed by group type, so two groups can coexist.
 
 
-use cmz::{cmz_basepoints, cmz_group_init};
+use cmz::{cmz_basepoints, cmz_generator_a, cmz_group_init};
 use curve25519_dalek::ristretto::RistrettoPoint;
 use curve25519_dalek::ristretto::RistrettoPoint;
-use group::Group;
-use sha2::Sha512;
+use group::{Group, GroupEncoding};
+
+const RISTRETTO_A_ENCODING: &[u8] = &[
+    0x8e, 0x65, 0xa3, 0x1d, 0xc2, 0x60, 0x08, 0xdf, 0xbc, 0xd9, 0xf6, 0xef, 0xbd, 0x57, 0xf7, 0xcb,
+    0xb2, 0xb3, 0x98, 0x72, 0xf6, 0xfd, 0x32, 0x73, 0x6a, 0x00, 0x7a, 0x08, 0xc2, 0x1e, 0x47, 0x18,
+];
+const P256_A_ENCODING: &[u8] = &[
+    0x02, 0x73, 0xe5, 0x71, 0x95, 0x24, 0x34, 0xf0, 0xd5, 0xac, 0xaa, 0xe0, 0xfc, 0xe7, 0x35, 0x7c,
+    0x45, 0xb2, 0x73, 0xbd, 0x8e, 0x30, 0x4e, 0xe1, 0xac, 0xd9, 0xf2, 0x43, 0xf3, 0x0b, 0x96, 0x18,
+    0x9e,
+];
 
 
 #[test]
 #[test]
 fn two_groups_coexist() {
 fn two_groups_coexist() {
-    let ristretto_a = RistrettoPoint::hash_from_bytes::<Sha512>(b"CMZ Generator A");
-    let p256_a = p256::ProjectivePoint::generator() * p256::Scalar::from(42u64);
+    let ristretto_a = cmz_generator_a::<RistrettoPoint>();
+    let p256_a = cmz_generator_a::<p256::ProjectivePoint>();
 
 
-    cmz_group_init::<RistrettoPoint>(ristretto_a);
-    cmz_group_init::<p256::ProjectivePoint>(p256_a);
+    assert_eq!(&ristretto_a.to_bytes()[..], RISTRETTO_A_ENCODING);
+    assert_eq!(&p256_a.to_bytes()[..], P256_A_ENCODING);
 
 
-    // Each group must read back its own basepoints, not the other's.
+    // Each group is initialized automatically and reads back its own
+    // basepoints, not the other's.
     assert_eq!(cmz_basepoints::<RistrettoPoint>().A(), ristretto_a);
     assert_eq!(cmz_basepoints::<RistrettoPoint>().A(), ristretto_a);
-    assert_eq!(cmz_basepoints::<RistrettoPoint>().B(), RistrettoPoint::generator());
+    assert_eq!(
+        cmz_basepoints::<RistrettoPoint>().B(),
+        RistrettoPoint::generator()
+    );
     assert_eq!(cmz_basepoints::<p256::ProjectivePoint>().A(), p256_a);
     assert_eq!(cmz_basepoints::<p256::ProjectivePoint>().A(), p256_a);
     assert_eq!(
     assert_eq!(
         cmz_basepoints::<p256::ProjectivePoint>().B(),
         cmz_basepoints::<p256::ProjectivePoint>().B(),

+ 0 - 3
tests/wallet.rs

@@ -6,7 +6,6 @@ use cmz::*;
 use curve25519_dalek::ristretto::RistrettoPoint as G;
 use curve25519_dalek::ristretto::RistrettoPoint as G;
 use group::Group;
 use group::Group;
 use rand::{CryptoRng, RngCore};
 use rand::{CryptoRng, RngCore};
-use sha2::Sha512;
 
 
 CMZ! { Wallet: randid, balance }
 CMZ! { Wallet: randid, balance }
 CMZ! { Item: serialno, price }
 CMZ! { Item: serialno, price }
@@ -107,9 +106,7 @@ macro_rules! protos_def {
                 }
                 }
             }
             }
 
 
-            // Initialization
             let mut rng = rand::thread_rng();
             let mut rng = rand::thread_rng();
-            cmz_group_init(G::hash_from_bytes::<Sha512>(b"CMZ Generator A"));
 
 
             // Issuer: generate private and public keys for each type of
             // Issuer: generate private and public keys for each type of
             // credential.  (The client gets a copy of the public keys.)
             // credential.  (The client gets a copy of the public keys.)