Skip to content

Commit fe86cb0

Browse files
[keys]: add features: account.sign and verify() fns
1 parent 599a3fe commit fe86cb0

File tree

2 files changed

+114
-16
lines changed

2 files changed

+114
-16
lines changed

crates/delano-keys/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ serde = { version = "1.0.130", features = ["derive"], optional = true }
2020
serde_json = { version = "1.0", optional = true }
2121
getrandom = "0.2"
2222
sha2 = "0.10"
23+
thiserror = "1.0"
2324

2425
[dev-dependencies]
2526
rand = "0.8"

crates/delano-keys/src/kdf.rs

+113-16
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
11
//! If `derive` is feature is enabled, this crate provides a key derivation mechanism for BLS12-381.
2+
use crate::error::Error;
23
use crate::vk;
34
use blastkids::kdf;
45

6+
use bls12_381_plus::elliptic_curve::hash2curve::ExpandMsgXmd;
57
// re-exports
68
pub use bls12_381_plus::group::{Group, GroupEncoding};
7-
pub use bls12_381_plus::G1Projective as G1;
8-
pub use bls12_381_plus::G2Projective as G2;
9+
pub use bls12_381_plus::G1Projective;
10+
pub use bls12_381_plus::G2Projective;
911
pub use bls12_381_plus::Scalar;
12+
use bls12_381_plus::{group::Curve, G2Affine};
1013
pub use secrecy::zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
1114
pub use secrecy::{ExposeSecret, Secret};
1215

1316
use bls12_381_plus::elliptic_curve::ops::MulByGenerator;
1417

