From b5f97439754102bfe35876553c34d79b682cd5a0 Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 18 Jun 2020 00:25:50 +0000 Subject: [PATCH 1/2] Add documentation and simplify API. --- Cargo.toml | 2 +- README.md | 55 ++++++++++++++++++++++++++++- src/amacs.rs | 59 +++++++++++++++++++++++++------ src/credential.rs | 25 +++++++++++++ src/errors.rs | 4 ++- src/issuer.rs | 61 +++++++++++++++++++++++++++++--- src/lib.rs | 15 ++++++-- src/macros.rs | 1 - src/nizk.rs | 90 +++++++++++++++++++++++++++-------------------- src/symmetric.rs | 34 ++++++++++++++++-- 10 files changed, 284 insertions(+), 62 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 64e3230..651e780 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,7 +40,7 @@ zeroize = { version = "1", default-features = false, features = ["zeroize_derive zkp = { version = "0.7" } [dev-dependencies] -criterion = "*" +criterion = { version = "0.3" } curve25519-dalek = { version = "2", default-features = false } rand = { version = "0.7" } diff --git a/README.md b/README.md index e1f1bec..7c0c489 100644 --- a/README.md +++ b/README.md @@ -46,4 +46,57 @@ I'm likely not _your_ cryptographer. Use at your own risk. Usage ------- -XXX +```rust +extern crate aeonflux; +extern crate curve25519_dalek; +extern crate rand; + +use aeonflux::amacs::Attribute; +use aeonflux::issuer::Issuer; +use aeonflux::parameters::IssuerParameters; +use aeonflux::parameters::SystemParameters; +use aeonflux::symmetric::Plaintext; +use aeonflux::symmetric::Keypair as SymmetricKeypair; + +use curve25519_dalek::ristretto::RistrettoPoint; +use curve25519_dalek::scalar::Scalar; + +use rand::thread_rng; + +// First we set up an anonymous credential issuer. We have to specify +// the number of attributes the credentials will have (here, eight), +// but not their type. +let mut rng = thread_rng(); +let system_parameters = SystemParameters::generate(&mut rng, 8).unwrap(); +let issuer = Issuer::new(&system_parameters, &mut rng); + +// Our user creates a request for a new credential with some revealed +// attributes and sends it to the issuer. +let plaintext: Plaintext = b"This is a tsunami alert test..".into(); + +let mut attributes = Vec::new(); + +attributes.push(Attribute::SecretPoint(plaintext)); +attributes.push(Attribute::SecretScalar(Scalar::random(&mut rng))); +attributes.push(Attribute::SecretScalar(Scalar::random(&mut rng))); +attributes.push(Attribute::PublicScalar(Scalar::random(&mut rng))); +attributes.push(Attribute::PublicPoint(RistrettoPoint::random(&mut rng))); +attributes.push(Attribute::SecretScalar(Scalar::random(&mut rng))); +attributes.push(Attribute::PublicScalar(Scalar::random(&mut rng))); +attributes.push(Attribute::PublicPoint(RistrettoPoint::random(&mut rng))); + +let credential = issuer.issue(attributes, &mut rng).unwrap(); + +// Optionally, upon showing the credential, the user can create a +// keypair and encrypt some or all of the attributes. The master secret +// can be stored to regenerate the full keypair later on. Encryption +// keys can be rotated to rerandomise the encrypted attributes. +let (keypair, master_secret) = SymmetricKeypair::generate(&system_parameters, &mut rng); +let proof = credential.show(&system_parameters, &issuer.issuer_parameters, Some(&keypair), &mut rng); + +assert!(proof.is_ok()); + +let verification = proof.unwrap().verify(&issuer, &credential); + +assert!(verification.is_ok()); +``` diff --git a/src/amacs.rs b/src/amacs.rs index 4516632..9e5a681 100644 --- a/src/amacs.rs +++ b/src/amacs.rs @@ -39,6 +39,11 @@ use crate::errors::MacError; use crate::parameters::SystemParameters; use crate::symmetric::Plaintext; +/// Determine the size of a [`SecretKey`], in bytes. +pub(crate) fn sizeof_secret_key(number_of_attributes: u8) -> usize { + 32 * (6 + number_of_attributes) as usize +} + /// An AMAC secret key is \(( (w, w', x_0, x_1, \vec{y_{n}}, W ) \in \mathbb{Z}_q \)) /// where \(( W := G_w * w \)). (The \(( G_w \)) is one of the orthogonal generators /// from the [`SystemParameters`].) @@ -101,12 +106,12 @@ impl SecretKey { } /// DOCDOC - pub fn from_bytes(bytes: &[u8]) -> Result { + pub(crate) fn from_bytes(bytes: &[u8]) -> Result { unimplemented!() } /// DOCDOC - pub fn to_bytes(&self) -> Vec { + pub(crate) fn to_bytes(&self) -> Vec { unimplemented!() } } @@ -120,20 +125,54 @@ impl_serde_with_to_bytes_and_from_bytes!(SecretKey, "A valid byte sequence repre /// When a `Credential` is shown, its attributes may be either revealed or /// hidden from the credential issuer. These represent all the valid attribute /// types. +#[derive(Clone)] pub enum Attribute { + /// A scalar attribute which is revealed upon credential presentation. PublicScalar(Scalar), + /// A scalar attribute which is hidden upon credential presentation. SecretScalar(Scalar), + /// A group element attribute which is revealed upon credential presentation. PublicPoint(RistrettoPoint), + /// A group element attribute which is hidden upon credential presentation. SecretPoint(Plaintext), } -// XXX impl Drop for Attribute? +// We can't derive this because generally in elliptic curve cryptography group +// elements aren't used as secrets, thus curve25519-dalek doesn't impl Zeroize +// for RistrettoPoint. +impl Zeroize for Attribute { + fn zeroize(&mut self) { + match self { + Attribute::SecretScalar(x) => x.zeroize(), + Attribute::SecretPoint(x) => x.zeroize(), + _ => return, + } + } +} + +/// Overwrite the secret attributes with zeroes (and the identity element) +/// when it drops out of scope. +impl Drop for Attribute { + fn drop(&mut self) { + self.zeroize(); + } +} -/// DOCDOC + +/// These are the form of the attributes during credential presentation, when +/// some may be be hidden either by commiting to them and proving them in +/// zero-knowledge (as is the case for hidden scalar attributes) or by +/// encrypting them and proving the ciphertext's validity in zero-knowledge (as +/// is the case for the hidden group element attributes). +#[derive(Clone)] pub enum EncryptedAttribute { + /// A scalar attribute which is revealed upon credential presentation. PublicScalar(Scalar), + /// A scalar attribute which is hidden upon credential presentation. SecretScalar, + /// A group element attribute which is revealed upon credential presentation. PublicPoint(RistrettoPoint), + /// A group element attribute which is hidden upon credential presentation. SecretPoint, } @@ -163,10 +202,10 @@ impl Messages { } /// An algebraic message authentication code, \(( (t,U,V) \in \mathbb{Z}_q \times \mathbb{G} \times \mathbb{G} \)). -pub struct Amac { - pub t: Scalar, - pub U: RistrettoPoint, - pub V: RistrettoPoint, +pub(crate) struct Amac { + pub(crate) t: Scalar, + pub(crate) U: RistrettoPoint, + pub(crate) V: RistrettoPoint, } impl Amac { @@ -191,7 +230,7 @@ impl Amac { /// Compute an algebraic message authentication code with a secret key for a /// vector of messages. - pub fn tag( + pub(crate) fn tag( csprng: &mut R, system_parameters: &SystemParameters, secret_key: &SecretKey, @@ -213,7 +252,7 @@ impl Amac { } /// Verify this algebraic MAC w.r.t. a secret key and vector of messages. - pub fn verify( + pub(crate) fn verify( &self, system_parameters: &SystemParameters, secret_key: &SecretKey, diff --git a/src/credential.rs b/src/credential.rs index 63ab21a..f10d7da 100644 --- a/src/credential.rs +++ b/src/credential.rs @@ -14,10 +14,35 @@ use alloc::vec::Vec; #[cfg(all(not(feature = "alloc"), feature = "std"))] use std::vec::Vec; +use rand_core::CryptoRng; +use rand_core::RngCore; + use crate::amacs::Amac; use crate::amacs::Attribute; +use crate::errors::CredentialError; +use crate::parameters::IssuerParameters; +use crate::parameters::SystemParameters; +use crate::nizk::ProofOfValidCredential; +use crate::symmetric::Keypair as SymmetricKeypair; +/// An anonymous credential. pub struct AnonymousCredential { pub(crate) amac: Amac, pub(crate) attributes: Vec, } + +impl AnonymousCredential { + /// Present this credential to an issuer. + pub fn show( + &self, + system_parameters: &SystemParameters, + issuer_parameters: &IssuerParameters, + keypair: Option<&SymmetricKeypair>, + csprng: &mut C, + ) -> Result + where + C: CryptoRng + RngCore, + { + ProofOfValidCredential::prove(&system_parameters, &issuer_parameters, &self, keypair, csprng) + } +} diff --git a/src/errors.rs b/src/errors.rs index 64e02dc..f8adec3 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -8,6 +8,8 @@ // Authors: // - isis agora lovecruft +//! Errors which may occur during anonymous credential issuance and verification. + #[cfg(feature = "std")] use std::convert::From; #[cfg(feature = "std")] @@ -32,7 +34,7 @@ use core::option::NoneError; use zkp::ProofError; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub enum MacError { +pub(crate) enum MacError { KeypairDeserialisation, PointDecompressionError, ScalarFormatError, diff --git a/src/issuer.rs b/src/issuer.rs index 562f9a8..50996aa 100644 --- a/src/issuer.rs +++ b/src/issuer.rs @@ -17,11 +17,19 @@ use std::vec::Vec; use rand_core::CryptoRng; use rand_core::RngCore; +use serde::de::Deserialize; +use serde::de::Deserializer; +use serde::de::Visitor; +use serde::ser::Serialize; +use serde::ser::Serializer; + +use crate::amacs::sizeof_secret_key; use crate::amacs::Amac; use crate::amacs::Attribute; use crate::amacs::SecretKey; use crate::credential::AnonymousCredential; use crate::errors::CredentialError; +use crate::parameters::sizeof_system_parameters; use crate::parameters::IssuerParameters; use crate::parameters::SystemParameters; @@ -33,16 +41,30 @@ pub struct Issuer { } impl Issuer { - pub fn new( + /// Create a new anonymous credential issuer and verifier. + /// + /// # Inputs + /// + /// * Some previously generated [`SystemParameters`]. + /// * A cryptographically secure PRNG. + /// + /// # Returns + /// + /// An new issuer. + pub fn new( system_parameters: &SystemParameters, - issuer_parameters: &IssuerParameters, - amacs_key: &SecretKey + csprng: &mut C, ) -> Issuer + where + C: CryptoRng + RngCore, { + let amacs_key = SecretKey::generate(csprng, &system_parameters); + let issuer_parameters = IssuerParameters::generate(&system_parameters, &amacs_key); + Issuer { system_parameters: system_parameters.clone(), - issuer_parameters: issuer_parameters.clone(), - amacs_key: amacs_key.clone(), + issuer_parameters: issuer_parameters, + amacs_key: amacs_key, } } @@ -76,3 +98,32 @@ impl Issuer { } } } + +impl Issuer { + /// Create an [`Issuer`] from bytes. + pub fn from_bytes(bytes: &[u8]) -> Result { + let system_parameters = SystemParameters::from_bytes(&bytes)?; + let offset = sizeof_system_parameters(system_parameters.NUMBER_OF_ATTRIBUTES); + let issuer_parameters = IssuerParameters::from_bytes(&bytes[offset..offset+64])?; + let amacs_key = SecretKey::from_bytes(&bytes[offset+64..])?; + + Ok(Issuer { system_parameters, issuer_parameters, amacs_key }) + } + + /// Serialise this [`Issuer`] to a byte array. + pub fn to_bytes(&self) -> Vec { + let size = 64 + + sizeof_system_parameters(self.system_parameters.NUMBER_OF_ATTRIBUTES) + + sizeof_secret_key(self.system_parameters.NUMBER_OF_ATTRIBUTES); + + let mut bytes: Vec = Vec::with_capacity(size); + + bytes.extend(self.system_parameters.to_bytes()); + bytes.extend(self.issuer_parameters.to_bytes()); + bytes.extend(self.amacs_key.to_bytes()); + + bytes + } +} + +impl_serde_with_to_bytes_and_from_bytes!(Issuer, "A valid byte sequence representing an Issuer"); diff --git a/src/lib.rs b/src/lib.rs index b140c36..ed31c88 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,6 +10,17 @@ #![no_std] +#![cfg_attr(feature = "nightly", feature(external_doc))] +#![cfg_attr(feature = "nightly", feature(doc_cfg))] + +// Refuse to compile if documentation is missing, but only on nightly. +// +// This means that missing docs will still fail CI, but means we can use +// README.md as the crate documentation. +//#![cfg_attr(feature = "nightly", deny(missing_docs))] + +#![cfg_attr(feature = "nightly", doc(include = "../README.md"))] + // TODO Get rid of the syntax that uses the nightly-only try_trait. #![feature(try_trait)] // We denote group elements with capital and scalars with lowercased names. @@ -35,7 +46,7 @@ extern crate zkp; // The macros have to come first. #[macro_use] -pub mod macros; +mod macros; pub mod amacs; pub mod credential; @@ -43,9 +54,7 @@ pub mod encoding; pub mod errors; pub mod issuer; pub mod nizk; -//pub mod nonces; pub mod parameters; -//pub mod pedersen; pub mod prelude; pub mod symmetric; //pub mod user; diff --git a/src/macros.rs b/src/macros.rs index 4b3615d..57ce280 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -8,7 +8,6 @@ // Authors: // - isis agora lovecruft -#[macro_export] macro_rules! impl_serde_with_to_bytes_and_from_bytes { ($t:tt, $expecting:expr) => { impl Serialize for $t { diff --git a/src/nizk.rs b/src/nizk.rs index 64ee18b..a2b9ef3 100644 --- a/src/nizk.rs +++ b/src/nizk.rs @@ -40,7 +40,6 @@ use zkp::toolbox::verifier::ScalarVar as VerifierScalarVar; use crate::amacs::Attribute; use crate::amacs::EncryptedAttribute; use crate::amacs::Messages; -use crate::amacs::SecretKey; use crate::credential::AnonymousCredential; use crate::errors::CredentialError; use crate::issuer::Issuer; @@ -58,9 +57,7 @@ pub struct ProofOfIssuance(CompactProof); impl ProofOfIssuance { /// Create a [`ProofOfIssuance`]. pub fn prove( - secret_key: &SecretKey, - system_parameters: &SystemParameters, - issuer_parameters: &IssuerParameters, + issuer: &Issuer, credential: &AnonymousCredential, ) -> ProofOfIssuance { @@ -71,14 +68,14 @@ impl ProofOfIssuance { let mut prover = Prover::new(b"2019/1416 issuance proof", &mut transcript); // Commit the names of the Camenisch-Stadler secrets to the protocol transcript. - let w = prover.allocate_scalar(b"w", secret_key.w); - let w_prime = prover.allocate_scalar(b"w'", secret_key.w_prime); - let x_0 = prover.allocate_scalar(b"x_0", secret_key.x_0); - let x_1 = prover.allocate_scalar(b"x_1", secret_key.x_1); + let w = prover.allocate_scalar(b"w", issuer.amacs_key.w); + let w_prime = prover.allocate_scalar(b"w'", issuer.amacs_key.w_prime); + let x_0 = prover.allocate_scalar(b"x_0", issuer.amacs_key.x_0); + let x_1 = prover.allocate_scalar(b"x_1", issuer.amacs_key.x_1); - let mut y: Vec = Vec::with_capacity(system_parameters.NUMBER_OF_ATTRIBUTES as usize); + let mut y: Vec = Vec::with_capacity(issuer.system_parameters.NUMBER_OF_ATTRIBUTES as usize); - for (i, y_i) in secret_key.y.iter().enumerate() { + for (i, y_i) in issuer.amacs_key.y.iter().enumerate() { // XXX fix the zkp crate to take Strings //y.push(prover.allocate_scalar(format!("y_{}", i), y_i)); y.push(prover.allocate_scalar(b"y", *y_i)); @@ -92,16 +89,16 @@ impl ProofOfIssuance { let t = prover.allocate_scalar(b"t", credential.amac.t); // Commit to the values and names of the Camenisch-Stadler publics. - let (neg_G_V, _) = prover.allocate_point(b"-G_V", -system_parameters.G_V); - let (G, _) = prover.allocate_point(b"G", system_parameters.G); - let (G_w, _) = prover.allocate_point(b"G_w", system_parameters.G_w); - let (G_w_prime, _) = prover.allocate_point(b"G_w_prime", system_parameters.G_w_prime); - let (G_x_0, _) = prover.allocate_point(b"G_x_0", system_parameters.G_x_0); - let (G_x_1, _) = prover.allocate_point(b"G_x_1", system_parameters.G_x_1); + let (neg_G_V, _) = prover.allocate_point(b"-G_V", -issuer.system_parameters.G_V); + let (G, _) = prover.allocate_point(b"G", issuer.system_parameters.G); + let (G_w, _) = prover.allocate_point(b"G_w", issuer.system_parameters.G_w); + let (G_w_prime, _) = prover.allocate_point(b"G_w_prime", issuer.system_parameters.G_w_prime); + let (G_x_0, _) = prover.allocate_point(b"G_x_0", issuer.system_parameters.G_x_0); + let (G_x_1, _) = prover.allocate_point(b"G_x_1", issuer.system_parameters.G_x_1); - let mut G_y: Vec = Vec::with_capacity(system_parameters.NUMBER_OF_ATTRIBUTES as usize); + let mut G_y: Vec = Vec::with_capacity(issuer.system_parameters.NUMBER_OF_ATTRIBUTES as usize); - for (i, G_y_i) in system_parameters.G_y.iter().enumerate() { + for (i, G_y_i) in issuer.system_parameters.G_y.iter().enumerate() { // XXX fix the zkp crate to take Strings //let (G_y_x, _) = prover.allocate_point(format!("G_y_{}", i), G_y_i); let (G_y_x, _) = prover.allocate_point(b"G_y", *G_y_i); @@ -109,14 +106,14 @@ impl ProofOfIssuance { G_y.push(G_y_x); } - let (C_W, _) = prover.allocate_point(b"C_W", issuer_parameters.C_W); - let (I, _) = prover.allocate_point(b"I", issuer_parameters.I); + let (C_W, _) = prover.allocate_point(b"C_W", issuer.issuer_parameters.C_W); + let (I, _) = prover.allocate_point(b"I", issuer.issuer_parameters.I); let (U, _) = prover.allocate_point(b"U", credential.amac.U); let (V, _) = prover.allocate_point(b"V", credential.amac.V); - let mut M: Vec = Vec::with_capacity(system_parameters.NUMBER_OF_ATTRIBUTES as usize); + let mut M: Vec = Vec::with_capacity(issuer.system_parameters.NUMBER_OF_ATTRIBUTES as usize); - let messages: Messages = Messages::from_attributes(&credential.attributes, system_parameters); + let messages: Messages = Messages::from_attributes(&credential.attributes, &issuer.system_parameters); for (i, M_i) in messages.0.iter().enumerate() { // XXX fix the zkp crate to take Strings @@ -130,7 +127,7 @@ impl ProofOfIssuance { prover.constrain(C_W, vec![(w, G_w), (w_prime, G_w_prime)]); // Constraint #2: I = -G_V + G_x_0 * x_0 + G_x_1 * x_1 + G_y_1 * y_1 + ... + G_y_n * y_n - let mut rhs: Vec<(ScalarVar, PointVar)> = Vec::with_capacity(3 + system_parameters.NUMBER_OF_ATTRIBUTES as usize); + let mut rhs: Vec<(ScalarVar, PointVar)> = Vec::with_capacity(3 + issuer.system_parameters.NUMBER_OF_ATTRIBUTES as usize); rhs.push((one, neg_G_V)); rhs.push((x_0, G_x_0)); @@ -140,7 +137,7 @@ impl ProofOfIssuance { prover.constrain(I, rhs); // Constraint #3: V = G_w * w + U * x_0 + U * x_1 + U * t + \sigma{i=1}{n} M_i * y_i - let mut rhs: Vec<(ScalarVar, PointVar)> = Vec::with_capacity(4 + system_parameters.NUMBER_OF_ATTRIBUTES as usize); + let mut rhs: Vec<(ScalarVar, PointVar)> = Vec::with_capacity(4 + issuer.system_parameters.NUMBER_OF_ATTRIBUTES as usize); rhs.push((w, G_w)); rhs.push((x_0, U)); @@ -815,9 +812,7 @@ mod test { fn issuance_proof() { let mut rng = thread_rng(); let system_parameters = SystemParameters::generate(&mut rng, 5).unwrap(); - let amacs_key = SecretKey::generate(&mut rng, &system_parameters); - let issuer_parameters = IssuerParameters::generate(&system_parameters, &amacs_key); - let issuer = Issuer::new(&system_parameters, &issuer_parameters, &amacs_key); + let issuer = Issuer::new(&system_parameters, &mut rng); let plaintext: Plaintext = (&[1u8; 30]).into(); let mut attributes = Vec::new(); @@ -828,8 +823,8 @@ mod test { attributes.push(Attribute::SecretScalar(Scalar::random(&mut rng))); let credential = issuer.issue(attributes, &mut rng).unwrap(); - let proof = ProofOfIssuance::prove(&amacs_key, &system_parameters, &issuer_parameters, &credential); - let verification = proof.verify(&system_parameters, &issuer_parameters, &credential); + let proof = ProofOfIssuance::prove(&issuer, &credential); + let verification = proof.verify(&system_parameters, &issuer.issuer_parameters, &credential); assert!(verification.is_ok()); } @@ -840,9 +835,7 @@ mod test { fn issuance_proof_identity_plaintext() { let mut rng = thread_rng(); let system_parameters = SystemParameters::generate(&mut rng, 5).unwrap(); - let amacs_key = SecretKey::generate(&mut rng, &system_parameters); - let issuer_parameters = IssuerParameters::generate(&system_parameters, &amacs_key); - let issuer = Issuer::new(&system_parameters, &issuer_parameters, &amacs_key); + let issuer = Issuer::new(&system_parameters, &mut rng); let plaintext: Plaintext = (&[0u8; 30]).into(); assert!(plaintext.M1.is_identity()); @@ -855,8 +848,8 @@ mod test { attributes.push(Attribute::SecretScalar(Scalar::random(&mut rng))); let credential = issuer.issue(attributes, &mut rng).unwrap(); - let proof = ProofOfIssuance::prove(&amacs_key, &system_parameters, &issuer_parameters, &credential); - let verification = proof.verify(&system_parameters, &issuer_parameters, &credential); + let proof = ProofOfIssuance::prove(&issuer, &credential); + let verification = proof.verify(&system_parameters, &issuer.issuer_parameters, &credential); assert!(verification.is_ok()); } @@ -890,9 +883,7 @@ mod test { fn credential_proof_8_attributes() { let mut rng = thread_rng(); let system_parameters = SystemParameters::generate(&mut rng, 8).unwrap(); - let amacs_key = SecretKey::generate(&mut rng, &system_parameters); - let issuer_parameters = IssuerParameters::generate(&system_parameters, &amacs_key); - let issuer = Issuer::new(&system_parameters, &issuer_parameters, &amacs_key); + let issuer = Issuer::new(&system_parameters, &mut rng); let plaintext: Plaintext = b"This is a tsunami alert test..".into(); let mut attributes = Vec::new(); @@ -908,7 +899,7 @@ mod test { let credential = issuer.issue(attributes, &mut rng).unwrap(); let (keypair, master_secret) = SymmetricKeypair::generate(&system_parameters, &mut rng); - let proof = ProofOfValidCredential::prove(&system_parameters, &issuer_parameters, &credential, Some(&keypair), &mut rng); + let proof = ProofOfValidCredential::prove(&system_parameters, &issuer.issuer_parameters, &credential, Some(&keypair), &mut rng); assert!(proof.is_ok()); @@ -916,4 +907,27 @@ mod test { assert!(verification.is_ok()); } + + #[test] + fn credential_proof_1_attribute() { + let mut rng = thread_rng(); + let system_parameters = SystemParameters::generate(&mut rng, 1).unwrap(); + let issuer = Issuer::new(&system_parameters, &mut rng); + let plaintext: Plaintext = b"This is a tsunami alert test..".into(); + + let mut attributes = Vec::new(); + + attributes.push(Attribute::SecretPoint(plaintext)); + + let credential = issuer.issue(attributes, &mut rng).unwrap(); + let (keypair, _) = SymmetricKeypair::generate(&system_parameters, &mut rng); + let proof = ProofOfValidCredential::prove(&system_parameters, &issuer.issuer_parameters, &credential, Some(&keypair), &mut rng); + + assert!(proof.is_ok()); + + let verification = proof.unwrap().verify(&issuer, &credential); + + assert!(verification.is_ok()); + } + } diff --git a/src/symmetric.rs b/src/symmetric.rs index 323f885..8a276f3 100644 --- a/src/symmetric.rs +++ b/src/symmetric.rs @@ -24,6 +24,7 @@ use curve25519_dalek::ristretto::RistrettoPoint; use curve25519_dalek::scalar::Scalar; +use curve25519_dalek::traits::Identity; use rand_core::CryptoRng; use rand_core::RngCore; @@ -33,6 +34,8 @@ use sha2::Sha512; use subtle::Choice; use subtle::ConstantTimeEq; +use zeroize::Zeroize; + use crate::encoding::decode_from_group; use crate::encoding::encode_to_group; use crate::errors::CredentialError; @@ -40,14 +43,20 @@ use crate::parameters::SystemParameters; /// A secret key, used for hidden group element attributes during credential /// presentation. -#[derive(Clone)] +#[derive(Clone, Zeroize)] pub(crate) struct SecretKey { pub(crate) a: Scalar, pub(crate) a0: Scalar, pub(crate) a1: Scalar, } -// XXX impl Drop for SecretKey +/// Overwrite the secret key material with zeroes (and the identity element) +/// when it drops out of scope. +impl Drop for SecretKey { + fn drop(&mut self) { + self.zeroize(); + } +} /// A public key, used for verification of a symmetrica encryption. #[derive(Clone, Copy)] @@ -58,7 +67,9 @@ pub struct PublicKey { /// A keypair for encryption of hidden group element attributes. #[derive(Clone)] pub struct Keypair { + /// The secret portion of this keypair. pub(crate) secret: SecretKey, + /// The public portion of this keypair. pub public: PublicKey, } @@ -79,6 +90,25 @@ pub struct Plaintext { pub(crate) m3: Scalar, } +// We can't derive this because generally in elliptic curve cryptography group +// elements aren't used as secrets, thus curve25519-dalek doesn't impl Zeroize +// for RistrettoPoint. +impl Zeroize for Plaintext { + fn zeroize(&mut self) { + self.M1 = RistrettoPoint::identity(); + self.M2 = RistrettoPoint::identity(); + self.m3.zeroize(); + } +} + +/// Overwrite the plaintext with zeroes (and the identity element) +/// when it drops out of scope. +impl Drop for Plaintext { + fn drop(&mut self) { + self.zeroize(); + } +} + impl From<&[u8; 30]> for Plaintext { fn from(source: &[u8; 30]) -> Plaintext { let (M1, _) = encode_to_group(source); From ff5ffad970f04c70aafa9643c7e2ad182bd7c03a Mon Sep 17 00:00:00 2001 From: Isis Lovecruft Date: Thu, 18 Jun 2020 20:10:59 +0000 Subject: [PATCH 2/2] Fix typo in README. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c0c489..11d4723 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ all the attributes on their credentials to the issuer when requesting a new credential. When presenting said credential afterwards, attributes may be either hidden or revealed. -Credentials may be either scalars (integers modulo the group order, a large +Credential attributes may be either scalars (integers modulo the group order, a large prime) or group elements. This library provides a way to encode arbitrary byte arrays to group elements---which may then be encrypted and decrypted---in an invertible manner, such that arbitrary strings can be stored as attributes.