Skip to content
This repository was archived by the owner on Feb 26, 2025. It is now read-only.

Commit 9b2646e

Browse files
authored
feat: add blinded bls12-381 key generation (#130)
1 parent 57aecf4 commit 9b2646e

File tree

7 files changed

+248
-5
lines changed

7 files changed

+248
-5
lines changed

__tests__/bls12381.spec.ts

+71
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@
1414
import {
1515
generateBls12381G1KeyPair,
1616
generateBls12381G2KeyPair,
17+
generateBlindedBls12381G1KeyPair,
18+
generateBlindedBls12381G2KeyPair,
1719
DEFAULT_BLS12381_G1_PUBLIC_KEY_LENGTH,
1820
DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH,
1921
DEFAULT_BLS12381_PRIVATE_KEY_LENGTH,
22+
BLS12381_BLINDING_FACTOR_LENGTH,
2023
} from "../src";
2124

2225
describe("bls12381", () => {
@@ -109,4 +112,72 @@ describe("bls12381", () => {
109112
expect(result.publicKey).toEqual(value.publicKey);
110113
});
111114
});
115+
116+
[
117+
{
118+
field: "G1",
119+
generateKeyFn: generateBlindedBls12381G1KeyPair,
120+
secretKeyLength: DEFAULT_BLS12381_PRIVATE_KEY_LENGTH,
121+
publicKeyLength: DEFAULT_BLS12381_G1_PUBLIC_KEY_LENGTH,
122+
},
123+
{
124+
field: "G2",
125+
generateKeyFn: generateBlindedBls12381G2KeyPair,
126+
secretKeyLength: DEFAULT_BLS12381_PRIVATE_KEY_LENGTH,
127+
publicKeyLength: DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH,
128+
},
129+
].forEach((value) => {
130+
it(`should be able to generate a blinded key pair in ${value.field} field`, () => {
131+
const result = value.generateKeyFn();
132+
expect(result).toBeDefined();
133+
expect(result.publicKey).toBeDefined();
134+
expect(result.secretKey).toBeDefined();
135+
expect(result.secretKey?.length as number).toEqual(value.secretKeyLength);
136+
expect(result.publicKey.length).toEqual(value.publicKeyLength);
137+
expect(result.blindingFactor.length).toEqual(BLS12381_BLINDING_FACTOR_LENGTH);
138+
});
139+
});
140+
141+
[
142+
{
143+
field: "G1",
144+
generateKeyFn: generateBlindedBls12381G1KeyPair,
145+
secretKeyLength: DEFAULT_BLS12381_PRIVATE_KEY_LENGTH,
146+
publicKeyLength: DEFAULT_BLS12381_G1_PUBLIC_KEY_LENGTH,
147+
seed: new Uint8Array(
148+
Buffer.from("H297BpoOgkfpXcxr1fJyQRiNx1+ZekeQ+OU/AYV/lVxaPXXhFBIbxeIU8kIAAX68cwQ=", "base64")
149+
),
150+
secretKey: new Uint8Array(Buffer.from("Cm550dHeqo5I/dVC/bXD9s5Cx8vnyhV/gm7KO5UuviE=", "base64")),
151+
publicKey: new Uint8Array(
152+
Buffer.from("rxCOFKk5NvHZZqUY0b6DdwvBGgyqZQmmL9vB3t8iAvY2IEOBF8l1rBV23BSClV56", "base64")
153+
),
154+
},
155+
{
156+
field: "G2",
157+
generateKeyFn: generateBlindedBls12381G2KeyPair,
158+
secretKeyLength: DEFAULT_BLS12381_PRIVATE_KEY_LENGTH,
159+
publicKeyLength: DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH,
160+
seed: new Uint8Array(
161+
Buffer.from("H297BpoOgkfpXcxr1fJyQRiNx1+ZekeQ+OU/AYV/lVxaPXXhFBIbxeIU8kIAAX68cwQ=", "base64")
162+
),
163+
secretKey: new Uint8Array(Buffer.from("Cm550dHeqo5I/dVC/bXD9s5Cx8vnyhV/gm7KO5UuviE=", "base64")),
164+
publicKey: new Uint8Array(
165+
Buffer.from(
166+
"kGLIHOh7+NHNf8JWYtxPtiNvEDc5EQ6V0TifHf9vbCm48IwzNCOwBfUTZoz2JFhgGDdZTAHg9Bn4YhAHmFj7dWPeFcJHl6HdWqtLxJ6/wAzs6i7bqoMc98IkUDSSJXrm",
167+
"base64"
168+
)
169+
),
170+
},
171+
].forEach((value) => {
172+
it(`should be able to generate a blinded key pair with a seed in ${value.field} field`, () => {
173+
const result = value.generateKeyFn(value.seed);
174+
expect(result.publicKey).toBeDefined();
175+
expect(result.secretKey).toBeDefined();
176+
expect(result.secretKey?.length as number).toEqual(value.secretKeyLength);
177+
expect(result.publicKey.length).toEqual(value.publicKeyLength);
178+
expect(result.publicKey).toEqual(value.publicKey);
179+
expect(result.secretKey as Uint8Array).toEqual(value.secretKey);
180+
expect(result.blindingFactor.length).toEqual(BLS12381_BLINDING_FACTOR_LENGTH);
181+
});
182+
});
112183
});

