ic_verify_bls_signature/
lib.rs1#![no_std]
2#![forbid(unsafe_code)]
3#![forbid(missing_docs)]
4#![allow(clippy::result_unit_err)]
5
6use core::fmt;
12
13use bls12_381::hash_to_curve::{ExpandMsgXmd, HashToCurve};
14use bls12_381::{G1Affine, G1Projective, G2Affine, Scalar};
15use pairing::group::Curve;
16
17#[cfg(feature = "alloc")]
18use core::ops::Neg;
19
20#[cfg(feature = "alloc")]
21lazy_static::lazy_static! {
22 static ref G2PREPARED_NEG_G : bls12_381::G2Prepared = G2Affine::generator().neg().into();
23}
24
25const BLS_SIGNATURE_DOMAIN_SEP: [u8; 43] = *b"BLS_SIG_BLS12381G1_XMD:SHA-256_SSWU_RO_NUL_";
26
27fn hash_to_g1(msg: &[u8]) -> G1Affine {
28 <G1Projective as HashToCurve<ExpandMsgXmd<sha2::Sha256>>>::hash_to_curve(
29 msg,
30 &BLS_SIGNATURE_DOMAIN_SEP,
31 )
32 .to_affine()
33}
34
35#[derive(Clone, Eq, PartialEq)]
37#[cfg_attr(not(feature = "alloc"), derive(Debug))]
38pub struct PublicKey {
39 pk: G2Affine,
40}
41
42#[cfg(feature = "alloc")]
43impl fmt::Debug for PublicKey {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 write!(f, "PublicKey({})", hex::encode(self.serialize()))
46 }
47}
48
49#[derive(Debug, Copy, Clone, Eq, PartialEq)]
51pub enum InvalidPublicKey {
52 WrongLength,
54 InvalidPoint,
56}
57
58impl PublicKey {
59 pub const BYTES: usize = 96;
61
62 fn new(pk: G2Affine) -> Self {
63 Self { pk }
64 }
65
66 pub fn serialize(&self) -> [u8; Self::BYTES] {
68 self.pk.to_compressed()
69 }
70
71 pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidPublicKey> {
73 let bytes: Result<[u8; Self::BYTES], _> = bytes.try_into();
74
75 match bytes {
76 Err(_) => Err(InvalidPublicKey::WrongLength),
77 Ok(b) => {
78 let pk = G2Affine::from_compressed(&b);
79 if bool::from(pk.is_some()) {
80 Ok(Self::new(pk.unwrap()))
81 } else {
82 Err(InvalidPublicKey::InvalidPoint)
83 }
84 }
85 }
86 }
87
88 pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), ()> {
90 let msg = hash_to_g1(message);
91
92 #[cfg(feature = "alloc")]
93 {
94 use bls12_381::{multi_miller_loop, G2Prepared};
95 use pairing::group::Group;
96
97 let g2_gen: &G2Prepared = &G2PREPARED_NEG_G;
98 let pk = G2Prepared::from(self.pk);
99
100 let sig_g2 = (&signature.sig, g2_gen);
101 let msg_pk = (&msg, &pk);
102
103 let x = multi_miller_loop(&[sig_g2, msg_pk]).final_exponentiation();
104
105 if bool::from(x.is_identity()) {
106 Ok(())
107 } else {
108 Err(())
109 }
110 }
111
112 #[cfg(not(feature = "alloc"))]
113 {
114 use bls12_381::pairing;
115
116 let g2 = G2Affine::generator();
117 let lhs = pairing(&signature.sig, &g2);
118 let rhs = pairing(&msg, &self.pk);
119
120 if lhs == rhs {
121 Ok(())
122 } else {
123 Err(())
124 }
125 }
126 }
127}
128
129#[derive(Debug, Copy, Clone, Eq, PartialEq)]
131pub enum InvalidSignature {
132 WrongLength,
134 InvalidPoint,
136}
137
138#[derive(Copy, Clone, Eq, PartialEq)]
140#[cfg_attr(not(feature = "alloc"), derive(Debug))]
141pub struct Signature {
142 sig: G1Affine,
143}
144
145#[cfg(feature = "alloc")]
146impl fmt::Debug for Signature {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "Signature({})", hex::encode(self.serialize()))
149 }
150}
151
152impl Signature {
153 pub const BYTES: usize = 48;
155
156 fn new(sig: G1Affine) -> Self {
157 Self { sig }
158 }
159
160 pub fn serialize(&self) -> [u8; Self::BYTES] {
162 self.sig.to_compressed()
163 }
164
165 pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidSignature> {
167 let bytes: Result<[u8; Self::BYTES], _> = bytes.try_into();
168
169 match bytes {
170 Err(_) => Err(InvalidSignature::WrongLength),
171 Ok(b) => {
172 let sig = G1Affine::from_compressed(&b);
173 if bool::from(sig.is_some()) {
174 Ok(Self::new(sig.unwrap()))
175 } else {
176 Err(InvalidSignature::InvalidPoint)
177 }
178 }
179 }
180 }
181}
182
183#[derive(Debug, Copy, Clone, Eq, PartialEq)]
185pub enum InvalidPrivateKey {
186 WrongLength,
188 OutOfRange,
190}
191
192#[derive(Copy, Clone, Eq, PartialEq)]
194pub struct PrivateKey {
195 sk: Scalar,
196}
197
198impl fmt::Debug for PrivateKey {
199 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200 write!(f, "PrivateKey(REDACTED)")
201 }
202}
203
204impl PrivateKey {
205 pub const BYTES: usize = 32;
207
208 fn new(sk: Scalar) -> Self {
209 Self { sk }
210 }
211
212 #[cfg(feature = "rand")]
214 pub fn random<R: rand::RngCore + rand::CryptoRng>(rng: &mut R) -> Self {
215 loop {
216 let mut buf = [0u8; 32];
217 rng.fill_bytes(&mut buf);
218 let s: Option<Scalar> = Scalar::from_bytes(&buf).into();
219
220 if let Some(s) = s {
221 return Self::new(s);
222 }
223 }
224 }
225
226 pub fn serialize(&self) -> [u8; Self::BYTES] {
228 let mut bytes = self.sk.to_bytes();
229 bytes.reverse();
230 bytes
231 }
232
233 pub fn deserialize(bytes: &[u8]) -> Result<Self, InvalidPrivateKey> {
235 let bytes: Result<[u8; Self::BYTES], _> = bytes.try_into();
236
237 match bytes {
238 Err(_) => Err(InvalidPrivateKey::WrongLength),
239 Ok(mut b) => {
240 b.reverse();
241 let sk = Scalar::from_bytes(&b);
242 if bool::from(sk.is_some()) {
243 Ok(Self::new(sk.unwrap()))
244 } else {
245 Err(InvalidPrivateKey::OutOfRange)
246 }
247 }
248 }
249 }
250
251 pub fn public_key(&self) -> PublicKey {
253 PublicKey::new(G2Affine::from(G2Affine::generator() * self.sk))
254 }
255
256 pub fn sign(&self, message: &[u8]) -> Signature {
260 let sig = hash_to_g1(message) * self.sk;
261 Signature::new(sig.to_affine())
262 }
263}
264
265pub fn verify_bls_signature(sig: &[u8], msg: &[u8], key: &[u8]) -> Result<(), ()> {
270 let sig = Signature::deserialize(sig).map_err(|_| ())?;
271 let pk = PublicKey::deserialize(key).map_err(|_| ())?;
272 pk.verify(msg, &sig)
273}