use chia_bls::{PublicKey, SecretKey, Signature};
use rand_core::{CryptoRng, RngCore};
use zeroize::Zeroizing;
use crate::error::{KeystoreError, Result};
use crate::scheme::KeyScheme;
#[derive(Debug, Clone, Copy)]
pub struct BlsSigning;
impl KeyScheme for BlsSigning {
type PublicKey = PublicKey;
type Signature = Signature;
const MAGIC: [u8; 6] = *b"DIGVK1";
const NAME: &'static str = "BlsSigning";
const SCHEME_ID: u16 = 0x0001;
const SECRET_LEN: usize = 32;
fn generate<R: RngCore + CryptoRng>(rng: &mut R) -> Zeroizing<Vec<u8>> {
let mut seed = Zeroizing::new(vec![0u8; Self::SECRET_LEN]);
rng.fill_bytes(&mut seed);
seed
}
fn public_key(secret: &[u8]) -> Result<Self::PublicKey> {
let sk = secret_to_bls_secret_key(secret)?;
let pk = sk.public_key();
Ok(pk)
}
fn sign(secret: &[u8], msg: &[u8]) -> Result<Self::Signature> {
let sk = secret_to_bls_secret_key(secret)?;
Ok(chia_bls::sign(&sk, msg))
}
}
pub(crate) fn secret_to_bls_secret_key(secret: &[u8]) -> Result<SecretKey> {
if secret.len() != BlsSigning::SECRET_LEN {
return Err(KeystoreError::InvalidPlaintext {
expected: BlsSigning::SECRET_LEN,
got: secret.len(),
});
}
Ok(SecretKey::from_seed(secret))
}
#[cfg(test)]
mod tests {
use super::*;
use rand_chacha::rand_core::SeedableRng;
#[test]
fn generate_is_32_bytes() {
let mut rng = rand_chacha::ChaCha20Rng::seed_from_u64(42);
let seed = BlsSigning::generate(&mut rng);
assert_eq!(seed.len(), 32);
}
#[test]
fn public_key_deterministic() {
let seed = [7u8; 32];
let pk1 = BlsSigning::public_key(&seed).unwrap();
let pk2 = BlsSigning::public_key(&seed).unwrap();
assert_eq!(pk1.to_bytes(), pk2.to_bytes());
}
#[test]
fn sign_verifies_via_chia_bls() {
let seed = [11u8; 32];
let pk = BlsSigning::public_key(&seed).unwrap();
let msg = b"hello";
let sig = BlsSigning::sign(&seed, msg).unwrap();
assert!(chia_bls::verify(&sig, &pk, msg));
}
#[test]
fn sign_different_messages_produce_different_signatures() {
let seed = [11u8; 32];
let s1 = BlsSigning::sign(&seed, b"msg1").unwrap();
let s2 = BlsSigning::sign(&seed, b"msg2").unwrap();
assert_ne!(s1.to_bytes(), s2.to_bytes());
}
#[test]
fn different_seeds_produce_different_keys() {
let pk1 = BlsSigning::public_key(&[1u8; 32]).unwrap();
let pk2 = BlsSigning::public_key(&[2u8; 32]).unwrap();
assert_ne!(pk1.to_bytes(), pk2.to_bytes());
}
#[test]
fn wrong_seed_length_errors() {
let err = BlsSigning::public_key(&[0u8; 16]).unwrap_err();
assert!(matches!(
err,
KeystoreError::InvalidPlaintext {
expected: 32,
got: 16
}
));
}
}