From d42820163830f4e09db2e68e78628875794fc257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eduardo=20Leegwater=20Sim=C3=B5es?= Date: Wed, 31 Jul 2024 13:47:08 +0200 Subject: [PATCH] Change to better distinguish signature schemes It is currently possible to call `APK::verify` or `PublicKey::verify` on the same signature type, leading to situations where even if a user is technically *allowed* to call the function, verification is in principle impossible, since the signature schemes used to produce a signature are different. Furthermore, the `sign_vulnerable` function is arguably misnamed, since it is only actually vulnerable to a rogue key attack *when used in a multi-signature context*. In the interest of fixing this, this commit introduces a couple of changes to the API of the crate, designed to better distinguish and clarify the usage of the two different signature schemes it provides. First and foremost, a new signature type - `MultisigSignature` is added, representing a signature produced using the multi-signature scheme. Conversely, the already existing `Signature` type is taken to mean the a signature produced using the single-key scheme. The functions under `SecretKey`, `sign_vulnerable` and `sign`, are respectively renamed into `sign` and `sign_multisig`, and the latter is changed to return the new `MultisigSignature` type. The "aggregated public key" type is renamed from `APK` into `MultisigPublicKey`, to better denote its use and to be in line with the kind signature it can now verify using the `verify` function - which is changed to take a `MultisigSignature`. On a more subtle note, the `From<&PublicKey>` and `From<&SecretKey>` implementations for `MultisigPublicKey`, formerly `APK` are removed and the API of `MultisigPublicKey::aggregate` is changed to *not* take `&self`, and only take a slice of `PublicKey`. This is designed to promote the intended usage of the struct - aggregating a collection of public keys, as opposed to just "aggregating" one. Resolves: #18 --- CHANGELOG.md | 18 ++++ Cargo.toml | 2 +- benches/signature_bench.rs | 48 +++------ src/error.rs | 5 + src/keys.rs | 1 - src/keys/apk.rs | 127 ------------------------ src/keys/public.rs | 149 ++++++++++++++++++++++++---- src/keys/secret.rs | 20 ++-- src/lib.rs | 22 ++-- src/{signature.rs => signatures.rs} | 55 ++++++++-- tests/keys.rs | 29 +++--- tests/signature.rs | 102 +++++++++---------- 12 files changed, 303 insertions(+), 275 deletions(-) delete mode 100644 src/keys/apk.rs rename src/{signature.rs => signatures.rs} (51%) diff --git a/CHANGELOG.md b/CHANGELOG.md index e956040..38b06a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Add `MultisigSignature` struct [#18] +- Add `Error::NoKeysProvided` variant [#18] + +### Changed + +- Rename `SecretKey::sign` to `sign_multisig` and change return to `MultisigSignature` [#18] +- Rename `SecretKey::sign_vulnerable` to `sign` [#18] +- Rename `APK` to `MultisigPublicKey` [#18] +- Change `APK::verify` to take a `MultisigSignature` [#18] + +### Removed + +- Remove `impl From<&PublicKey> for APK` [#18] +- Remove `impl From<&SecretKey> for APK` [#18] + ## [0.3.1] - 2024-06-27 ### Changed @@ -48,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Add initial commit, this package continues the development of [dusk-bls12_381-sign](https://github.com/dusk-network/bls12_381-sign/) at version `0.6.0` under the new name: `bls12_381-bls` and without the go related code. +[#18]: https://github.com/dusk-network/bls12_381-bls/issues/18 [#8]: https://github.com/dusk-network/bls12_381-bls/issues/8 [#7]: https://github.com/dusk-network/bls12_381-bls/issues/7 [#5]: https://github.com/dusk-network/bls12_381-bls/issues/5 diff --git a/Cargo.toml b/Cargo.toml index 58965b1..0429e5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,7 +25,7 @@ bytecheck = { version = "0.6", optional = true, default-features = false } rayon = { version = "1.8", optional = true } [dev-dependencies] -rand = { version = "0.8", default-features = false, features = ["std_rng"] } +rand = "0.8" [features] rkyv-impl = [ diff --git a/benches/signature_bench.rs b/benches/signature_bench.rs index 5177b13..5f9fdda 100644 --- a/benches/signature_bench.rs +++ b/benches/signature_bench.rs @@ -9,24 +9,25 @@ extern crate test; mod benches { - use bls12_381_bls::{PublicKey, SecretKey, APK}; + use bls12_381_bls::{MultisigPublicKey, PublicKey, SecretKey}; use dusk_bytes::Serializable; - use rand_core::{OsRng, RngCore}; + use rand::rngs::OsRng; + use rand::RngCore; use test::Bencher; #[bench] - fn bench_sign_vulnerable(b: &mut Bencher) { + fn bench_sign(b: &mut Bencher) { let sk = SecretKey::random(&mut OsRng); let msg = random_message(); - b.iter(|| sk.sign_vulnerable(&msg)); + b.iter(|| sk.sign(&msg)); } #[bench] - fn bench_sign(b: &mut Bencher) { + fn bench_multisig_sign(b: &mut Bencher) { let sk = SecretKey::random(&mut OsRng); let pk = PublicKey::from(&sk); let msg = random_message(); - b.iter(|| sk.sign(&pk, &msg)); + b.iter(|| sk.sign_multisig(&pk, &msg)); } #[bench] @@ -34,54 +35,35 @@ mod benches { let sk = SecretKey::random(&mut OsRng); let pk = PublicKey::from(&sk); let msg = random_message(); - let sig = sk.sign_vulnerable(&msg); + let sig = sk.sign(&msg); b.iter(|| pk.verify(&sig, &msg)); } #[bench] - fn bench_aggregate_sig(b: &mut Bencher) { + fn bench_multisig_aggregate_sig(b: &mut Bencher) { let sk = SecretKey::random(&mut OsRng); + let pk = PublicKey::from(&sk); let msg = random_message(); - let sig = sk.sign_vulnerable(&msg); + let sig = sk.sign_multisig(&pk, &msg); b.iter(|| sig.aggregate(&[sig])); } #[bench] - fn bench_aggregate_pk(b: &mut Bencher) { - let sk = SecretKey::random(&mut OsRng); - let pk = PublicKey::from(&sk); - let mut apk = APK::from(&pk); - b.iter(|| apk.aggregate(&[pk])); - } - - #[bench] - fn bench_aggregate_pk_64_bulk(b: &mut Bencher) { + fn bench_multisig_aggregate_pk(b: &mut Bencher) { let sk = SecretKey::random(&mut OsRng); let pk = PublicKey::from(&sk); - let mut apk = APK::from(&pk); - let mut pks = vec![]; - for _ in 0..64 { - let sk = SecretKey::random(&mut OsRng); - let pk = PublicKey::from(&sk); - pks.push(pk) - } - let pks = &pks[..]; - b.iter(|| apk.aggregate(pks)); + b.iter(|| MultisigPublicKey::aggregate(&[pk])); } #[bench] - fn bench_aggregate_pk_64_each(b: &mut Bencher) { - let sk = SecretKey::random(&mut OsRng); - let pk = PublicKey::from(&sk); - let mut apk = APK::from(&pk); + fn bench_multisig_aggregate_pk_64_bulk(b: &mut Bencher) { let mut pks = vec![]; for _ in 0..64 { let sk = SecretKey::random(&mut OsRng); let pk = PublicKey::from(&sk); pks.push(pk) } - let pks = &pks[..]; - b.iter(|| pks.iter().for_each(|&p| apk.aggregate(&[p]))); + b.iter(|| MultisigPublicKey::aggregate(&pks[..])); } fn random_message() -> [u8; 100] { diff --git a/src/error.rs b/src/error.rs index 73bf55d..4a61b7e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,6 +17,8 @@ pub enum Error { InvalidSignature, /// Invalid Point InvalidPoint, + /// Tried to aggregate an empty list of public keys + NoKeysProvided, } impl From for Error { @@ -35,6 +37,9 @@ impl fmt::Display for Error { Self::InvalidPoint => { write!(f, "Invalid Point") } + Self::NoKeysProvided => { + write!(f, "No keys provided") + } } } } diff --git a/src/keys.rs b/src/keys.rs index c5e1f82..3fac4a0 100644 --- a/src/keys.rs +++ b/src/keys.rs @@ -4,6 +4,5 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -pub mod apk; pub mod public; pub mod secret; diff --git a/src/keys/apk.rs b/src/keys/apk.rs deleted file mode 100644 index 453aea9..0000000 --- a/src/keys/apk.rs +++ /dev/null @@ -1,127 +0,0 @@ -// This Source Code Form is subject to the terms of the Mozilla Public -// License, v. 2.0. If a copy of the MPL was not distributed with this -// file, You can obtain one at http://mozilla.org/MPL/2.0/. -// -// Copyright (c) DUSK NETWORK. All rights reserved. - -use crate::{hash::h1, Signature}; -use crate::{Error, PublicKey, SecretKey}; - -use dusk_bls12_381::G2Projective; -use dusk_bytes::{Error as DuskBytesError, Serializable}; - -#[cfg(feature = "rkyv-impl")] -use rkyv::{Archive, Deserialize, Serialize}; - -#[cfg(feature = "parallel")] -use rayon::prelude::*; - -/// Aggregated form of a BLS public key. -/// The public keys are aggregated in a rogue-key attack -/// resistant manner, by using the hash function defined -/// in the modified version of BLS. -#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] -#[cfg_attr( - feature = "rkyv-impl", - derive(Archive, Deserialize, Serialize), - archive_attr(derive(bytecheck::CheckBytes)) -)] -pub struct APK(PublicKey); - -impl Serializable<96> for APK { - type Error = DuskBytesError; - - fn to_bytes(&self) -> [u8; Self::SIZE] { - self.0.to_bytes() - } - - fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result { - Ok(APK(PublicKey::from_bytes(bytes)?)) - } -} - -impl From<&PublicKey> for APK { - fn from(pk: &PublicKey) -> Self { - let t = h1(pk); - let gx = pk.0 * t; - Self(PublicKey(gx.into())) - } -} - -impl From<&SecretKey> for APK { - fn from(sk: &SecretKey) -> Self { - let pk = PublicKey::from(sk); - - Self::from(&pk) - } -} - -impl APK { - /// Aggregate a set of [`PublicKey`] into the [`APK`]. - /// - /// # Errors - /// - /// The aggregation errors when one of the [`PublicKey`]s is made of the - /// identity or an otherwise invalid point. - pub fn aggregate(&mut self, pks: &[PublicKey]) -> Result<(), Error> { - #[cfg(not(feature = "parallel"))] - let valid_iter = pks.iter(); - #[cfg(feature = "parallel")] - let valid_iter = pks.par_iter(); - - #[cfg(not(feature = "parallel"))] - let pks_valid = - valid_iter.fold(true, |acc, next| acc & next.is_valid()); - #[cfg(feature = "parallel")] - let pks_valid = valid_iter - .map(PublicKey::is_valid) - .reduce(|| true, |acc, next| acc & next); - - if !(self.0.is_valid() & pks_valid) { - return Err(Error::InvalidPoint); - } - - #[cfg(not(feature = "parallel"))] - let sum_iter = pks.iter(); - #[cfg(feature = "parallel")] - let sum_iter = pks.par_iter(); - - let sum: G2Projective = - sum_iter.map(|pk| G2Projective::from(pk.pk_t())).sum(); - - self.0 .0 = (self.0 .0 + sum).into(); - - Ok(()) - } - - /// Verify a [`Signature`]. - /// Wrapper function for PublicKey.verify. - /// Currently, this function only supports batched signature verification - /// for the same message. Distinct messages are not supported. - pub fn verify(&self, sig: &Signature, msg: &[u8]) -> Result<(), Error> { - self.0.verify(sig, msg) - } - - /// Raw bytes representation - /// - /// The intended usage of this function is for trusted sets of data where - /// performance is critical. - /// - /// For secure serialization, check `to_bytes` - pub fn to_raw_bytes(&self) -> [u8; dusk_bls12_381::G2Affine::RAW_SIZE] { - self.0.to_raw_bytes() - } - - /// Create a `APK` from a set of bytes created by `APK::to_raw_bytes`. - /// - /// # Safety - /// - /// No check is performed and no constant time is granted. The expected - /// usage of this function is for trusted bytes where performance is - /// critical. - /// - /// For secure serialization, check `from_bytes` - pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { - APK(PublicKey::from_slice_unchecked(bytes)) - } -} diff --git a/src/keys/public.rs b/src/keys/public.rs index aaa5f46..48af806 100644 --- a/src/keys/public.rs +++ b/src/keys/public.rs @@ -5,14 +5,18 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::hash::{h0, h1}; -use crate::{Error, SecretKey, Signature}; +use crate::signatures::is_valid as is_valid_sig; +use crate::{Error, MultisigSignature, SecretKey, Signature}; -use dusk_bls12_381::G2Affine; +use dusk_bls12_381::{G1Affine, G2Affine, G2Projective}; use dusk_bytes::{Error as DuskBytesError, Serializable}; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize, Serialize}; +#[cfg(feature = "parallel")] +use rayon::prelude::*; + /// A BLS public key, holding a BLS12-381 G2 element inside. /// The G2 element is constructed by multiplying a [`SecretKey`] /// by `g2` (the base point of the G2 group). @@ -23,7 +27,7 @@ use rkyv::{Archive, Deserialize, Serialize}; derive(Archive, Deserialize, Serialize), archive_attr(derive(bytecheck::CheckBytes)) )] -pub struct PublicKey(pub(crate) G2Affine); +pub struct PublicKey(G2Affine); impl Serializable<96> for PublicKey { type Error = DuskBytesError; @@ -52,18 +56,7 @@ impl PublicKey { /// Verify a [`Signature`] by comparing the results of the two pairing /// operations: e(sig, g_2) == e(Hâ‚’(m), pk). pub fn verify(&self, sig: &Signature, msg: &[u8]) -> Result<(), Error> { - if !self.is_valid() || !sig.is_valid() { - return Err(Error::InvalidPoint); - } - let h0m = h0(msg); - let p1 = dusk_bls12_381::pairing(&sig.0, &G2Affine::generator()); - let p2 = dusk_bls12_381::pairing(&h0m, &self.0); - - if p1.eq(&p2) { - Ok(()) - } else { - Err(Error::InvalidSignature) - } + verify(&self.0, &sig.0, msg) } /// Return pk * t, where t is H_(pk). @@ -106,9 +99,127 @@ impl PublicKey { /// 2. It is on the curve. /// 3. It is not the identity. pub fn is_valid(&self) -> bool { - let is_identity: bool = self.0.is_identity().into(); - self.0.is_torsion_free().into() - && self.0.is_on_curve().into() - && !is_identity + is_valid(&self.0) + } +} + +fn verify(key: &G2Affine, sig: &G1Affine, msg: &[u8]) -> Result<(), Error> { + if !is_valid(key) || !is_valid_sig(sig) { + return Err(Error::InvalidPoint); + } + let h0m = h0(msg); + let p1 = dusk_bls12_381::pairing(sig, &G2Affine::generator()); + let p2 = dusk_bls12_381::pairing(&h0m, key); + + if p1.eq(&p2) { + Ok(()) + } else { + Err(Error::InvalidSignature) + } +} + +fn is_valid(key: &G2Affine) -> bool { + let is_identity: bool = key.is_identity().into(); + key.is_torsion_free().into() && key.is_on_curve().into() && !is_identity +} + +/// Aggregated form of a BLS public key. +/// The public keys are aggregated in a rogue-key attack +/// resistant manner, by using the hash function defined +/// in the modified version of BLS. +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr( + feature = "rkyv-impl", + derive(Archive, Deserialize, Serialize), + archive_attr(derive(bytecheck::CheckBytes)) +)] +pub struct MultisigPublicKey(G2Affine); + +impl Serializable<96> for MultisigPublicKey { + type Error = DuskBytesError; + + fn to_bytes(&self) -> [u8; Self::SIZE] { + self.0.to_bytes() + } + + fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result { + Ok(MultisigPublicKey(G2Affine::from_bytes(bytes)?)) + } +} + +impl MultisigPublicKey { + /// Aggregate a set of [`PublicKey`] into a [`MultisigPublicKey`]. + /// + /// # Errors + /// + /// The aggregation errors when an empty slice is passed, or one of the + /// [`PublicKey`]s is made of the identity or an otherwise invalid point. + pub fn aggregate(pks: &[PublicKey]) -> Result { + if pks.is_empty() { + return Err(Error::NoKeysProvided); + } + + #[cfg(not(feature = "parallel"))] + let valid_iter = pks.iter(); + #[cfg(feature = "parallel")] + let valid_iter = pks.par_iter(); + + #[cfg(not(feature = "parallel"))] + let pks_valid = + valid_iter.fold(true, |acc, next| acc & next.is_valid()); + #[cfg(feature = "parallel")] + let pks_valid = valid_iter + .map(PublicKey::is_valid) + .reduce(|| true, |acc, next| acc & next); + + if !pks_valid { + return Err(Error::InvalidPoint); + } + + #[cfg(not(feature = "parallel"))] + let sum_iter = pks.iter(); + #[cfg(feature = "parallel")] + let sum_iter = pks.par_iter(); + + let sum: G2Projective = + sum_iter.map(|pk| G2Projective::from(pk.pk_t())).sum(); + + Ok(Self(sum.into())) + } + + /// Verify a [`MultisigSignature`]. + /// Wrapper function for PublicKey.verify. + /// Currently, this function only supports batched signature verification + /// for the same message. Distinct messages are not supported. + pub fn verify( + &self, + sig: &MultisigSignature, + msg: &[u8], + ) -> Result<(), Error> { + verify(&self.0, &sig.0, msg) + } + + /// Raw bytes representation + /// + /// The intended usage of this function is for trusted sets of data where + /// performance is critical. + /// + /// For secure serialization, check `to_bytes` + pub fn to_raw_bytes(&self) -> [u8; dusk_bls12_381::G2Affine::RAW_SIZE] { + self.0.to_raw_bytes() + } + + /// Create a `MultisigPublicKey` from a set of bytes created by + /// `MultisigPublicKey::to_raw_bytes`. + /// + /// # Safety + /// + /// No check is performed and no constant time is granted. The expected + /// usage of this function is for trusted bytes where performance is + /// critical. + /// + /// For secure serialization, check `from_bytes` + pub unsafe fn from_slice_unchecked(bytes: &[u8]) -> Self { + MultisigPublicKey(G2Affine::from_slice_unchecked(bytes)) } } diff --git a/src/keys/secret.rs b/src/keys/secret.rs index 1fc3c21..6409beb 100644 --- a/src/keys/secret.rs +++ b/src/keys/secret.rs @@ -5,7 +5,7 @@ // Copyright (c) DUSK NETWORK. All rights reserved. use crate::hash::{h0, h1}; -use crate::{PublicKey, Signature}; +use crate::{MultisigSignature, PublicKey, Signature}; use dusk_bls12_381::BlsScalar; use dusk_bytes::{Error as DuskBytesError, Serializable}; @@ -94,9 +94,8 @@ impl Serializable<32> for SecretKey { } impl SecretKey { - /// Sign a message, producing a [`Signature`]. - /// The signature produced is vulnerable to a rogue-key attack. - pub fn sign_vulnerable(&self, msg: &[u8]) -> Signature { + /// Sign a message, using the single-signature scheme. + pub fn sign(&self, msg: &[u8]) -> Signature { // Hash message let h = h0(msg); @@ -105,14 +104,19 @@ impl SecretKey { Signature(e.into()) } - /// Sign a message in a rogue-key attack resistant way. - pub fn sign(&self, pk: &PublicKey, msg: &[u8]) -> Signature { - let mut sig = self.sign_vulnerable(msg); + /// Sign a message, using the multi-signature scheme. + pub fn sign_multisig( + &self, + pk: &PublicKey, + msg: &[u8], + ) -> MultisigSignature { + let mut sig = self.sign(msg); // Turn signature into its modified construction, // which provides protection against rogue-key attacks. let t = h1(pk); sig.0 = (sig.0 * t).into(); - sig + + MultisigSignature(sig.0) } } diff --git a/src/lib.rs b/src/lib.rs index 0973837..f71e1f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,23 +7,31 @@ #![no_std] //! Implementation of BLS signatures on the BLS12-381 curve. -//! Reference paper: https://crypto.stanford.edu/~dabo/pubs/papers/BLSmultisig.html +//! Reference paper: mod error; mod hash; mod keys; -mod signature; +mod signatures; pub use error::Error; -pub use keys::{apk::APK, public::PublicKey, secret::SecretKey}; -pub use signature::Signature; +pub use keys::{ + public::{MultisigPublicKey, PublicKey}, + secret::SecretKey, +}; +pub use signatures::{MultisigSignature, Signature}; #[cfg(feature = "rkyv-impl")] pub use crate::keys::{ - apk::{APKResolver, ArchivedAPK}, - public::{ArchivedPublicKey, PublicKeyResolver}, + public::{ + ArchivedMultisigPublicKey, ArchivedPublicKey, + MultisigPublicKeyResolver, PublicKeyResolver, + }, secret::{ArchivedSecretKey, SecretKeyResolver}, }; #[cfg(feature = "rkyv-impl")] -pub use crate::signature::{ArchivedSignature, SignatureResolver}; +pub use crate::signatures::{ + ArchivedMultisigSignature, ArchivedSignature, MultisigSignatureResolver, + SignatureResolver, +}; diff --git a/src/signature.rs b/src/signatures.rs similarity index 51% rename from src/signature.rs rename to src/signatures.rs index ee7b3ae..70aabb9 100644 --- a/src/signature.rs +++ b/src/signatures.rs @@ -12,7 +12,7 @@ use dusk_bytes::Serializable; #[cfg(feature = "rkyv-impl")] use rkyv::{Archive, Deserialize, Serialize}; -/// A BLS signature. +/// A BLS signature, in the single-signature scheme. #[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] #[cfg_attr( feature = "rkyv-impl", @@ -22,8 +22,48 @@ use rkyv::{Archive, Deserialize, Serialize}; pub struct Signature(pub(crate) G1Affine); impl Signature { - /// Aggregate a set of signatures by simply adding up the points. - pub fn aggregate(&self, sigs: &[Signature]) -> Self { + /// Returns true if the inner point is valid according to certain criteria. + /// + /// A [`Signature`] is considered valid if its inner point meets the + /// following conditions: + /// 1. It is free of an $h$-torsion component and exists within the + /// $q$-order subgroup $\mathbb{G}_2$. + /// 2. It is on the curve. + /// 3. It is not the identity. + pub fn is_valid(&self) -> bool { + is_valid(&self.0) + } +} + +impl Serializable<48> for Signature { + type Error = Error; + + fn to_bytes(&self) -> [u8; Self::SIZE] { + self.0.to_bytes() + } + + fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result { + Ok(Self(G1Affine::from_bytes(bytes)?)) + } +} + +pub(crate) fn is_valid(sig: &G1Affine) -> bool { + let is_identity: bool = sig.is_identity().into(); + sig.is_torsion_free().into() && sig.is_on_curve().into() && !is_identity +} + +/// A BLS signature, in the multi-signature scheme. +#[derive(Default, Copy, Clone, Debug, Eq, PartialEq)] +#[cfg_attr( + feature = "rkyv-impl", + derive(Archive, Deserialize, Serialize), + archive_attr(derive(bytecheck::CheckBytes)) +)] +pub struct MultisigSignature(pub(crate) G1Affine); + +impl MultisigSignature { + /// Aggregate a set of signatures by adding up the points. + pub fn aggregate(&self, sigs: &[MultisigSignature]) -> Self { Self( sigs.iter().fold(self.0, |acc, sig| { (acc + G1Projective::from(sig.0)).into() @@ -33,21 +73,18 @@ impl Signature { /// Returns true if the inner point is valid according to certain criteria. /// - /// A [`Signature`] is considered valid if its inner point meets the + /// A [`MultisigSignature`] is considered valid if its inner point meets the /// following conditions: /// 1. It is free of an $h$-torsion component and exists within the /// $q$-order subgroup $\mathbb{G}_2$. /// 2. It is on the curve. /// 3. It is not the identity. pub fn is_valid(&self) -> bool { - let is_identity: bool = self.0.is_identity().into(); - self.0.is_torsion_free().into() - && self.0.is_on_curve().into() - && !is_identity + is_valid(&self.0) } } -impl Serializable<48> for Signature { +impl Serializable<48> for MultisigSignature { type Error = Error; fn to_bytes(&self) -> [u8; Self::SIZE] { diff --git a/tests/keys.rs b/tests/keys.rs index d94a701..99ee52a 100644 --- a/tests/keys.rs +++ b/tests/keys.rs @@ -4,7 +4,7 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use bls12_381_bls::{Error, PublicKey, SecretKey, APK}; +use bls12_381_bls::{Error, MultisigPublicKey, PublicKey, SecretKey}; use dusk_bls12_381::BlsScalar; use dusk_bytes::Serializable; use rand::rngs::StdRng; @@ -25,42 +25,37 @@ fn keys_encoding() { let mut rng = StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(&mut rng); let pk = PublicKey::from(&sk); - let apk = APK::from(&pk); + let mspk = MultisigPublicKey::aggregate(&[pk]) + .expect("Aggregating should succeed"); assert_eq!(sk, SecretKey::from_bytes(&sk.to_bytes()).unwrap()); assert_eq!(pk, PublicKey::from_bytes(&pk.to_bytes()).unwrap()); - assert_eq!(apk, APK::from_bytes(&apk.to_bytes()).unwrap()); + assert_eq!( + mspk, + MultisigPublicKey::from_bytes(&mspk.to_bytes()).unwrap() + ); } #[test] fn apk_identity_fails() { let mut rng = StdRng::seed_from_u64(0xba0bab); - let sk = SecretKey::random(&mut rng); - let pk = PublicKey::from(&sk); + let sk1 = SecretKey::random(&mut rng); + let pk1 = PublicKey::from(&sk1); let sk2 = SecretKey::random(&mut rng); let pk2 = PublicKey::from(&sk2); - let sk3 = SecretKey::random(&mut rng); - let pk3 = PublicKey::from(&sk3); let identity = PublicKey::from(&SecretKey::from(BlsScalar::zero())); - let mut apk = APK::from(&pk); assert_eq!( - apk.aggregate(&[identity, pk2, pk3]).unwrap_err(), + MultisigPublicKey::aggregate(&[identity, pk1, pk2]).unwrap_err(), Error::InvalidPoint ); assert_eq!( - apk.aggregate(&[pk2, identity, pk3]).unwrap_err(), + MultisigPublicKey::aggregate(&[pk1, identity, pk2]).unwrap_err(), Error::InvalidPoint ); assert_eq!( - apk.aggregate(&[pk2, pk3, identity]).unwrap_err(), - Error::InvalidPoint - ); - - let mut apk = APK::from(&identity); - assert_eq!( - apk.aggregate(&[pk, pk2, pk3]).unwrap_err(), + MultisigPublicKey::aggregate(&[pk1, pk2, identity]).unwrap_err(), Error::InvalidPoint ); } diff --git a/tests/signature.rs b/tests/signature.rs index 4b09b8f..f56a255 100644 --- a/tests/signature.rs +++ b/tests/signature.rs @@ -4,32 +4,32 @@ // // Copyright (c) DUSK NETWORK. All rights reserved. -use bls12_381_bls::{Error, PublicKey, SecretKey, APK}; +use bls12_381_bls::{Error, MultisigPublicKey, PublicKey, SecretKey}; use dusk_bls12_381::BlsScalar; use rand::rngs::StdRng; use rand::{RngCore, SeedableRng}; #[test] -fn vulnerable_sign_verify() { +fn sign_verify() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let msg = random_message(rng); // Sign and verify. - let sig = sk.sign_vulnerable(&msg); + let sig = sk.sign(&msg); let pk = PublicKey::from(&sk); assert!(pk.verify(&sig, &msg).is_ok()); } #[test] -fn vulnerable_sign_verify_incorrect_message() { +fn sign_verify_incorrect_message() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let msg = random_message(rng); - let sig = sk.sign_vulnerable(&msg); + let sig = sk.sign(&msg); // Verify with a different message. let msg = random_message(rng); @@ -39,13 +39,13 @@ fn vulnerable_sign_verify_incorrect_message() { } #[test] -fn vulnerable_sign_verify_incorrect_pk() { +fn sign_verify_incorrect_pk() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let msg = random_message(rng); - let sig = sk.sign_vulnerable(&msg); + let sig = sk.sign(&msg); // Verify with a different public key. let sk = SecretKey::random(rng); @@ -54,139 +54,135 @@ fn vulnerable_sign_verify_incorrect_pk() { } #[test] -fn sign_verify() { +fn multisig_sign_verify() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); let msg = random_message(rng); - let sig = sk.sign(&pk, &msg); - - // Verification with the standard pk should fail. - assert!(pk.verify(&sig, &msg).is_err()); + let sig = sk.sign_multisig(&pk, &msg); - // Verification with the aggregated version should work. - let apk = APK::from(&pk); - assert!(apk.verify(&sig, &msg).is_ok()); + let ms_pk = MultisigPublicKey::aggregate(&[pk]) + .expect("Aggregation should succeed"); + assert!(ms_pk.verify(&sig, &msg).is_ok()); } #[test] -fn sign_verify_incorrect_message() { +fn multisig_sign_verify_incorrect_message() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); let msg = random_message(rng); - let sig = sk.sign(&pk, &msg); + let sig = sk.sign_multisig(&pk, &msg); // Verification with a different message should fail. - let apk = APK::from(&pk); + let ms_pk = MultisigPublicKey::aggregate(&[pk]) + .expect("Aggregation should succeed"); let msg = random_message(rng); - assert!(apk.verify(&sig, &msg).is_err()); + assert!(ms_pk.verify(&sig, &msg).is_err()); } #[test] -fn sign_verify_incorrect_apk() { +fn multisig_sign_verify_incorrect_apk() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); let msg = random_message(rng); - let sig = sk.sign(&pk, &msg); + let sig = sk.sign_multisig(&pk, &msg); // Verification with another APK should fail. let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); - let apk = APK::from(&pk); - assert!(apk.verify(&sig, &msg).is_err()); + let ms_pk = MultisigPublicKey::aggregate(&[pk]) + .expect("Aggregation should succeed"); + assert!(ms_pk.verify(&sig, &msg).is_err()); } #[test] -fn sign_verify_aggregated() { +fn multisig_sign_verify_aggregated() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); let msg = random_message(rng); - let mut agg_sig = sk.sign(&pk, &msg); - - let mut apk = APK::from(&pk); + let mut ms_sig = sk.sign_multisig(&pk, &msg); - let mut pks = vec![]; + let mut pks = vec![pk]; for _ in 0..10 { let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); - let sig = sk.sign(&pk, &msg); - agg_sig = agg_sig.aggregate(&[sig]); - pks.push(pk) + let sig = sk.sign_multisig(&pk, &msg); + ms_sig = ms_sig.aggregate(&[sig]); + pks.push(pk); } - apk.aggregate(&pks[..]) - .expect("public keys should be valid"); + let ms_pk = + MultisigPublicKey::aggregate(&pks).expect("Aggregation should succeed"); - assert!(apk.verify(&agg_sig, &msg).is_ok()); + assert!(ms_pk.verify(&ms_sig, &msg).is_ok()); } #[test] -fn sign_verify_aggregated_incorrect_message() { +fn multisig_sign_verify_aggregated_incorrect_message() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); let msg = random_message(rng); - let mut agg_sig = sk.sign(&pk, &msg); - - let mut apk = APK::from(&pk); + let mut ms_sig = sk.sign_multisig(&pk, &msg); + let mut pks = vec![pk]; for _ in 0..10 { let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); - let sig = sk.sign(&pk, &msg); - agg_sig = agg_sig.aggregate(&[sig]); - apk.aggregate(&[pk]).expect("public keys should be valid"); + let sig = sk.sign_multisig(&pk, &msg); + ms_sig = ms_sig.aggregate(&[sig]); + pks.push(pk); } + let ms_pk = + MultisigPublicKey::aggregate(&pks).expect("Aggregation should succeed"); // Verification should fail with a different message. let msg = random_message(rng); - assert!(apk.verify(&agg_sig, &msg).is_err()); + assert!(ms_pk.verify(&ms_sig, &msg).is_err()); } #[test] -fn sign_verify_aggregated_incorrect_apk() { +fn multisig_sign_verify_aggregated_incorrect_apk() { let rng = &mut StdRng::seed_from_u64(0xbeef); let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); let msg = random_message(rng); - let mut agg_sig = sk.sign(&pk, &msg); - - let mut apk = APK::from(&pk); + let mut ms_sig = sk.sign_multisig(&pk, &msg); for _ in 0..10 { let sk = SecretKey::random(rng); let pk = PublicKey::from(&sk); - let sig = sk.sign(&pk, &msg); - agg_sig = agg_sig.aggregate(&[sig]); - apk.aggregate(&[pk]).expect("public keys should be valid"); + let sig = sk.sign_multisig(&pk, &msg); + ms_sig = ms_sig.aggregate(&[sig]); } // Verification with the wrong APK should fail. - let apk = APK::from(&pk); - assert!(apk.verify(&agg_sig, &msg).is_err()); + let ms_pk = MultisigPublicKey::aggregate(&[pk]) + .expect("Aggregation should succeed"); + assert!(ms_pk.verify(&ms_sig, &msg).is_err()); } #[test] -fn sign_verify_identity_fails() { +fn multisig_sign_verify_identity_fails() { let rng = &mut StdRng::seed_from_u64(0xbeef); let msg = random_message(rng); let sk = SecretKey::from(BlsScalar::zero()); let pk = PublicKey::from(&sk); - let sig = sk.sign_vulnerable(&msg); + let sig = sk.sign(&msg); assert_eq!(pk.verify(&sig, &msg).unwrap_err(), Error::InvalidPoint); }