#![forbid(unsafe_code)]
#![forbid(missing_docs)]
#![expect(clippy::result_unit_err, reason = "Preserve the upstream unit-error API.")]
use super::curve as bls12_381;
use core::fmt;
use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve};
use bls12_381::{G1Affine, G1Projective, G2Affine, Scalar};
use pairing::group::Curve;
use core::ops::Neg;
static G2PREPARED_NEG_G: std::sync::LazyLock<bls12_381::G2Prepared> =
std::sync::LazyLock::new(|| G2Affine::generator().neg().into());
const BLS_SIGNATURE_DOMAIN_SEP: [u8; 43] = *b"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_";
fn hash_to_g1(msg: &[u8]) -> G1Affine {
<G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(
msg,
&BLS_SIGNATURE_DOMAIN_SEP,
)
.to_affine()
}
#[derive(Clone, Eq, PartialEq)]
pub struct PublicKey {
pk: G2Affine,
}
impl fmt::Debug for PublicKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PublicKey({})", hex::encode(self.serialize()))
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum InvalidPublicKey {
WrongLength,
InvalidPoint,
}
impl PublicKey {
pub const BYTES: usize = 96;
fn new(pk: G2Affine) -> Self {
Self { pk }
}
pub fn serialize(&self) -> [u8; Self::BYTES] {
self.pk.to_compressed()
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidPublicKey> {
let bytes: Result<[u8; Self::BYTES], _> = bytes.try_into();
match bytes {
Err(_) => Err(InvalidPublicKey::WrongLength),
Ok(b) => {
let pk = G2Affine::from_compressed(&b);
if bool::from(pk.is_some()) {
Ok(Self::new(pk.unwrap()))
} else {
Err(InvalidPublicKey::InvalidPoint)
}
}
}
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), ()> {
if self.pk.is_identity().into() {
return Err(());
}
let msg = hash_to_g1(message);
{
use bls12_381::{multi_miller_loop, G2Prepared};
use pairing::group::Group;
let g2_gen: &G2Prepared = &G2PREPARED_NEG_G;
let pk = G2Prepared::from(self.pk);
let sig_g2 = (&signature.sig, g2_gen);
let msg_pk = (&msg, &pk);
let x = multi_miller_loop(&[sig_g2, msg_pk]).final_exponentiation();
if bool::from(x.is_identity()) {
Ok(())
} else {
Err(())
}
}
#[cfg(any())] {
use bls12_381::pairing;
let g2 = G2Affine::generator();
let lhs = pairing(&signature.sig, &g2);
let rhs = pairing(&msg, &self.pk);
if lhs == rhs {
Ok(())
} else {
Err(())
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum InvalidSignature {
WrongLength,
InvalidPoint,
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct Signature {
sig: G1Affine,
}
impl fmt::Debug for Signature {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Signature({})", hex::encode(self.serialize()))
}
}
impl Signature {
pub const BYTES: usize = 48;
fn new(sig: G1Affine) -> Self {
Self { sig }
}
pub fn serialize(&self) -> [u8; Self::BYTES] {
self.sig.to_compressed()
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidSignature> {
let bytes: Result<[u8; Self::BYTES], _> = bytes.try_into();
match bytes {
Err(_) => Err(InvalidSignature::WrongLength),
Ok(b) => {
let sig = G1Affine::from_compressed(&b);
if bool::from(sig.is_some()) {
Ok(Self::new(sig.unwrap()))
} else {
Err(InvalidSignature::InvalidPoint)
}
}
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum InvalidPrivateKey {
WrongLength,
OutOfRange,
}
#[derive(Copy, Clone, Eq, PartialEq)]
pub struct PrivateKey {
sk: Scalar,
}
impl fmt::Debug for PrivateKey {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "PrivateKey(REDACTED)")
}
}
impl PrivateKey {
pub const BYTES: usize = 32;
fn new(sk: Scalar) -> Self {
Self { sk }
}
#[cfg(test)] pub fn random<R: rand::RngCore + rand::CryptoRng>(rng: &mut R) -> Self {
loop {
let mut buf = [0u8; 32];
rng.fill_bytes(&mut buf);
let s: Option<Scalar> = Scalar::from_bytes(&buf).into();
if let Some(s) = s {
return Self::new(s);
}
}
}
pub fn serialize(&self) -> [u8; Self::BYTES] {
let mut bytes = self.sk.to_bytes();
bytes.reverse();
bytes
}
pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidPrivateKey> {
let bytes: Result<[u8; Self::BYTES], _> = bytes.try_into();
match bytes {
Err(_) => Err(InvalidPrivateKey::WrongLength),
Ok(mut b) => {
b.reverse();
let sk = Scalar::from_bytes(&b);
if bool::from(sk.is_some()) {
Ok(Self::new(sk.unwrap()))
} else {
Err(InvalidPrivateKey::OutOfRange)
}
}
}
}
pub fn public_key(&self) -> PublicKey {
PublicKey::new(G2Affine::from(G2Affine::generator() * self.sk))
}
pub fn sign(&self, message: &[u8]) -> Signature {
let sig = hash_to_g1(message) * self.sk;
Signature::new(sig.to_affine())
}
}
pub fn verify_bls_signature(sig: &[u8], msg: &[u8], key: &[u8]) -> Result<(), ()> {
let sig = Signature::deserialize(sig).map_err(|_| ())?;
let pk = PublicKey::deserialize(key).map_err(|_| ())?;
pk.verify(msg, &sig)
}
#[cfg(test)]
#[rustfmt::skip]
mod tests;