├── .gitignore ├── Cargo.toml ├── readme.md ├── src ├── constants.rs ├── keys.rs ├── lib.rs ├── member.rs ├── mlsag.rs ├── signature.rs ├── tests_helper.rs └── transcript.rs └── tests └── protocol.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mlsag" 3 | version = "0.3.0" 4 | authors = ["Kevaundray Wedderburn ); 16 | 17 | impl PublicSet { 18 | // Returns the number of public keys in the set 19 | pub fn len(&self) -> usize { 20 | self.0.len() 21 | } 22 | // Checks if the public set contains any duplicate keys 23 | pub fn duplicates_exist(&self) -> bool { 24 | // XXX: Very in-efficient way to do this. 25 | // We can wait for upstream crate to implement Hash and use a HashSet instead 26 | 27 | let compressed_points: Vec = 28 | self.0.iter().map(|point| point.compress()).collect(); 29 | 30 | let hashable_slice: Vec<&[u8; 32]> = 31 | compressed_points.iter().map(|cp| cp.as_bytes()).collect(); 32 | 33 | let uniques: HashSet<_> = hashable_slice.iter().collect(); 34 | 35 | self.0.len() != uniques.len() 36 | } 37 | // Copies the public key set into a vector of bytes 38 | pub fn compress(&self) -> Vec { 39 | self.0.iter().map(|point| point.compress()).collect() 40 | } 41 | } 42 | 43 | #[derive(Debug, Clone)] 44 | pub struct PrivateSet(pub(crate) Vec); 45 | 46 | impl PrivateSet { 47 | pub fn new(scalars: Vec) -> Self { 48 | PrivateSet(scalars) 49 | } 50 | // Takes a set of private keys 51 | // and returns the corresponding public key set 52 | pub fn to_public_set(&self) -> PublicSet { 53 | let public_keys = self 54 | .0 55 | .iter() 56 | .map(|&x| x * BASEPOINT) 57 | .collect::>(); 58 | 59 | PublicSet(public_keys) 60 | } 61 | 62 | // Returns all of the keyImages for a specific private key set 63 | // We calculate the key image using the formula keyImage = privateKey * HashToPoint(PublicKey) 64 | // Note that the HashToPoint must not allow the basepoint in the public key to be factored out 65 | pub fn compute_key_images(&self, public_set: &PublicSet) -> Vec { 66 | // Set of private keys must be the same length as the set of private keys 67 | assert_eq!(self.len(), public_set.len()); 68 | 69 | let num_public_keys = public_set.len(); 70 | 71 | let mut key_images: Vec = Vec::with_capacity(num_public_keys); 72 | 73 | for i in 0..num_public_keys { 74 | let public_key_i = public_set.0[i].compress(); 75 | let private_key_i = self.0[i]; 76 | 77 | let hashed_public_key = 78 | RistrettoPoint::hash_from_bytes::(public_key_i.as_bytes()); 79 | 80 | let key_image = private_key_i * hashed_public_key; 81 | 82 | key_images.push(key_image); 83 | } 84 | 85 | key_images 86 | } 87 | 88 | // Returns the number of private keys in the set 89 | pub fn len(&self) -> usize { 90 | self.0.len() 91 | } 92 | } 93 | 94 | #[cfg(test)] 95 | mod test { 96 | use super::*; 97 | use crate::tests_helper::*; 98 | // This test is a sanity check for private to public key sets. 99 | // The iter method is used when converting from a set of private keys 100 | // to a set of public keys. In the test, we use a for loop and check that both 101 | // are equal. 102 | #[test] 103 | fn private_set_to_public_set() { 104 | let private_set = generate_private_set(10); 105 | let public_set = private_set.to_public_set(); 106 | 107 | assert_eq!(private_set.len(), public_set.len()); 108 | 109 | for i in 0..private_set.len() { 110 | match (private_set.0.get(i), public_set.0.get(i)) { 111 | (Some(private_key), Some(expected_public_key)) => { 112 | let public_key = private_key * &BASEPOINT; 113 | assert_eq!(public_key, *expected_public_key); 114 | } 115 | _ => panic!("could not get the private/public key at index {} ", i), 116 | } 117 | } 118 | } 119 | #[test] 120 | fn check_duplicates_exist() { 121 | let private_set = generate_private_set(10); 122 | let mut public_set = private_set.to_public_set(); 123 | 124 | let dup_exists = public_set.duplicates_exist(); 125 | assert!(!dup_exists); 126 | 127 | let last_element = public_set.0.last().unwrap().clone(); 128 | public_set.0[0] = last_element; 129 | 130 | let dup_exists = public_set.duplicates_exist(); 131 | assert!(dup_exists); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | pub mod constants; 3 | pub mod keys; 4 | pub mod member; 5 | pub mod mlsag; 6 | pub mod signature; 7 | pub mod tests_helper; 8 | mod transcript; 9 | -------------------------------------------------------------------------------- /src/member.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::BASEPOINT; 2 | use crate::keys::{PrivateSet, PublicSet}; 3 | use crate::transcript::TranscriptProtocol; 4 | use curve25519_dalek::ristretto::RistrettoPoint; 5 | use curve25519_dalek::scalar::Scalar; 6 | use merlin::Transcript; 7 | use rand; 8 | use sha2::Sha512; 9 | 10 | #[derive(Debug)] 11 | pub enum Error { 12 | // Occurs when you try to use a method specific to 13 | // a signer as a decoy 14 | NotASigner, 15 | // Occurs when you try to use a method specific to 16 | // a decoy as a signer 17 | NotADecoy, 18 | } 19 | 20 | // A member represents a member in the ring 21 | // This includes the signer of the ring 22 | #[derive(Clone)] 23 | pub struct Member { 24 | // The signer is the only member with a set of private keys 25 | private_set: Option, 26 | 27 | pub(crate) public_set: PublicSet, 28 | 29 | // The signing member wil have a nonce per public key in the public/private set. 30 | // In an sigma protocol, this nonce would signify the commit phase. 31 | pub(crate) nonces: Option>, 32 | 33 | // Each member will have a response value per public key in their set 34 | // In an sigma protocol, this would signify the reponse phase. 35 | pub(crate) responses: Vec, 36 | } 37 | 38 | impl Member { 39 | // Creates a member who will be the signer of the ring 40 | // Protocol explicitly checks if there is one signer per ring 41 | pub fn new_signer(private_keys: Vec) -> Self { 42 | let private_set = PrivateSet::new(private_keys); 43 | 44 | let num_private_keys = private_set.len(); 45 | 46 | let nonces = generate_rand_scalars(num_private_keys); 47 | 48 | let responses = Vec::with_capacity(num_private_keys); 49 | 50 | let public_set = private_set.to_public_set(); 51 | 52 | Member { 53 | nonces: Some(nonces), 54 | 55 | public_set: public_set, 56 | 57 | private_set: Some(private_set), 58 | 59 | responses: responses, 60 | } 61 | } 62 | // Creates a member who will be a decoy in the ring 63 | pub fn new_decoy(public_keys: Vec) -> Self { 64 | let num_public_keys = public_keys.len(); 65 | let responses = generate_rand_scalars(num_public_keys); 66 | 67 | Self::new_decoy_with_responses(public_keys, responses) 68 | } 69 | 70 | // Creates a member who will be used for verification in a signature 71 | pub(crate) fn new_decoy_with_responses( 72 | public_keys: Vec, 73 | responses: Vec, 74 | ) -> Self { 75 | Member { 76 | nonces: None, 77 | 78 | public_set: PublicSet(public_keys), 79 | 80 | private_set: None, 81 | 82 | responses: responses, 83 | } 84 | } 85 | // Returns true if the member has a set of private keys 86 | pub fn is_signer(&self) -> bool { 87 | self.private_set.is_some() 88 | } 89 | // Returns the number of keys the member has 90 | pub fn num_keys(&self) -> usize { 91 | self.public_set.len() 92 | } 93 | // Computes the key images if the member is a signer 94 | pub fn compute_key_images(&self) -> Result, Error> { 95 | match &self.private_set { 96 | Some(priv_set) => Ok(priv_set.compute_key_images(&self.public_set)), 97 | None => Err(Error::NotASigner), 98 | } 99 | } 100 | 101 | // This function uses the nonces to calculate the first challenge scalar 102 | // Effectively committing the current member; the ring will therefore 103 | // only be completed if the current member can generate the corresponding 104 | // responses per nonce, which can only be done if the current member possess 105 | // the discrete log to the public keys corresponding to his position in the ring. 106 | // returns a challenge scalar or an error if the user is not a signer 107 | pub fn compute_challenge_commitment(&self, msg: &[u8]) -> Result { 108 | if !self.is_signer() { 109 | return Err(Error::NotASigner); 110 | } 111 | 112 | let nonces = match &self.nonces { 113 | Some(x) => Ok(x), 114 | _ => Err(Error::NotASigner), 115 | }?; 116 | 117 | assert_eq!(nonces.len(), self.public_set.len()); 118 | 119 | let mut transcript = Transcript::new(b"mlsag"); 120 | transcript.append_message(b"msg", msg); 121 | 122 | for (nonce, public_key) in nonces.iter().zip(self.public_set.0.iter()) { 123 | // Add `nonce_i * basepoint` to the transcript 124 | transcript.append_scalar_mult(b"", nonce, &BASEPOINT); 125 | 126 | // Append `nonce_i * Hash(PublicKey_i) 127 | transcript.append_scalar_hash_point(b"", nonce, public_key); 128 | } 129 | 130 | Ok(transcript.challenge_scalar(b"")) 131 | } 132 | // This function is for the signer and will use the signers 133 | // private set to calculate the correct response values 134 | // returns a vector of responses or an error, if the user is not a signer 135 | pub fn compute_signer_responses(&self, challenge: Scalar) -> Result, Error> { 136 | let private_set = self.private_set.as_ref().ok_or(Error::NotASigner)?; 137 | let nonces = self.nonces.as_ref().ok_or(Error::NotASigner)?; 138 | 139 | let nonces_len = nonces.len(); 140 | 141 | let mut signer_responses: Vec = Vec::with_capacity(nonces_len); 142 | 143 | // calculate r_i = nonce_i - c * private_key_i 144 | for i in 0..nonces_len { 145 | let nonce = nonces[i]; 146 | let private_key = private_set.0[i]; 147 | 148 | let response = nonce - challenge * private_key; 149 | 150 | signer_responses.push(response); 151 | } 152 | 153 | Ok(signer_responses) 154 | } 155 | // This function is ran by all members who did not compute the challenge commitment (decoys) 156 | // Each member that runs this function, will link themselves to the ring using the challenge 157 | // passed to them by the newest member of the ring. 158 | // returns a challenge scalar, to be used by the next member who wants to join the ring 159 | pub fn compute_decoy_challenge( 160 | &self, 161 | challenge: &Scalar, 162 | key_images: &[RistrettoPoint], 163 | msg: &[u8], 164 | ) -> Result { 165 | if self.private_set.is_some() { 166 | return Err(Error::NotADecoy); 167 | } 168 | 169 | assert_eq!(self.public_set.len(), self.responses.len()); 170 | assert_eq!(self.public_set.len(), key_images.len()); 171 | 172 | let challenge = compute_challenge_ring( 173 | &self.public_set.0, 174 | challenge, 175 | key_images, 176 | &self.responses, 177 | msg, 178 | ); 179 | 180 | Ok(challenge) 181 | } 182 | } 183 | // A generic function to calculate the challenge for any member in the ring 184 | // While signing, this function will be used by the decoys 185 | // When verifying this function will be used by all members 186 | pub fn compute_challenge_ring( 187 | public_keys: &[RistrettoPoint], 188 | challenge: &Scalar, 189 | key_images: &[RistrettoPoint], 190 | responses: &[Scalar], 191 | msg: &[u8], 192 | ) -> Scalar { 193 | let mut transcript = Transcript::new(b"mlsag"); 194 | transcript.append_message(b"msg", msg); 195 | 196 | for i in 0..public_keys.len() { 197 | let response = &responses[i]; 198 | let public_key = &public_keys[i]; 199 | let key_image = &key_images[i]; 200 | 201 | // Append `r_i * basepoint + c * PublicKey_i 202 | transcript.append_double_scalar_mult_add( 203 | b"", 204 | (response, challenge), 205 | (&BASEPOINT, public_key), 206 | ); 207 | 208 | let hashed_pub_key = 209 | &RistrettoPoint::hash_from_bytes::(public_key.compress().as_bytes()); 210 | 211 | // Append `r_i * HashToPoint(PublicKey_i) + c * KeyImage_i 212 | transcript.append_double_scalar_mult_add( 213 | b"", 214 | (response, challenge), 215 | (hashed_pub_key, key_image), 216 | ); 217 | } 218 | transcript.challenge_scalar(b"") 219 | } 220 | 221 | fn generate_rand_scalars(num: usize) -> Vec { 222 | let mut rng = rand::thread_rng(); 223 | let mut scalars = Vec::::with_capacity(num); 224 | 225 | for _ in 0..num { 226 | scalars.push(Scalar::random(&mut rng)); 227 | } 228 | 229 | scalars 230 | } 231 | 232 | #[cfg(test)] 233 | mod test { 234 | use super::*; 235 | // Simple tests to check that when the members are instantiated 236 | // We have the correct number of values 237 | #[test] 238 | fn test_new() { 239 | let num_private_keys = 10; 240 | let scalars = generate_rand_scalars(num_private_keys); 241 | 242 | let signer = Member::new_signer(scalars); 243 | 244 | // We should have a nonce per public/private key for the signer 245 | match signer.nonces { 246 | Some(nonces) => { 247 | assert_eq!(nonces.len(), num_private_keys); 248 | } 249 | None => panic!( 250 | "We should not have a `None` value here as we have instantiated a signing member" 251 | ), 252 | } 253 | 254 | // The number of private keys argument we passed in as an argument 255 | //should equal the length of the private key set 256 | match signer.private_set { 257 | Some(priv_set) => { 258 | assert_eq!(priv_set.len(), num_private_keys); 259 | } 260 | _ => panic!("we should not have a `None` value for the private key set"), 261 | } 262 | 263 | // The number of private keys argument we passed in as an argument 264 | //should equal the length of the public key set 265 | assert_eq!(signer.public_set.len(), num_private_keys) 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /src/mlsag.rs: -------------------------------------------------------------------------------- 1 | use crate::member::Member; 2 | use crate::signature::Signature; 3 | use curve25519_dalek::ristretto::CompressedRistretto; 4 | use curve25519_dalek::scalar::Scalar; 5 | 6 | // This module will pull together all of the necessary things 7 | // Setting up everything we need 8 | #[derive(Debug)] 9 | pub enum Error { 10 | // This error occurs when the sign method is called 11 | // without a signer being in the ring 12 | NoSigner, 13 | // This error occurs if there are less than 2 members in the ring 14 | NotEnoughMembers, 15 | // This error occurs if all members do not have the same number of keys 16 | NumberOfKeysMismatch, 17 | // This error occurs if there is more than one signer in the ring 18 | MoreThanOneSigner, 19 | // This error occurs if a member in the ring has duplicate keys 20 | DuplicateKeysExist, 21 | // This error occurs when an underlying module produces an error 22 | UnderlyingErr(String), 23 | } 24 | 25 | impl From for crate::mlsag::Error { 26 | fn from(e: crate::member::Error) -> crate::mlsag::Error { 27 | match e { 28 | crate::member::Error::NotASigner => Error::UnderlyingErr(String::from( 29 | "Tried to use a method specific to a signer in the mlsag module", 30 | )), 31 | crate::member::Error::NotADecoy => Error::UnderlyingErr(String::from( 32 | "Tried to use a method specific to a decoy in the mlsag module", 33 | )), 34 | } 35 | } 36 | } 37 | // This struct is used to construct the mlsag signature 38 | pub struct Mlsag { 39 | members: Vec, 40 | } 41 | 42 | impl Mlsag { 43 | // Creates a new Mlsag component with a configured basepoint 44 | pub fn new() -> Self { 45 | Mlsag { 46 | members: Vec::new(), 47 | } 48 | } 49 | // Adds a member to the Mlsag component 50 | // Use this method to add decoys and signers to the struct 51 | pub fn add_member(&mut self, member: Member) { 52 | self.members.push(member); 53 | } 54 | // Returns public keys from all known members 55 | pub fn public_keys(&self) -> Vec> { 56 | self.members 57 | .iter() 58 | .map(|member| member.public_set.compress()) 59 | .collect() 60 | } 61 | // sign produces a Mlsag signature 62 | pub fn sign(&self, msg: &[u8]) -> Result { 63 | self.check_format()?; 64 | 65 | let num_members = self.members.len(); 66 | let mut all_challenges: Vec = Vec::with_capacity(num_members); 67 | 68 | // Fetch signer of the ring 69 | let signer_index = self.find_signer()?; 70 | let signer = &self.members[signer_index]; 71 | 72 | // Compute key images for signer 73 | let key_images = signer.compute_key_images()?; 74 | 75 | // Commit all randomly generated nonces to produce the first commitment 76 | // In this phase, we can imagine that each member produces the challenge for the member in 77 | // the position after them. 78 | let mut challenge = signer.compute_challenge_commitment(msg)?; 79 | all_challenges.push(challenge); 80 | 81 | // seed challenge into for loop starting from member after signer 82 | for decoy in self 83 | .members 84 | .iter() 85 | .cycle() 86 | .skip(signer_index + 1) 87 | .take(num_members - 1) 88 | { 89 | challenge = decoy.compute_decoy_challenge(&challenge, &key_images, msg)?; 90 | all_challenges.push(challenge); 91 | } 92 | 93 | // The last challenge variable should be the one generated by the member before the signer, 94 | // which will be for the signer. The signer will use this to generate his response values 95 | // and close the ring 96 | let mut signers_responses = signer.compute_signer_responses(challenge)?; 97 | 98 | // Collect all responses 99 | let mut all_responses: Vec = Vec::with_capacity(num_members * signer.num_keys()); 100 | for member in &self.members { 101 | match member.is_signer() { 102 | true => { 103 | all_responses.append(&mut signers_responses); 104 | } 105 | false => { 106 | let mut mem_response = member.responses.clone(); 107 | all_responses.append(&mut mem_response); 108 | } 109 | } 110 | } 111 | 112 | // Collect first members challenge 113 | // The last element in the vector of challenges will be the signers challenge 114 | // Since we also know the index `n` of the signer. We need to walk back `n` times 115 | // This is equivalent to reversing the vector and getting the `n`th element 116 | // to fetch the first challenge; the challenge corresponding to the first member 117 | all_challenges.reverse(); 118 | let first_challenge = all_challenges[signer_index]; 119 | 120 | // Compress key image points 121 | let compressed_key_images = key_images.into_iter().map(|x| x.compress()).collect(); 122 | 123 | Ok(Signature { 124 | challenge: first_challenge, 125 | responses: all_responses, 126 | key_images: compressed_key_images, 127 | }) 128 | } 129 | // Returns the position of the signer in the ring 130 | // If this call is completed after check_format, it should not fail 131 | // as check_format ensures there is one signer. This method has been 132 | // added for a cleaner API. The alternative would be for the method which checks 133 | // that MLSAG is formatted correctly, to also return the signer. 134 | pub fn find_signer(&self) -> Result { 135 | let signer_index = self 136 | .members 137 | .iter() 138 | .position(|member| member.is_signer()) 139 | .ok_or(Error::NoSigner)?; 140 | 141 | Ok(signer_index) 142 | } 143 | // Returns the number of signers in the ring 144 | pub fn num_signers(&self) -> usize { 145 | let signers: Vec<&Member> = self 146 | .members 147 | .iter() 148 | .filter(|member| member.is_signer()) 149 | .collect(); 150 | signers.len() 151 | } 152 | // Checks that the Mlsag is correctly constructed 153 | fn check_format(&self) -> Result<(), Error> { 154 | // Check that we have more than one member 155 | if self.members.len() < 2 { 156 | return Err(Error::NotEnoughMembers); 157 | } 158 | 159 | // Check there is only one signer in the ring 160 | let num_signers = self.num_signers(); 161 | match num_signers { 162 | 0 => return Err(Error::NoSigner), 163 | 1 => (), 164 | _ => return Err(Error::MoreThanOneSigner), 165 | }; 166 | 167 | // Check that each member has the same number of keys 168 | let first_member_num_keys = self.members[0].num_keys(); 169 | let all_same_num_keys = self 170 | .members 171 | .iter() 172 | .all(|member| member.num_keys() == first_member_num_keys); 173 | if !all_same_num_keys { 174 | return Err(Error::NumberOfKeysMismatch); 175 | } 176 | 177 | // Check that each member has no duplicates 178 | let no_duplicates_exists = self 179 | .members 180 | .iter() 181 | .all(|member| !member.public_set.duplicates_exist()); 182 | if !no_duplicates_exists { 183 | return Err(Error::DuplicateKeysExist); 184 | } 185 | Ok(()) 186 | } 187 | } 188 | #[cfg(test)] 189 | mod test { 190 | extern crate test; 191 | 192 | use super::*; 193 | use crate::tests_helper::*; 194 | use test::Bencher; 195 | 196 | #[test] 197 | fn test_check_format() { 198 | let num_decoys = 10; 199 | let num_keys = 3; 200 | let mut mlsag = generate_mlsag_with(num_decoys, num_keys); 201 | let msg = b"hello world"; 202 | 203 | // No signer in the ring 204 | match mlsag.sign(msg) { 205 | Ok(_) => panic!("expected an error as there is no signer in the ring"), 206 | Err(Error::NoSigner) => {} 207 | Err(_) => panic!("got an error, however we expected no signer error"), 208 | } 209 | 210 | // Add a signer 211 | mlsag.add_member(generate_signer(num_keys)); 212 | // Another one 213 | mlsag.add_member(generate_signer(num_keys)); 214 | 215 | // More than one signer in the ring 216 | match mlsag.sign(msg) { 217 | Ok(_) => panic!("expected an error as there are too many signers in the ring"), 218 | Err(Error::MoreThanOneSigner) => {} 219 | Err(_) => panic!("got an error, however we expected a more than one signer error"), 220 | } 221 | 222 | mlsag = generate_mlsag_with(num_decoys, num_keys); 223 | // Add different number of keys 224 | mlsag.add_member(generate_decoy(num_keys + 1)); 225 | 226 | // Add correct signer 227 | mlsag.add_member(generate_signer(num_keys)); 228 | 229 | // One member has a different number of keys 230 | match mlsag.sign(msg) { 231 | Ok(_) => { 232 | panic!("expected an error as one member has more keys than another in the ring") 233 | } 234 | Err(Error::NumberOfKeysMismatch) => {} 235 | Err(_) => panic!("got an error, however we expected a `number of keys mismatch` error"), 236 | }; 237 | 238 | mlsag = generate_mlsag_with(num_decoys, num_keys); 239 | // Add correct signer 240 | mlsag.add_member(generate_signer(num_keys)); 241 | 242 | // Set the first key in members key set to the value of the last key 243 | let first_member = &mut mlsag.members[0]; 244 | let first_member_last_element = &mut first_member.public_set.0.last().unwrap(); 245 | first_member.public_set.0[0] = first_member_last_element.clone(); 246 | 247 | match mlsag.sign(msg) { 248 | Ok(_) => panic!("expected an error as one member has a duplicate key"), 249 | Err(Error::DuplicateKeysExist) => {} 250 | Err(_) => panic!("got an error, however we expected a `duplicate keys` error"), 251 | }; 252 | } 253 | 254 | #[test] 255 | fn test_sign_no_error() { 256 | let num_decoys = 10; 257 | let num_keys = 3; 258 | let mut mlsag = generate_mlsag_with(num_decoys, num_keys); 259 | let msg = b"hello world"; 260 | // Add a signer 261 | mlsag.add_member(generate_signer(num_keys)); 262 | 263 | // Should produce no error 264 | let signature = mlsag.sign(msg).unwrap(); 265 | 266 | // number of key images should equal number of keys 267 | assert_eq!(num_keys, signature.key_images.len()); 268 | 269 | // number of responses should equal number of key images * number of members 270 | // number of members = number of decoys + signer 271 | let num_members = num_decoys + 1; 272 | assert_eq!(num_members * num_keys, signature.responses.len()); 273 | } 274 | 275 | macro_rules! param_bench_verify { 276 | ($func_name: ident,$num_keys:expr, $num_decoys :expr) => { 277 | #[bench] 278 | fn $func_name(b: &mut Bencher) { 279 | let num_keys = $num_keys; 280 | let num_decoys = $num_decoys; 281 | let msg = b"hello world"; 282 | 283 | let mut mlsag = generate_mlsag_with(num_decoys, num_keys); 284 | mlsag.add_member(generate_signer(num_keys)); 285 | let sig = mlsag.sign(msg).unwrap(); 286 | let mut pub_keys = mlsag.public_keys(); 287 | 288 | b.iter(|| sig.verify(&mut pub_keys, msg)); 289 | } 290 | }; 291 | } 292 | 293 | param_bench_verify!(bench_verify_2, 2, 2); 294 | param_bench_verify!(bench_verify_4, 2, 3); 295 | param_bench_verify!(bench_verify_6, 2, 5); 296 | param_bench_verify!(bench_verify_8, 2, 7); 297 | param_bench_verify!(bench_verify_11, 2, 10); 298 | param_bench_verify!(bench_verify_16, 2, 15); 299 | param_bench_verify!(bench_verify_32, 2, 31); 300 | param_bench_verify!(bench_verify_64, 2, 63); 301 | param_bench_verify!(bench_verify_128, 2, 127); 302 | param_bench_verify!(bench_verify_256, 2, 255); 303 | param_bench_verify!(bench_verify_512, 2, 511); 304 | } 305 | -------------------------------------------------------------------------------- /src/signature.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::ristretto::CompressedRistretto; 2 | use curve25519_dalek::ristretto::RistrettoPoint; 3 | use curve25519_dalek::scalar::Scalar; 4 | 5 | use crate::member::*; 6 | 7 | #[derive(Debug)] 8 | pub struct Signature { 9 | pub challenge: Scalar, 10 | pub responses: Vec, 11 | pub key_images: Vec, 12 | } 13 | 14 | pub enum Error { 15 | // This error occurs if the signature contains an amount of 16 | // responses, that does not match the number of key images 17 | IncorrectNumOfResponses, 18 | // This error occurs if the signature contains an amount of public keys 19 | // that does not match the number of public keys 20 | IncorrectNumOfPubKeys, 21 | // This error occurs when either one of the key images supplied cannot be decompressed 22 | BadKeyImages, 23 | // This error occurs when the calculated challenge is different from the challenge in the signature 24 | ChallengeMismatch, 25 | // This error occurs when an underlying error from the member package occurs 26 | MemberError(String), 27 | } 28 | 29 | impl From for Error { 30 | fn from(e: crate::member::Error) -> Error { 31 | let err_string = format!(" underlying member error {:?}", e); 32 | Error::MemberError(err_string) 33 | } 34 | } 35 | 36 | impl Signature { 37 | pub fn verify( 38 | &self, 39 | compressed_pub_keys: &mut Vec>, 40 | msg: &[u8], 41 | ) -> Result<(), Error> { 42 | // Skip subgroup check as ristretto points have co-factor 1. 43 | 44 | // -- Check that we have the correct amount of responses 45 | // Since `number of public keys = number of users * number of keys per user` 46 | // And `number of responses = number of users * number of keys per user` 47 | // `number of responses = number of total public keys` 48 | // `number of key images = number of keys for the signer` 49 | // This is equal to the number of keys per user because all members have the same 50 | // amount of keys. 51 | // We can then calculate `number of users = number of responses / number of key images` 52 | let num_key_images = self.key_images.len(); 53 | let num_responses = self.responses.len(); 54 | if num_responses % num_key_images != 0 { 55 | return Err(Error::IncorrectNumOfResponses); 56 | } 57 | 58 | // -- Check that we have the correct amount of public keys 59 | if compressed_pub_keys.len() != (num_responses / num_key_images) { 60 | return Err(Error::IncorrectNumOfPubKeys); 61 | } 62 | 63 | // Decompress Public keys 64 | let mut decompressed_keys: Vec> = Vec::new(); 65 | for compressed_key_set in compressed_pub_keys { 66 | decompressed_keys.push(self.decompress_keys(&compressed_key_set)) 67 | } 68 | let chunked_responses: Vec<_> = self.responses.chunks(num_key_images).collect(); 69 | 70 | let decomp_key_images = self.decompress_key_images()?; 71 | let mut challenge = self.challenge.clone(); 72 | for (pub_keys, responses) in decompressed_keys.iter().zip(chunked_responses.iter()) { 73 | challenge = 74 | compute_challenge_ring(pub_keys, &challenge, &decomp_key_images, responses, msg); 75 | } 76 | 77 | if self.challenge != challenge { 78 | return Err(Error::ChallengeMismatch); 79 | } 80 | 81 | Ok(()) 82 | } 83 | 84 | fn decompress_key_images(&self) -> Result, Error> { 85 | let mut decompressed_key_images = Vec::with_capacity(self.key_images.len()); 86 | for key_image in self.key_images.iter() { 87 | let dec_key_image = key_image.decompress().ok_or(Error::BadKeyImages)?; 88 | decompressed_key_images.push(dec_key_image); 89 | } 90 | Ok(decompressed_key_images) 91 | } 92 | 93 | fn decompress_keys(&self, compressed_keys: &Vec) -> Vec { 94 | let mut decompressed = Vec::with_capacity(compressed_keys.len()); 95 | for key in compressed_keys { 96 | decompressed.push(key.decompress().unwrap()); 97 | } 98 | decompressed 99 | } 100 | } 101 | 102 | #[cfg(test)] 103 | mod test { 104 | extern crate test; 105 | use crate::tests_helper::*; 106 | use test::Bencher; 107 | 108 | use crate::constants; 109 | 110 | use rand::seq::SliceRandom; 111 | use rand::thread_rng; 112 | 113 | #[test] 114 | fn test_verify_fail_shuffle_keys() { 115 | let num_keys = 2; 116 | let num_decoys = 11; 117 | let msg = b"hello world"; 118 | 119 | let mut mlsag = generate_mlsag_with(num_decoys, num_keys); 120 | mlsag.add_member(generate_signer(num_keys)); 121 | let sig = mlsag.sign(msg).unwrap(); 122 | let mut pub_keys = mlsag.public_keys(); 123 | 124 | // shuffle public key ordering 125 | pub_keys.shuffle(&mut thread_rng()); 126 | assert!(sig.verify(&mut pub_keys, msg).is_err()); 127 | } 128 | #[test] 129 | fn test_verify_fail_incorrect_num_keys() { 130 | let num_keys = 2; 131 | let num_decoys = 11; 132 | let msg = b"hello world"; 133 | 134 | let mut mlsag = generate_mlsag_with(num_decoys, num_keys); 135 | mlsag.add_member(generate_signer(num_keys)); 136 | let sig = mlsag.sign(msg).unwrap(); 137 | let mut pub_keys = mlsag.public_keys(); 138 | 139 | // Add extra key 140 | pub_keys.push(vec![constants::BASEPOINT.compress()]); 141 | assert!(sig.verify(&mut pub_keys, msg).is_err()); 142 | 143 | // remove the extra key and test should pass 144 | pub_keys.remove(pub_keys.len() - 1); 145 | assert!(sig.verify(&mut pub_keys, msg).is_ok()); 146 | 147 | // remove another key and tests should fail 148 | pub_keys.remove(pub_keys.len() - 1); 149 | assert!(sig.verify(&mut pub_keys, msg).is_err()); 150 | } 151 | 152 | #[bench] 153 | fn bench_verify(b: &mut Bencher) { 154 | // One time setup code here 155 | let num_keys = 2; 156 | let num_decoys = 11; 157 | let msg = b"hello world"; 158 | 159 | let mut mlsag = generate_mlsag_with(num_decoys, num_keys); 160 | mlsag.add_member(generate_signer(num_keys)); 161 | let sig = mlsag.sign(msg).unwrap(); 162 | let mut pub_keys = mlsag.public_keys(); 163 | 164 | b.iter(|| sig.verify(&mut pub_keys, msg)); 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /src/tests_helper.rs: -------------------------------------------------------------------------------- 1 | // helper functions for tests 2 | use crate::keys::PrivateSet; 3 | use crate::member::Member; 4 | use crate::mlsag::Mlsag; 5 | use curve25519_dalek::ristretto::RistrettoPoint; 6 | use curve25519_dalek::scalar::Scalar; 7 | 8 | // There is an exact copy of this function in member.rs 9 | // which is being used to generate nonces. The reason is because the code in this 10 | // file should only be used for test 11 | pub fn generate_rand_scalars(num: usize) -> Vec { 12 | let mut rng = rand::thread_rng(); 13 | let mut scalars = Vec::::with_capacity(num); 14 | 15 | for _ in 0..num { 16 | scalars.push(Scalar::random(&mut rng)); 17 | } 18 | 19 | scalars 20 | } 21 | 22 | pub fn generate_private_set(num: usize) -> PrivateSet { 23 | let scalars = generate_rand_scalars(num); 24 | PrivateSet(scalars) 25 | } 26 | 27 | pub fn generate_rand_points(num: usize) -> Vec { 28 | let mut rng = rand::thread_rng(); 29 | let mut points = Vec::::with_capacity(num); 30 | 31 | for _ in 0..num { 32 | points.push(RistrettoPoint::random(&mut rng)); 33 | } 34 | 35 | points 36 | } 37 | 38 | pub fn generate_decoy(num_keys: usize) -> Member { 39 | let points = generate_rand_points(num_keys); 40 | Member::new_decoy(points) 41 | } 42 | 43 | pub fn generate_decoys(num_decoys: usize, num_keys: usize) -> Vec { 44 | let mut decoys: Vec = Vec::with_capacity(num_decoys); 45 | for _ in 0..num_decoys { 46 | decoys.push(generate_decoy(num_keys)); 47 | } 48 | decoys 49 | } 50 | 51 | pub fn generate_signer(num_keys: usize) -> Member { 52 | let scalars = generate_rand_scalars(num_keys); 53 | Member::new_signer(scalars) 54 | } 55 | 56 | pub fn generate_mlsag_with(num_decoys: usize, num_keys: usize) -> Mlsag { 57 | let mut mlsag = Mlsag::new(); 58 | 59 | for _ in 0..num_decoys { 60 | mlsag.add_member(generate_decoy(num_keys)); 61 | } 62 | 63 | mlsag 64 | } 65 | -------------------------------------------------------------------------------- /src/transcript.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::ristretto::RistrettoPoint; 2 | use curve25519_dalek::scalar::Scalar; 3 | use merlin::Transcript; 4 | use sha2::Sha512; 5 | 6 | /// Extension trait to the Merlin transcript API that allows committing scalars and points and 7 | /// generating challenges as scalars. 8 | pub trait TranscriptProtocol { 9 | /// Appends a `point` with a given label 10 | fn append_point(&mut self, label: &'static [u8], point: &RistrettoPoint); 11 | 12 | /// Appends `scalar * point` with a given label 13 | fn append_scalar_mult(&mut self, label: &'static [u8], scalar: &Scalar, point: &RistrettoPoint); 14 | 15 | /// Appends `scalar * hash_to_point(point)` with a given label 16 | fn append_scalar_hash_point( 17 | &mut self, 18 | label: &'static [u8], 19 | scalar: &Scalar, 20 | point: &RistrettoPoint, 21 | ); 22 | 23 | /// Given (s_1, s_2) and (P_1, P_2) 24 | /// Appends s_1 * P_1 + s_2 * P_2 with a given label 25 | fn append_double_scalar_mult_add( 26 | &mut self, 27 | label: &'static [u8], 28 | scalars: (&Scalar, &Scalar), 29 | points: (&RistrettoPoint, &RistrettoPoint), 30 | ); 31 | 32 | /// Compute a `label`ed challenge variable. 33 | fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar; 34 | } 35 | 36 | impl TranscriptProtocol for Transcript { 37 | fn append_point(&mut self, label: &'static [u8], point: &RistrettoPoint) { 38 | self.append_message(label, point.compress().as_bytes()); 39 | } 40 | 41 | fn append_scalar_mult( 42 | &mut self, 43 | label: &'static [u8], 44 | scalar: &Scalar, 45 | point: &RistrettoPoint, 46 | ) { 47 | let new_point = scalar * point; 48 | self.append_message(label, new_point.compress().as_bytes()); 49 | } 50 | 51 | fn append_scalar_hash_point( 52 | &mut self, 53 | label: &'static [u8], 54 | scalar: &Scalar, 55 | point: &RistrettoPoint, 56 | ) { 57 | let hashed_point = RistrettoPoint::hash_from_bytes::(point.compress().as_bytes()); 58 | let scalar_hashed_point = scalar * hashed_point; 59 | self.append_message(label, scalar_hashed_point.compress().as_bytes()); 60 | } 61 | 62 | fn append_double_scalar_mult_add( 63 | &mut self, 64 | label: &'static [u8], 65 | scalars: (&Scalar, &Scalar), 66 | points: (&RistrettoPoint, &RistrettoPoint), 67 | ) { 68 | let left_scalar_mult = scalars.0 * points.0; 69 | let right_scalar_mult = scalars.1 * points.1; 70 | 71 | let new_point = left_scalar_mult + right_scalar_mult; 72 | self.append_message(label, new_point.compress().as_bytes()); 73 | } 74 | 75 | fn challenge_scalar(&mut self, label: &'static [u8]) -> Scalar { 76 | let mut buf = [0u8; 64]; 77 | self.challenge_bytes(label, &mut buf); 78 | 79 | Scalar::from_bytes_mod_order_wide(&buf) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /tests/protocol.rs: -------------------------------------------------------------------------------- 1 | extern crate curve25519_dalek; 2 | extern crate mlsag; 3 | 4 | use mlsag::mlsag::Mlsag; 5 | use mlsag::tests_helper::*; 6 | 7 | #[test] 8 | fn test_protocol() { 9 | // Define setup parameters 10 | let num_keys = 2; 11 | let num_decoys = 11; 12 | let msg = b"hello world"; 13 | 14 | // Define a mlsag object which will be used to create a signature 15 | let mut mlsag = Mlsag::new(); 16 | 17 | // Generate and add decoys 18 | let decoys = generate_decoys(num_decoys, num_keys); 19 | for decoy in decoys { 20 | mlsag.add_member(decoy); 21 | } 22 | 23 | // Generate and add signer 24 | let signer = generate_signer(num_keys); 25 | mlsag.add_member(signer); 26 | 27 | let signature = mlsag.sign(msg).unwrap(); 28 | let res = signature.verify(&mut mlsag.public_keys(), msg); 29 | 30 | assert!(res.is_ok()) 31 | } 32 | --------------------------------------------------------------------------------