native/src/lib.rs

+109-3
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,103 @@ use pairing_plus::{bls12_381::{Fr, G1, G2, Bls12}, serdes::SerDes, hash_to_field
2626
use rand::{thread_rng, RngCore};
2727
use std::collections::{BTreeMap, BTreeSet};
2828

29+
// This shows how the generators are created with nothing up my sleeve values
30+
// const PREHASH: &'static [u8] = b"To be, or not to be- that is the question:
31+
// Whether 'tis nobler in the mind to suffer
32+
// The slings and arrows of outrageous fortune
33+
// Or to take arms against a sea of troubles,
34+
// And by opposing end them. To die- to sleep-
35+
// No more; and by a sleep to say we end
36+
// The heartache, and the thousand natural shocks
37+
// That flesh is heir to. 'Tis a consummation
38+
// Devoutly to be wish'd. To die- to sleep.
39+
// To sleep- perchance to dream: ay, there's the rub!
40+
// For in that sleep of death what dreams may come
41+
// When we have shuffled off this mortal coil,
42+
// Must give us pause. There's the respect
43+
// That makes calamity of so long life.
44+
// For who would bear the whips and scorns of time,
45+
// Th' oppressor's wrong, the proud man's contumely,
46+
// The pangs of despis'd love, the law's delay,
47+
// The insolence of office, and the spurns
48+
// That patient merit of th' unworthy takes,
49+
// When he himself might his quietus make
50+
// With a bare bodkin? Who would these fardels bear,
51+
// To grunt and sweat under a weary life,
52+
// But that the dread of something after death-
53+
// The undiscover'd country, from whose bourn
54+
// No traveller returns- puzzles the will,
55+
// And makes us rather bear those ills we have
56+
// Than fly to others that we know not of?
57+
// Thus conscience does make cowards of us all,
58+
// And thus the native hue of resolution
59+
// Is sicklied o'er with the pale cast of thought,
60+
// And enterprises of great pith and moment
61+
// With this regard their currents turn awry
62+
// And lose the name of action.- Soft you now!
63+
// The fair Ophelia!- Nymph, in thy orisons
64+
// Be all my sins rememb'red.";
65+
// const DST_G1: &'static [u8] = b"BLS12381G1_XMD:BLAKE2B_SSWU_RO_BLS_SIGNATURES:1_0_0";
66+
// const DST_G2: &'static [u8] = b"BLS12381G2_XMD:BLAKE2B_SSWU_RO_BLS_SIGNATURES:1_0_0";
67+
//
68+
// fn main() {
69+
// let g1 = <G1 as HashToCurve<ExpandMsgXmd<blake2::Blake2b>>>::hash_to_curve(PREHASH, DST_G1);
70+
// let g2 = <G2 as HashToCurve<ExpandMsgXmd<blake2::Blake2b>>>::hash_to_curve(PREHASH, DST_G2);
71+
//
72+
// let mut g1_bytes = Vec::new();
73+
// let mut g2_bytes = Vec::new();
74+
//
75+
// g1.serialize(&mut g1_bytes, true).unwrap();
76+
// g2.serialize(&mut g2_bytes, true).unwrap();
77+
//
78+
// println!("g1 = {}", hex::encode(g1_bytes.as_slice()));
79+
// println!("g2 = {}", hex::encode(g2_bytes.as_slice()));
80+
// }
81+
// g1 = b9c9058e8a44b87014f98be4e1818db718f8b2d5101fc89e6983625f321f14b84d7cf6e155004987a215ee426df173c9
82+
// g2 = a963de2adfb1163cf4bed24d708ce47432742d2080b2573ebe2e19a8698f60c541cec000fcb19783e9be73341356df5f1191cddec7c476d7742bcc421afc5d505e63373c627ea01fda04f0e40159d25bdd12f45a010d8580a78f6a7d262272f3
83+
84+
const BLINDING_G1: &'static [u8] = &[185, 201, 5, 142, 138, 68, 184, 112, 20, 249, 139, 228, 225, 129, 141, 183, 24, 248, 178, 213, 16, 31, 200, 158, 105, 131, 98, 95, 50, 31, 20, 184, 77, 124, 246, 225, 85, 0, 73, 135, 162, 21, 238, 66, 109, 241, 115, 201];
85+
const BLINDING_G2: &'static [u8] = &[169, 99, 222, 42, 223, 177, 22, 60, 244, 190, 210, 77, 112, 140, 228, 116, 50, 116, 45, 32, 128, 178, 87, 62, 190, 46, 25, 168, 105, 143, 96, 197, 65, 206, 192, 0, 252, 177, 151, 131, 233, 190, 115, 52, 19, 86, 223, 95, 17, 145, 205, 222, 199, 196, 118, 215, 116, 43, 204, 66, 26, 252, 93, 80, 94, 99, 55, 60, 98, 126, 160, 31, 218, 4, 240, 228, 1, 89, 210, 91, 221, 18, 244, 90, 1, 13, 133, 128, 167, 143, 106, 125, 38, 34, 114, 243];
86+
87+
/// Generate a blinded BLS key pair where secret key `x` and blinding factor `r` in Fp
88+
/// and public key `w` = `g2` ^ `x` * `blinding_g2` ^ `r`
89+
/// `seed`: `ArrayBuffer` [opt]
90+
/// `return` Object { publicKey: `ArrayBuffer`, secretKey: `ArrayBuffer`, blindingFactor: `ArrayBuffer` }
91+
fn bls_generate_blinded_g2_key(cx: FunctionContext) -> JsResult<JsObject> {
92+
bls_generate_keypair::<G2>(cx, Some(BLINDING_G2))
93+
}
94+
95+
/// Generate a blinded BLS key pair where secret key `x` and blinding factor `r` in Fp
96+
/// and public key `w` = `g1` ^ `x` * `blinding_g1` ^ `r`
97+
/// `seed`: `ArrayBuffer` [opt]
98+
/// `return` Object { publicKey: `ArrayBuffer`, secretKey: `ArrayBuffer`, blindingFactor: `ArrayBuffer` }
99+
fn bls_generate_blinded_g1_key(cx: FunctionContext) -> JsResult<JsObject> {
100+
bls_generate_keypair::<G1>(cx, Some(BLINDING_G1))
101+
}
102+
29103
/// Generate a BLS key pair where secret key `x` in Fp
30104
/// and public key `w` = `g2` ^ `x`
31105
/// `seed`: `ArrayBuffer` [opt]
32106
/// `return`: Object { publicKey: `ArrayBuffer`, secretKey: `ArrayBuffer` }
33107
fn bls_generate_g2_key(cx: FunctionContext) -> JsResult<JsObject> {
34-
bls_generate_keypair::<G2>(cx)
108+
bls_generate_keypair::<G2>(cx, None)
35109
}
36110

37111
/// Generate a BLS key pair where secret key `x` in Fp
38112
/// and public key `w` = `g1` ^ `x`
39113
/// `seed`: `ArrayBuffer` [opt]
40114
/// `return`: Object { publicKey: `ArrayBuffer`, secretKey: `ArrayBuffer` }
41115
fn bls_generate_g1_key(cx: FunctionContext) -> JsResult<JsObject> {
42-
bls_generate_keypair::<G1>(cx)
116+
bls_generate_keypair::<G1>(cx, None)
43117
}
44118

45-
fn bls_generate_keypair<G: CurveProjective<Engine = Bls12, Scalar = Fr> + SerDes>(mut cx: FunctionContext) -> JsResult<JsObject> {
119+
fn bls_generate_keypair<'a, 'b, G: CurveProjective<Engine = Bls12, Scalar = Fr> + SerDes>(mut cx: FunctionContext<'a>, blinded: Option<&'b [u8]>) -> JsResult<'a, JsObject> {
120+
let mut passed_seed = false;
46121
let seed = match cx.argument_opt(0) {
47122
Some(arg) => {
48123
let arg: Handle<JsArrayBuffer> = arg.downcast::<JsArrayBuffer>().or_throw(&mut cx)?;
49124
let seed_data = cx.borrow(&arg, |data| data.as_slice::<u8>());
125+
passed_seed = true;
50126
seed_data.to_vec()
51127
},
52128
None => {
@@ -61,6 +137,28 @@ fn bls_generate_keypair<G: CurveProjective<Engine = Bls12, Scalar = Fr> + SerDes
61137
let mut pk = G::one();
62138
pk.mul_assign(sk);
63139

140+
let r =
141+
match blinded {
142+
Some(g) => {
143+
let mut data = g.to_vec();
144+
let mut gg = g.clone();
145+
if passed_seed {
146+
data.extend_from_slice(seed.as_slice());
147+
} else {
148+
let mut rng = thread_rng();
149+
let mut blinding_factor = vec![0u8, 32];
150+
rng.fill_bytes(blinding_factor.as_mut_slice());
151+
data.extend_from_slice(blinding_factor.as_slice());
152+
}
153+
let mut blinding_g = G::deserialize(&mut gg, true).unwrap();
154+
let r = gen_sk(data.as_slice());
155+
blinding_g.mul_assign(r);
156+
pk.add_assign(&blinding_g);
157+
Some(r)
158+
},
159+
None => None
160+
};
161+
64162
let mut sk_bytes = Vec::new();
65163
let mut pk_bytes = Vec::new();
66164
sk.serialize(&mut sk_bytes, true).unwrap();
@@ -71,6 +169,12 @@ fn bls_generate_keypair<G: CurveProjective<Engine = Bls12, Scalar = Fr> + SerDes
71169
let result = JsObject::new(&mut cx);
72170
result.set(&mut cx, "publicKey", pk_array)?;
73171
result.set(&mut cx, "secretKey", sk_array)?;
172+
if let Some(rr) = r {
173+
let mut r_bytes = Vec::new();
174+
rr.serialize(&mut r_bytes, true).unwrap();
175+
let r_array = slice_to_js_array_buffer!(&r_bytes[..], cx);
176+
result.set(&mut cx, "blindingFactor", r_array)?;
177+
}
74178

75179
Ok(result)
76180
}
@@ -834,6 +938,8 @@ fn bitvector_to_revealed(data: &[u8]) -> BTreeSet<usize> {
834938
}
835939

836940
register_module!(mut m, {
941+
m.export_function("bls_generate_blinded_g2_key", bls_generate_blinded_g2_key)?;
942+
m.export_function("bls_generate_blinded_g1_key", bls_generate_blinded_g1_key)?;
837943
m.export_function("bls_generate_g2_key", bls_generate_g2_key)?;
838944
m.export_function("bls_generate_g1_key", bls_generate_g1_key)?;
839945
m.export_function("bls_secret_key_to_bbs_key", bls_secret_key_to_bbs_key)?;

src/bls12381.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
* limitations under the License.
1212
*/
1313

14-
import { BlsKeyPair } from "./types";
14+
import { BlsKeyPair, BlindedBlsKeyPair } from "./types";
1515

1616
/**
1717
* @ignore
@@ -33,6 +33,22 @@ export const generateBls12381G1KeyPair = (seed?: Uint8Array): BlsKeyPair => {
3333
};
3434
};
3535

36+
/**
37+
* Generates a blinded BLS12-381 key pair where the public key is a commitment in G1 to the private key
38+
* along with a further commitment of a blinding factor to the blinding factor generator point in G1
39+
* @param seed [Optional] To derive the key pair from
40+
*
41+
* @returns A BlindedBlsKeyPair
42+
*/
43+
export const generateBlindedBls12381G1KeyPair = (seed?: Uint8Array): BlindedBlsKeyPair => {
44+
const result = seed ? bbs.bls_generate_blinded_g1_key(seed?.buffer) : bbs.bls_generate_blinded_g1_key();
45+
return {
46+
publicKey: new Uint8Array(result.publicKey),
47+
secretKey: new Uint8Array(result.secretKey),
48+
blindingFactor: new Uint8Array(result.blindingFactor),
49+
};
50+
};
51+
3652
/**
3753
* Generates a BLS12-381 key pair where the public key is a commitment in G2
3854
* @param seed [Optional] To derive the key pair from
@@ -46,3 +62,19 @@ export const generateBls12381G2KeyPair = (seed?: Uint8Array): BlsKeyPair => {
4662
secretKey: new Uint8Array(result.secretKey),
4763
};
4864
};
65+
66+
/**
67+
* Generates a blinded BLS12-381 key pair where the public key is a commitment in G2 to the private key
68+
* along with a further commitment of a blinding factor to the blinding factor generator point in G2
69+
* @param seed [Optional] To derive the key pair from
70+
*
71+
* @returns A BlindedBlsKeyPair
72+
*/
73+
export const generateBlindedBls12381G2KeyPair = (seed?: Uint8Array): BlindedBlsKeyPair => {
74+
const result = seed ? bbs.bls_generate_blinded_g2_key(seed?.buffer) : bbs.bls_generate_blinded_g2_key();
75+
return {
76+
publicKey: new Uint8Array(result.publicKey),
77+
secretKey: new Uint8Array(result.secretKey),
78+
blindingFactor: new Uint8Array(result.blindingFactor),
79+
};
80+
};

src/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,12 @@
1111
* limitations under the License.
1212
*/
1313

14-
export { generateBls12381G1KeyPair, generateBls12381G2KeyPair } from "./bls12381";
14+
export {
15+
generateBls12381G1KeyPair,
16+
generateBls12381G2KeyPair,
17+
generateBlindedBls12381G1KeyPair,
18+
generateBlindedBls12381G2KeyPair,
19+
} from "./bls12381";
1520
export { bls12381toBbs } from "./bls12381toBbs";
1621
export {
1722
BBS_SIGNATURE_LENGTH,

src/types/BlsKeyPair.ts

+22
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ export const DEFAULT_BLS12381_G1_PUBLIC_KEY_LENGTH = 48;
2626
*/
2727
export const DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH = 96;
2828

29+
/**
30+
* Length of the blinding factor for BLS 12-381 keys
31+
*/
32+
export const BLS12381_BLINDING_FACTOR_LENGTH = 32;
33+
2934
/**
3035
* A BLS 12-381 key pair
3136
*/
@@ -39,3 +44,20 @@ export interface BlsKeyPair {
3944
*/
4045
readonly secretKey?: Uint8Array;
4146
}
47+
/**
48+
* A Blinded BLS 12-381 key pair
49+
*/
50+
export interface BlindedBlsKeyPair {
51+
/**
52+
* Raw public key value for the key pair
53+
*/
54+
readonly publicKey: Uint8Array;
55+
/**
56+
* Raw secret/private key value for the key pair
57+
*/
58+
readonly secretKey?: Uint8Array;
59+
/**
60+
* Blinding factor
61+
*/
62+
readonly blindingFactor: Uint8Array;
63+
}

src/types/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,11 @@ export { BbsVerifyProofRequest } from "./BbsVerifyProofRequest";
2222
export { BbsVerifyRequest } from "./BbsVerifyRequest";
2323
export {
2424
BlsKeyPair,
25+
BlindedBlsKeyPair,
2526
DEFAULT_BLS12381_PRIVATE_KEY_LENGTH,
2627
DEFAULT_BLS12381_G2_PUBLIC_KEY_LENGTH,
2728
DEFAULT_BLS12381_G1_PUBLIC_KEY_LENGTH,
29+
BLS12381_BLINDING_FACTOR_LENGTH,
2830
} from "./BlsKeyPair";
2931
export { Bls12381ToBbsRequest } from "./Bls12381ToBbsRequest";
3032
export { BlsBbsSignRequest } from "./BlsBbsSignRequest";

yarn.lock

+5
Original file line numberDiff line numberDiff line change
@@ -3948,6 +3948,11 @@ lodash.templatesettings@^4.0.0:
39483948
dependencies:
39493949
lodash._reinterpolate "^3.0.0"
39503950

3951+
3952+
version "4.17.15"
3953+
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
3954+
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
3955+
39513956
lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.2.1, lodash@^4.3.0:
39523957
version "4.17.19"
39533958
resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz#e48ddedbe30b3321783c5b4301fbd353bc1e4a4b"

0 commit comments

Comments
 (0)