18+
/// Domain Separation Tag for Proof of Possession, as our Signatures are in G2 (and public keys are in G1).
19+
/// See <https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-bls-signature-04#section-4.2.3> for more details.
20+
const DST: &'static [u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
21+
1522
/// Seed and master key Manager.
1623
///
1724
/// Generic over the type of curve used, [either G1 or G2](https://hackmd.io/@benjaminion/bls12-381#Swapping-G1-and-G2)
@@ -91,9 +98,9 @@ impl Manager {
9198
// Derive the first normal secret key in advance
9299
// so we can get our Deterministic Public Keys for this account
93100
// for any length / size
94-
let sk_normal_0 = Zeroizing::new(kdf::ckd_sk_normal::<G2>(sk.expose_secret(), 0));
95-
let pk_normal_g1 = G1::mul_by_generator(&sk_normal_0);
96-
let pk_hardened_g2 = G2::mul_by_generator(sk.expose_secret());
101+
let sk_normal_0 = Zeroizing::new(kdf::ckd_sk_normal::<G2Projective>(sk.expose_secret(), 0));
102+
let pk_normal_g1 = G1Projective::mul_by_generator(&sk_normal_0);
103+
let pk_hardened_g2 = G2Projective::mul_by_generator(sk.expose_secret());
97104

98105
Account {
99106
index,
@@ -110,13 +117,13 @@ impl Manager {
110117
pub struct Account {
111118
pub index: u32,
112119
sk: Secret<Scalar>,
113-
pub pk_g1: G1,
114-
pub pk_g2: G2,
120+
pub pk_g1: G1Projective,
121+
pub pk_g2: G2Projective,
115122
}
116123

117124
impl Account {
118125
/// Create a new account from a secret key and public key
119-
pub fn new(index: u32, sk: Scalar, pk_g1: G1, pk_g2: G2) -> Self {
126+
pub fn new(index: u32, sk: Scalar, pk_g1: G1Projective, pk_g2: G2Projective) -> Self {
120127
Self {
121128
index,
122129
sk: Secret::new(sk),
@@ -133,19 +140,55 @@ impl Account {
133140
pub fn expand_to(&self, length: u8) -> Secret<Vec<Scalar>> {
134141
Secret::new(
135142
(0..length)
136-
.map(|i| kdf::ckd_sk_normal::<G2>(self.sk.expose_secret(), i as u32))
143+
.map(|i| kdf::ckd_sk_normal::<G2Projective>(self.sk.expose_secret(), i as u32))
137144
.collect::<Vec<Scalar>>(),
138145
)
139146
}
147+
148+
/// Generates a signature for this account's [G1Projective] public key.
149+
pub fn sign(&self, message: &[u8]) -> [u8; G2Affine::COMPRESSED_BYTES] {
150+
let sk_normal_0 = Zeroizing::new(kdf::ckd_sk_normal::<G2Projective>(
151+
self.sk.expose_secret(),
152+
0,
153+
));
154+
155+
// Hash the msg to G2
156+
let g2_point = G2Projective::hash::<ExpandMsgXmd<sha2::Sha256>>(message, DST);
157+
158+
let signature = g2_point * *sk_normal_0;
159+
160+
signature.to_compressed()
161+
}
162+
}
163+
164+
/// Verify a signed message ([G2Compressed]) against a [G1] public key.
165+
pub fn verify(pk: &G1Projective, message: &[u8], signature: &[u8]) -> Result<bool, Error> {
166+
// let err msg say that signature was not a valid G2 point
167+
let sig_g2 = try_decompress_g2(signature.to_vec())?;
168+
let pk_affine = pk.to_affine();
169+
170+
// Hash the msg to G2Affine
171+
let hashed_msg_g2 = G2Projective::hash::<ExpandMsgXmd<sha2::Sha256>>(message, DST).to_affine();
172+
let g1_generator = G1Projective::generator().to_affine();
173+
174+
// Verify the signature by checking the pairing(G1_pubkey, G2_hashed_msg) == pairing(G1_generator, G2_signature)
175+
let result = bls12_381_plus::pairing(&pk_affine, &hashed_msg_g2)
176+
== bls12_381_plus::pairing(&g1_generator, &sig_g2);
177+
178+
Ok(result)
140179
}
141180

142181
/// Given an Account's root Public Keys and a desired [VK] length,
143182
/// derive the expanded Verification Key [VK] for the Account size.
144183
///
145184
/// The length is the target length of the entire Verification Key [VK]
146-
pub fn derive(pk_g1: &G1, pk_g2: &G2, length: u8) -> Vec<vk::VK> {
185+
pub fn derive(pk_g1: &G1Projective, pk_g2: &G2Projective, length: u8) -> Vec<vk::VK> {
147186
let vk_g2_expanded: Vec<vk::VK> = (0..(length - 1))
148-
.map(|i| vk::VK::G2(blastkids::kdf::ckd_pk_normal::<G2>(pk_g2, i as u32)))
187+
.map(|i| {
188+
vk::VK::G2(blastkids::kdf::ckd_pk_normal::<G2Projective>(
189+
pk_g2, i as u32,
190+
))
191+
})
149192
.collect();
150193

151194
// concat pk_g1, vk_g2_expanded
@@ -155,6 +198,19 @@ pub fn derive(pk_g1: &G1, pk_g2: &G2, length: u8) -> Vec<vk::VK> {
155198
vk
156199
}
157200

201+
/// Try Decompress G2
202+
pub(crate) fn try_decompress_g2(value: Vec<u8>) -> Result<G2Affine, Error> {
203+
let mut bytes = [0u8; G2Affine::COMPRESSED_BYTES];
204+
bytes.copy_from_slice(&value);
205+
let maybe_g2 = G2Affine::from_compressed(&bytes);
206+
207+
if maybe_g2.is_none().into() {
208+
return Err(Error::InvalidG2Point);
209+
} else {
210+
Ok(maybe_g2.unwrap())
211+
}
212+
}
213+
158214
#[cfg(test)]
159215
mod basic_test {
160216

@@ -174,9 +230,9 @@ mod basic_test {
174230
// get the pk from each of the expanded keys
175231
// remember, the first VK is sk[0] * G1 and 2nd VK is sk[0] * G2Projective
176232
// so we need len - 1 secret keys
177-
let pk_g1 = G1::mul_by_generator(&expanded.expose_secret()[0]);
178-
let pk_g2_0 = G2::mul_by_generator(&expanded.expose_secret()[0]);
179-
let pk_g2_1 = G2::mul_by_generator(&expanded.expose_secret()[1]);
233+
let pk_g1 = G1Projective::mul_by_generator(&expanded.expose_secret()[0]);
234+
let pk_g2_0 = G2Projective::mul_by_generator(&expanded.expose_secret()[0]);
235+
let pk_g2_1 = G2Projective::mul_by_generator(&expanded.expose_secret()[1]);
180236

181237
let vk = derive(&account.pk_g1, &account.pk_g2, 3);
182238

@@ -190,9 +246,50 @@ mod basic_test {
190246
vk,
191247
vec![
192248
vk::VK::G1(account.pk_g1),
193-
vk::VK::G2(blastkids::kdf::ckd_pk_normal::<G2>(&account.pk_g2, 0)),
194-
vk::VK::G2(blastkids::kdf::ckd_pk_normal::<G2>(&account.pk_g2, 1))
249+
vk::VK::G2(blastkids::kdf::ckd_pk_normal::<G2Projective>(
250+
&account.pk_g2,
251+
0
252+
)),
253+
vk::VK::G2(blastkids::kdf::ckd_pk_normal::<G2Projective>(
254+
&account.pk_g2,
255+
1
256+
))
195257
]
196258
);
197259
}
260+
261+
// Tests signature and verify
262+
#[test]
263+
fn test_sign_roundtrip() {
264+
let seed = Zeroizing::new([69u8; 32]);
265+
let manager: Manager = Manager::from_seed(seed);
266+
267+
// For each account indices: 0, 1, 2, 3
268+
let indices = vec![0, 1, 2, 3];
269+
270+
for index in indices {
271+
let account = manager.account(index);
272+
273+
let message = b"hello world";
274+
let signature = account.sign(message);
275+
276+
let verified = verify(&account.pk_g1, message, &signature).unwrap();
277+
assert!(verified);
278+
}
279+
}
280+
281+
// Failing signature verification
282+
#[test]
283+
fn test_sign_roundtrip_fail() {
284+
let seed = Zeroizing::new([69u8; 32]);
285+
let manager: Manager = Manager::from_seed(seed);
286+
287+
let account = manager.account(1);
288+
289+
let message = b"hello world";
290+
let signature = account.sign(message);
291+
292+
let verified = verify(&account.pk_g1, b"hello world!", &signature).unwrap();
293+
assert!(!verified);
294+
}
198295
}

0 commit comments

Comments
 (0)