1
1
//! If `derive` is feature is enabled, this crate provides a key derivation mechanism for BLS12-381.
2
+ use crate :: error:: Error ;
2
3
use crate :: vk;
3
4
use blastkids:: kdf;
4
5
6
+ use bls12_381_plus:: elliptic_curve:: hash2curve:: ExpandMsgXmd ;
5
7
// re-exports
6
8
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 ;
9
11
pub use bls12_381_plus:: Scalar ;
12
+ use bls12_381_plus:: { group:: Curve , G2Affine } ;
10
13
pub use secrecy:: zeroize:: { Zeroize , ZeroizeOnDrop , Zeroizing } ;
11
14
pub use secrecy:: { ExposeSecret , Secret } ;
12
15
13
16
use bls12_381_plus:: elliptic_curve:: ops:: MulByGenerator ;
14
17
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
+
15
22
/// Seed and master key Manager.
16
23
///
17
24
/// 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 {
91
98
// Derive the first normal secret key in advance
92
99
// so we can get our Deterministic Public Keys for this account
93
100
// 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 ( ) ) ;
97
104
98
105
Account {
99
106
index,
@@ -110,13 +117,13 @@ impl Manager {
110
117
pub struct Account {
111
118
pub index : u32 ,
112
119
sk : Secret < Scalar > ,
113
- pub pk_g1 : G1 ,
114
- pub pk_g2 : G2 ,
120
+ pub pk_g1 : G1Projective ,
121
+ pub pk_g2 : G2Projective ,
115
122
}
116
123
117
124
impl Account {
118
125
/// 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 {
120
127
Self {
121
128
index,
122
129
sk : Secret :: new ( sk) ,
@@ -133,19 +140,55 @@ impl Account {
133
140
pub fn expand_to ( & self , length : u8 ) -> Secret < Vec < Scalar > > {
134
141
Secret :: new (
135
142
( 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 ) )
137
144
. collect :: < Vec < Scalar > > ( ) ,
138
145
)
139
146
}
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)
140
179
}
141
180
142
181
/// Given an Account's root Public Keys and a desired [VK] length,
143
182
/// derive the expanded Verification Key [VK] for the Account size.
144
183
///
145
184
/// 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 > {
147
186
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
+ } )
149
192
. collect ( ) ;
150
193
151
194
// concat pk_g1, vk_g2_expanded
@@ -155,6 +198,19 @@ pub fn derive(pk_g1: &G1, pk_g2: &G2, length: u8) -> Vec<vk::VK> {
155
198
vk
156
199
}
157
200
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
+
158
214
#[ cfg( test) ]
159
215
mod basic_test {
160
216
@@ -174,9 +230,9 @@ mod basic_test {
174
230
// get the pk from each of the expanded keys
175
231
// remember, the first VK is sk[0] * G1 and 2nd VK is sk[0] * G2Projective
176
232
// 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 ] ) ;
180
236
181
237
let vk = derive ( & account. pk_g1 , & account. pk_g2 , 3 ) ;
182
238
@@ -190,9 +246,50 @@ mod basic_test {
190
246
vk,
191
247
vec![
192
248
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
+ ) )
195
257
]
196
258
) ;
197
259
}
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
+ }
198
295
}
0 commit comments