#[commonware_macros::stability(ALPHA)]
pub use crate::secp256r1::certificate as secp256r1;
pub use crate::{
bls12381::certificate::{multisig as bls12381_multisig, threshold as bls12381_threshold},
ed25519::certificate as ed25519,
};
use crate::{Digest, PublicKey};
#[cfg(not(feature = "std"))]
use alloc::{collections::BTreeSet, sync::Arc, vec, vec::Vec};
use bytes::{Buf, BufMut, Bytes};
use commonware_codec::{
types::lazy::Lazy, Codec, CodecFixed, EncodeSize, Error, Read, ReadExt, Write,
};
use commonware_parallel::Strategy;
use commonware_utils::{bitmap::BitMap, ordered::Set, Faults, Participant};
use core::{fmt::Debug, hash::Hash};
use rand_core::CryptoRngCore;
#[cfg(feature = "std")]
use std::{collections::BTreeSet, sync::Arc, vec::Vec};
#[derive(Clone, Debug)]
pub struct Attestation<S: Scheme> {
pub signer: Participant,
pub signature: Lazy<S::Signature>,
}
impl<S: Scheme> PartialEq for Attestation<S> {
fn eq(&self, other: &Self) -> bool {
self.signer == other.signer && self.signature == other.signature
}
}
impl<S: Scheme> Eq for Attestation<S> {}
impl<S: Scheme> Hash for Attestation<S> {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.signer.hash(state);
self.signature.hash(state);
}
}
impl<S: Scheme> Write for Attestation<S> {
fn write(&self, writer: &mut impl BufMut) {
self.signer.write(writer);
self.signature.write(writer);
}
}
impl<S: Scheme> EncodeSize for Attestation<S> {
fn encode_size(&self) -> usize {
self.signer.encode_size() + self.signature.encode_size()
}
}
impl<S: Scheme> Read for Attestation<S> {
type Cfg = ();
fn read_cfg(reader: &mut impl Buf, _: &()) -> Result<Self, Error> {
let signer = Participant::read(reader)?;
let signature = ReadExt::read(reader)?;
Ok(Self { signer, signature })
}
}
#[cfg(feature = "arbitrary")]
impl<S: Scheme> arbitrary::Arbitrary<'_> for Attestation<S>
where
S::Signature: for<'a> arbitrary::Arbitrary<'a>,
{
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let signer = Participant::arbitrary(u)?;
let signature = S::Signature::arbitrary(u)?;
Ok(Self {
signer,
signature: signature.into(),
})
}
}
pub struct Verification<S: Scheme> {
pub verified: Vec<Attestation<S>>,
pub invalid: Vec<Participant>,
}
impl<S: Scheme> Verification<S> {
pub const fn new(verified: Vec<Attestation<S>>, invalid: Vec<Participant>) -> Self {
Self { verified, invalid }
}
}
pub trait Namespace: Clone + Send + Sync {
fn derive(namespace: &[u8]) -> Self;
}
impl Namespace for Vec<u8> {
fn derive(namespace: &[u8]) -> Self {
namespace.to_vec()
}
}
pub trait Subject: Clone + Debug + Send + Sync {
type Namespace: Namespace;
fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8];
fn message(&self) -> Bytes;
}
pub trait Scheme: Clone + Debug + Send + Sync + 'static {
type Subject<'a, D: Digest>: Subject;
type PublicKey: PublicKey;
type Signature: Clone + Debug + PartialEq + Eq + Hash + Send + Sync + CodecFixed<Cfg = ()>;
type Certificate: Clone + Debug + PartialEq + Eq + Hash + Send + Sync + Codec;
fn me(&self) -> Option<Participant>;
fn participants(&self) -> &Set<Self::PublicKey>;
fn sign<D: Digest>(&self, subject: Self::Subject<'_, D>) -> Option<Attestation<Self>>;
fn verify_attestation<R, D>(
&self,
rng: &mut R,
subject: Self::Subject<'_, D>,
attestation: &Attestation<Self>,
strategy: &impl Strategy,
) -> bool
where
R: CryptoRngCore,
D: Digest;
fn verify_attestations<R, D, I>(
&self,
rng: &mut R,
subject: Self::Subject<'_, D>,
attestations: I,
strategy: &impl Strategy,
) -> Verification<Self>
where
R: CryptoRngCore,
D: Digest,
I: IntoIterator<Item = Attestation<Self>>,
I::IntoIter: Send,
{
let mut invalid = BTreeSet::new();
let verified = attestations.into_iter().filter_map(|attestation| {
if self.verify_attestation(&mut *rng, subject.clone(), &attestation, strategy) {
Some(attestation)
} else {
invalid.insert(attestation.signer);
None
}
});
Verification::new(verified.collect(), invalid.into_iter().collect())
}
fn assemble<I, M>(
&self,
attestations: I,
strategy: &impl Strategy,
) -> Option<Self::Certificate>
where
I: IntoIterator<Item = Attestation<Self>>,
I::IntoIter: Send,
M: Faults;
fn verify_certificate<R, D, M>(
&self,
rng: &mut R,
subject: Self::Subject<'_, D>,
certificate: &Self::Certificate,
strategy: &impl Strategy,
) -> bool
where
R: CryptoRngCore,
D: Digest,
M: Faults;
fn verify_certificates<'a, R, D, I, M>(
&self,
rng: &mut R,
certificates: I,
strategy: &impl Strategy,
) -> bool
where
R: CryptoRngCore,
D: Digest,
I: Iterator<Item = (Self::Subject<'a, D>, &'a Self::Certificate)>,
M: Faults,
{
for (subject, certificate) in certificates {
if !self.verify_certificate::<_, _, M>(rng, subject, certificate, strategy) {
return false;
}
}
true
}
fn verify_certificates_bisect<'a, R, D, M>(
&self,
rng: &mut R,
certificates: &[(Self::Subject<'a, D>, &'a Self::Certificate)],
strategy: &impl Strategy,
) -> Vec<bool>
where
R: CryptoRngCore,
D: Digest,
Self::Subject<'a, D>: Copy,
Self::Certificate: 'a,
M: Faults,
{
let len = certificates.len();
let mut verified = vec![false; len];
if len == 0 {
return verified;
}
if !Self::is_batchable() {
for (i, (subject, certificate)) in certificates.iter().enumerate() {
verified[i] =
self.verify_certificate::<_, _, M>(rng, *subject, certificate, strategy);
}
return verified;
}
let mut stack = vec![(0, len)];
while let Some((start, end)) = stack.pop() {
if self.verify_certificates::<_, D, _, M>(
rng,
certificates[start..end].iter().copied(),
strategy,
) {
verified[start..end].fill(true);
} else if end - start > 1 {
let mid = start + (end - start) / 2;
stack.push((mid, end));
stack.push((start, mid));
}
}
verified
}
fn is_attributable() -> bool;
fn is_batchable() -> bool;
fn certificate_codec_config(&self) -> <Self::Certificate as Read>::Cfg;
fn certificate_codec_config_unbounded() -> <Self::Certificate as Read>::Cfg;
}
pub trait Provider: Clone + Send + Sync + 'static {
type Scope: Clone + Send + Sync + 'static;
type Scheme: Scheme;
fn scoped(&self, scope: Self::Scope) -> Option<Arc<Self::Scheme>>;
fn all(&self) -> Option<Arc<Self::Scheme>> {
None
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Signers {
bitmap: BitMap<1>,
}
impl Signers {
pub fn from(participants: usize, signers: impl IntoIterator<Item = Participant>) -> Self {
let mut bitmap = BitMap::zeroes(participants as u64);
for signer in signers.into_iter() {
assert!(
!bitmap.get(signer.get() as u64),
"duplicate signer index: {signer}",
);
bitmap.set(signer.get() as u64, true);
}
Self { bitmap }
}
#[allow(clippy::len_without_is_empty)]
pub const fn len(&self) -> usize {
self.bitmap.len() as usize
}
pub fn count(&self) -> usize {
self.bitmap.count_ones() as usize
}
pub fn iter(&self) -> impl Iterator<Item = Participant> + '_ {
self.bitmap
.iter()
.enumerate()
.filter_map(|(index, bit)| bit.then_some(Participant::from_usize(index)))
}
}
impl Write for Signers {
fn write(&self, writer: &mut impl BufMut) {
self.bitmap.write(writer);
}
}
impl EncodeSize for Signers {
fn encode_size(&self) -> usize {
self.bitmap.encode_size()
}
}
impl Read for Signers {
type Cfg = usize;
fn read_cfg(reader: &mut impl Buf, max_participants: &usize) -> Result<Self, Error> {
let bitmap = BitMap::read_cfg(reader, &(*max_participants as u64))?;
Ok(Self { bitmap })
}
}
#[cfg(feature = "arbitrary")]
impl arbitrary::Arbitrary<'_> for Signers {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let participants = u.arbitrary_len::<u8>()? % 10;
let signer_count = u.arbitrary_len::<u8>()?.min(participants);
let signers = (0..signer_count as u32)
.map(Participant::new)
.collect::<Vec<_>>();
Ok(Self::from(participants, signers))
}
}
#[derive(Clone, Debug)]
pub struct ConstantProvider<S: Scheme, Sc = ()> {
scheme: Arc<S>,
_scope: core::marker::PhantomData<Sc>,
}
impl<S: Scheme, Sc> ConstantProvider<S, Sc> {
pub fn new(scheme: S) -> Self {
Self {
scheme: Arc::new(scheme),
_scope: core::marker::PhantomData,
}
}
}
impl<S: Scheme, Sc: Clone + Send + Sync + 'static> crate::certificate::Provider
for ConstantProvider<S, Sc>
{
type Scope = Sc;
type Scheme = S;
fn scoped(&self, _: Sc) -> Option<Arc<S>> {
Some(self.scheme.clone())
}
fn all(&self) -> Option<Arc<Self::Scheme>> {
Some(self.scheme.clone())
}
}
#[cfg(feature = "mocks")]
pub mod mocks;
#[cfg(test)]
mod tests {
use super::*;
use crate::{ed25519::PrivateKey, sha256::Digest as Sha256Digest, Signer as _};
use commonware_codec::{Decode, Encode};
use commonware_math::algebra::Random;
use commonware_parallel::Sequential;
use commonware_utils::{ordered::Set, test_rng, N3f1, TryCollect};
use ed25519_fixture::{Scheme as Ed25519Scheme, TestSubject};
#[test]
fn test_from_signers() {
let signers = Signers::from(6, [0, 3, 5].map(Participant::new));
let collected: Vec<_> = signers.iter().collect();
assert_eq!(
collected,
vec![0, 3, 5]
.into_iter()
.map(Participant::new)
.collect::<Vec<_>>()
);
assert_eq!(signers.count(), 3);
}
#[test]
#[should_panic(expected = "bit 4 out of bounds (len: 4)")]
fn test_from_out_of_bounds() {
Signers::from(4, [0, 4].map(Participant::new));
}
#[test]
#[should_panic(expected = "duplicate signer index: 0")]
fn test_from_duplicate() {
Signers::from(4, [0, 0, 1].map(Participant::new));
}
#[test]
fn test_from_not_increasing() {
Signers::from(4, [2, 1].map(Participant::new));
}
#[test]
fn test_codec_round_trip() {
let signers = Signers::from(9, [1, 6].map(Participant::new));
let encoded = signers.encode();
let decoded = Signers::decode_cfg(encoded, &9).unwrap();
assert_eq!(decoded, signers);
}
#[test]
fn test_decode_respects_participant_limit() {
let signers = Signers::from(8, [0, 3, 7].map(Participant::new));
let encoded = signers.encode();
assert!(Signers::decode_cfg(encoded.clone(), &2).is_err());
assert!(Signers::decode_cfg(encoded.clone(), &8).is_ok());
assert!(Signers::decode_cfg(encoded, &10).is_ok());
}
mod ed25519_fixture {
use crate::{certificate::Subject, impl_certificate_ed25519};
#[derive(Copy, Clone, Debug)]
pub struct TestSubject {
pub message: &'static [u8],
}
impl Subject for TestSubject {
type Namespace = Vec<u8>;
fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] {
derived
}
fn message(&self) -> bytes::Bytes {
bytes::Bytes::from_static(self.message)
}
}
impl_certificate_ed25519!(TestSubject, Vec<u8>);
}
const NAMESPACE: &[u8] = b"test-bisect";
const MESSAGE: &[u8] = b"good message";
const BAD_MESSAGE: &[u8] = b"bad message";
fn make_certificate(
schemes: &[Ed25519Scheme],
message: &'static [u8],
) -> <Ed25519Scheme as Scheme>::Certificate {
let attestations: Vec<_> = schemes
.iter()
.filter_map(|s| s.sign::<Sha256Digest>(TestSubject { message }))
.collect();
schemes[0]
.assemble::<_, N3f1>(attestations, &Sequential)
.expect("assembly failed")
}
fn setup_ed25519(n: u32) -> (Vec<Ed25519Scheme>, Ed25519Scheme) {
let mut rng = test_rng();
let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut rng)).collect();
let participants: Set<crate::ed25519::PublicKey> = private_keys
.iter()
.map(|sk| sk.public_key())
.try_collect()
.unwrap();
let signers: Vec<_> = private_keys
.into_iter()
.map(|sk| Ed25519Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap())
.collect();
let verifier = Ed25519Scheme::verifier(NAMESPACE, participants);
(signers, verifier)
}
#[test]
fn test_bisect_empty() {
let mut rng = test_rng();
let (_, verifier) = setup_ed25519(4);
let result = verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
&mut rng,
&[],
&Sequential,
);
assert!(result.is_empty());
}
#[test]
fn test_bisect_all_valid() {
let mut rng = test_rng();
let (schemes, verifier) = setup_ed25519(4);
let cert = make_certificate(&schemes, MESSAGE);
let good = TestSubject { message: MESSAGE };
let pairs: Vec<_> = (0..5).map(|_| (good, &cert)).collect();
let result = verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
&mut rng,
&pairs,
&Sequential,
);
assert_eq!(result, vec![true; 5]);
}
#[test]
fn test_bisect_mixed() {
let mut rng = test_rng();
let (schemes, verifier) = setup_ed25519(4);
let cert = make_certificate(&schemes, MESSAGE);
let good = TestSubject { message: MESSAGE };
let bad = TestSubject {
message: BAD_MESSAGE,
};
let pairs = vec![
(good, &cert),
(bad, &cert),
(good, &cert),
(bad, &cert),
(good, &cert),
(good, &cert),
(bad, &cert),
(bad, &cert),
];
let expected = vec![true, false, true, false, true, true, false, false];
let result = verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
&mut rng,
&pairs,
&Sequential,
);
assert_eq!(result, expected);
}
#[test]
fn test_bisect_all_invalid() {
let mut rng = test_rng();
let (schemes, verifier) = setup_ed25519(4);
let cert = make_certificate(&schemes, MESSAGE);
let bad = TestSubject {
message: BAD_MESSAGE,
};
let pairs: Vec<_> = (0..4).map(|_| (bad, &cert)).collect();
let result = verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
&mut rng,
&pairs,
&Sequential,
);
assert_eq!(result, vec![false; 4]);
}
#[test]
fn test_bisect_single_valid() {
let mut rng = test_rng();
let (schemes, verifier) = setup_ed25519(4);
let cert = make_certificate(&schemes, MESSAGE);
let pairs = vec![(TestSubject { message: MESSAGE }, &cert)];
let result = verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
&mut rng,
&pairs,
&Sequential,
);
assert_eq!(result, vec![true]);
}
#[test]
fn test_bisect_single_invalid() {
let mut rng = test_rng();
let (schemes, verifier) = setup_ed25519(4);
let cert = make_certificate(&schemes, MESSAGE);
let pairs = vec![(
TestSubject {
message: BAD_MESSAGE,
},
&cert,
)];
let result = verifier.verify_certificates_bisect::<_, Sha256Digest, N3f1>(
&mut rng,
&pairs,
&Sequential,
);
assert_eq!(result, vec![false]);
}
#[cfg(feature = "arbitrary")]
mod conformance {
use super::{ed25519_fixture::Scheme, *};
use commonware_codec::conformance::CodecConformance;
commonware_conformance::conformance_tests! {
CodecConformance<Signers>,
CodecConformance<Attestation<Scheme>>,
}
}
}