blsful 4.0.0

BLS signature implementation according to the IETF spec on the BLS12-381 curve.
Documentation
use blsful::*;
use rand::{Rng, SeedableRng, TryRng, rand_core::Infallible};
use rstest::*;
use serde::{Deserialize, Serialize, de::DeserializeOwned};

const TEST_MSG: &[u8] = b"signatures_work";

struct MockRng(rand_xorshift::XorShiftRng);

impl SeedableRng for MockRng {
    type Seed = [u8; 16];

    fn from_seed(seed: Self::Seed) -> Self {
        Self(rand_xorshift::XorShiftRng::from_seed(seed))
    }
}

impl rand::TryCryptoRng for MockRng {}

impl TryRng for MockRng {
    type Error = Infallible;

    fn try_next_u32(&mut self) -> Result<u32, Self::Error> {
        Ok(self.0.next_u32())
    }

    fn try_next_u64(&mut self) -> Result<u64, Self::Error> {
        Ok(self.0.next_u64())
    }

    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> {
        self.0.fill_bytes(dest);
        Ok(())
    }
}

impl Default for MockRng {
    fn default() -> Self {
        Self(rand_xorshift::XorShiftRng::from_seed([7u8; 16]))
    }
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
struct Document<T> {
    value: T,
}

fn assert_required_format_roundtrips<T>(value: &T)
where
    T: Serialize + DeserializeOwned + PartialEq + std::fmt::Debug + 'static,
{
    let postcard = postcard::to_allocvec(value).expect("postcard serialization succeeds");
    let postcard_value =
        postcard::from_bytes::<T>(&postcard).expect("postcard deserialization succeeds");
    assert_eq!(value, &postcard_value);

    let mut cbor = Vec::new();
    ciborium::into_writer(value, &mut cbor).expect("CBOR serialization succeeds");
    let cbor_value =
        ciborium::from_reader::<T, _>(cbor.as_slice()).expect("CBOR deserialization succeeds");
    assert_eq!(value, &cbor_value);

    let json = serde_json::to_vec(value).expect("JSON serialization succeeds");
    let json_value = serde_json::from_slice::<T>(&json).expect("JSON deserialization succeeds");
    assert_eq!(value, &json_value);

    let toml = toml::to_string(&Document { value }).expect("TOML serialization succeeds");
    let toml_value = toml::from_str::<Document<T>>(&toml).expect("TOML deserialization succeeds");
    assert_eq!(value, &toml_value.value);

    let yaml = noyalib::to_string(value).expect("YAML serialization succeeds");
    let yaml_value = noyalib::from_str::<T>(&yaml).expect("YAML deserialization succeeds");
    assert_eq!(value, &yaml_value);
}

#[rstest]
#[case::g1(Bls12381G1Impl)]
#[case::g2(Bls12381G2Impl)]
fn basic_types_serialize_required_formats<
    C: BlsSignatureImpl + PartialEq + Eq + std::fmt::Debug + 'static,
>(
    #[case] _c: C,
) {
    let sk = SecretKey::<C>::random(MockRng::default());
    let pk = sk.public_key();
    let sig = sk
        .sign(SignatureSchemes::ProofOfPossession, TEST_MSG)
        .unwrap();

    assert_required_format_roundtrips(&sk);
    assert_required_format_roundtrips(&pk);
    assert_required_format_roundtrips(&sig);
}

#[rstest]
#[case::g1(Bls12381G1Impl)]
#[case::g2(Bls12381G2Impl)]
fn basic_types_serialize_json<C: BlsSignatureImpl + PartialEq + Eq + std::fmt::Debug>(
    #[case] _c: C,
) {
    let sk = SecretKey::<C>::random(MockRng::default());
    let pk = sk.public_key();
    let sig_b = sk.sign(SignatureSchemes::Basic, TEST_MSG).unwrap();
    let sig_ma = sk
        .sign(SignatureSchemes::MessageAugmentation, TEST_MSG)
        .unwrap();
    let sig_pop = sk
        .sign(SignatureSchemes::ProofOfPossession, TEST_MSG)
        .unwrap();

    let res = serde_json::to_vec(&sk);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_json::from_slice::<SecretKey<C>>(&text);
    assert!(res.is_ok());
    let sk2 = res.unwrap();
    assert_eq!(sk, sk2);

    let res = serde_json::to_vec(&pk);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_json::from_slice::<PublicKey<C>>(&text);
    assert!(res.is_ok());
    let pk2 = res.unwrap();
    assert_eq!(pk, pk2);

    let res = serde_json::to_vec(&sig_b);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_json::from_slice::<Signature<C>>(&text);
    assert!(res.is_ok());
    let sig_b2 = res.unwrap();
    assert_eq!(sig_b, sig_b2);

    let res = serde_json::to_vec(&sig_ma);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_json::from_slice::<Signature<C>>(&text);
    assert!(res.is_ok());
    let sig_ma2 = res.unwrap();
    assert_eq!(sig_ma, sig_ma2);

    let res = serde_json::to_vec(&sig_pop);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_json::from_slice::<Signature<C>>(&text);
    assert!(res.is_ok());
    let sig_pop2 = res.unwrap();
    assert_eq!(sig_pop, sig_pop2);
}

#[rstest]
#[case::g1(Bls12381G1Impl)]
#[case::g2(Bls12381G2Impl)]
fn basic_types_serialize_binary<C: BlsSignatureImpl + PartialEq + Eq + std::fmt::Debug>(
    #[case] _c: C,
) {
    let sk = SecretKey::<C>::random(MockRng::default());
    let pk = sk.public_key();
    let sig_b = sk.sign(SignatureSchemes::Basic, TEST_MSG).unwrap();
    let sig_ma = sk
        .sign(SignatureSchemes::MessageAugmentation, TEST_MSG)
        .unwrap();
    let sig_pop = sk
        .sign(SignatureSchemes::ProofOfPossession, TEST_MSG)
        .unwrap();

    let res = serde_bare::to_vec(&sk);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_bare::from_slice::<SecretKey<C>>(&text);
    assert!(res.is_ok());
    let sk2 = res.unwrap();
    assert_eq!(sk, sk2);

    let res = serde_bare::to_vec(&pk);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_bare::from_slice::<PublicKey<C>>(&text);
    assert!(res.is_ok());
    let pk2 = res.unwrap();
    assert_eq!(pk, pk2);

    let res = serde_bare::to_vec(&sig_b);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_bare::from_slice::<Signature<C>>(&text);
    assert!(res.is_ok());
    let sig_b2 = res.unwrap();
    assert_eq!(sig_b, sig_b2);

    let res = serde_bare::to_vec(&sig_ma);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_bare::from_slice::<Signature<C>>(&text);
    assert!(res.is_ok());
    let sig_ma2 = res.unwrap();
    assert_eq!(sig_ma, sig_ma2);

    let res = serde_bare::to_vec(&sig_pop);
    assert!(res.is_ok());
    let text = res.unwrap();
    let res = serde_bare::from_slice::<Signature<C>>(&text);
    assert!(res.is_ok());
    let sig_pop2 = res.unwrap();
    assert_eq!(sig_pop, sig_pop2);
}

#[rstest]
#[case::g1(Bls12381G1Impl)]
#[case::g2(Bls12381G2Impl)]
fn shares_serialize<
    C: BlsSignatureImpl
        + PartialEq
        + Eq
        + std::fmt::Debug
        + serde::Serialize
        + serde::de::DeserializeOwned
        + 'static,
>(
    #[case] _c: C,
) {
    let sk = SecretKey::<C>::from_hash(b"shares_serialize_json");
    // High number to test for fuzzing
    let sk_shares = sk.split(10, 20).unwrap();
    for share in &sk_shares {
        assert_required_format_roundtrips(share);

        let text = serde_json::to_vec(&share).unwrap_or_else(|e| panic!("{e:?}"));
        let share2 =
            serde_json::from_slice::<SecretKeyShare<C>>(&text).unwrap_or_else(|e| panic!("{e:?}"));
        assert_eq!(share, &share2);

        let text = serde_bare::to_vec(&share).unwrap_or_else(|e| panic!("{e:?}"));
        let share2 =
            serde_bare::from_slice::<SecretKeyShare<C>>(&text).unwrap_or_else(|e| panic!("{e:?}"));
        assert_eq!(share, &share2);

        let pks = share.public_key().unwrap();
        assert_required_format_roundtrips(&pks);
        let text = serde_json::to_vec(&pks).unwrap_or_else(|e| panic!("{e:?}"));
        let pks2 =
            serde_json::from_slice::<PublicKeyShare<C>>(&text).unwrap_or_else(|e| panic!("{e:?}"));
        assert_eq!(pks, pks2);

        let sgs = share
            .sign(SignatureSchemes::ProofOfPossession, TEST_MSG)
            .unwrap();
        assert_required_format_roundtrips(&sgs);
        let res = serde_json::to_vec(&sgs);
        assert!(res.is_ok());
        let text = res.unwrap();
        let res = serde_json::from_slice::<SignatureShare<C>>(&text);
        assert!(res.is_ok());
        let sgs2 = res.unwrap();
        assert_eq!(sgs, sgs2);
    }
}

#[test]
fn shares_serialize_test() {
    let sk = SecretKey::<Bls12381G1Impl>::from_hash(b"shares_serialize_json");
    // High number to test for fuzzing
    let sk_shares = sk.split(10, 20).unwrap();
    for share in &sk_shares {
        let text = serde_json::to_vec(&share).unwrap_or_else(|e| panic!("{e:?}"));
        let share2 = serde_json::from_slice::<SecretKeyShare<Bls12381G1Impl>>(&text)
            .unwrap_or_else(|e| panic!("{e:?}"));
        assert_eq!(share, &share2);

        let pks = share.public_key().unwrap();
        let text = serde_json::to_vec(&pks).unwrap_or_else(|e| panic!("{e:?}"));
        let pks2 = serde_json::from_slice::<PublicKeyShare<Bls12381G1Impl>>(&text)
            .unwrap_or_else(|e| panic!("{e:?}"));
        assert_eq!(pks, pks2);

        let sgs = share
            .sign(SignatureSchemes::ProofOfPossession, TEST_MSG)
            .unwrap();
        let res = serde_json::to_vec(&sgs);
        assert!(res.is_ok());
        let text = res.unwrap();
        let res = serde_json::from_slice::<SignatureShare<Bls12381G1Impl>>(&text);
        assert!(res.is_ok());
        let sgs2 = res.unwrap();
        assert_eq!(sgs, sgs2);
    }
}