-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathBBSSign.js
166 lines (147 loc) · 7.69 KB
/
BBSSign.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
/* BBS sign and related functions per the draft
- SK (REQUIRED), a non negative integer mod r outputted by the KeyGen
operation.
- PK (REQUIRED), an octet string of the form outputted by the SkToPk
operation provided the above SK as input.
- header (OPTIONAL), an octet string containing context and application
specific information. If not supplied, it defaults
to an empty string.
- messages (OPTIONAL), a vector of scalars. If not supplied, it defaults
to the empty array "()".
Parameters:
- ciphersuite_id, ASCII string. The unique ID of the ciphersuite.
- P1, fixed point of G1, defined by the ciphersuite.
Definitions:
- L, is the non-negative integer representing the number of messages to
be signed e.g length(messages). If no messages are supplied as an
input, the value of L MUST evaluate to zero (0).
Outputs:
- signature, a signature encoded as an octet string.
Precomputations:
1. msg_1, ..., msg_L = messages[1], ..., messages[L]
2. (Q_1, Q_2, H_1, ..., H_L) = create_generators(L+2)
Procedure:
1. dom_array = (PK, L, Q_1, Q_2, H_1, ..., H_L, ciphersuite_id, header)
2. dom_for_hash = encode_for_hash(dom_array)
3. if dom_for_hash is INVALID, return INVALID
4. domain = hash_to_scalar(dom_for_hash, 1)
5. e_s_for_hash = encode_for_hash((SK, domain, msg_1, ..., msg_L))
6. if e_s_for_hash is INVALID, return INVALID
7. (e, s) = hash_to_scalar(e_s_for_hash, 2)
8. B = P1 + Q_1 * s + Q_2 * domain + H_1 * msg_1 + ... + H_L * msg_L
9. A = B * (1 / (SK + e))
10. signature_octets = signature_to_octets(A, e, s)
11. return signature_octets
*/
import {bls12_381 as bls} from '@noble/curves/bls12-381';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';
import { i2osp, concat, os2ip, numberToBytesBE} from './myUtils.js';
import { encode_to_hash } from './BBSEncodeHash.js';
import { hash_to_scalar, messages_to_scalars, prepareGenerators } from './BBSGeneral.js';
const ciphersuite_id = "BBS_BLS12381G1_XMD:SHA-256_SSWU_RO_";
/**
*
* @param {scalar bigInt} SK
* @param {Uint8Array compressed G2 point raw bytes} PK
* @param {Uint8Array default 0 length} header
* @param {array of scalars (bigInt)} messages
*/
async function sign(SK, PK, header, messages, generators) {
// check that we have enough generators for the messages
if (messages.length > generators.H.length) {
throw new TypeError('Sign: not enough generators! string');
}
// elemTypes:"PublicKey", "NonNegInt", "GPoint", "Scalar", "PlainOctets", "CipherID", "ASCII"
// dom_array = (PK, L, Q_1, Q_2, H_1, ..., H_L, ciphersuite_id, header)
let L = messages.length;
let dom_array = [
{type: "PublicKey", value: PK}, {type: "NonNegInt", value: L},
{type: "GPoint", value: generators.Q1},
{type: "GPoint", value: generators.Q2},
];
for (let i = 0; i < L; i++) {
dom_array.push({type: "GPoint", value: generators.H[i]})
}
dom_array.push({type: "CipherID", value: ciphersuite_id});
dom_array.push({type: "PlainOctets", value: header});
// console.log(dom_array);
// dom_for_hash = encode_for_hash(dom_array)
let dom_for_hash = encode_to_hash(dom_array);
let dst = new TextEncoder().encode(ciphersuite_id + "H2S_");
// let dst = new TextEncoder().encode("BLS12381G1_XMD:SHA-256_SSWU_RO_");
// let dst = hexToBytes("4242535f424c53313233383147315f584d443a5348412d3235365f535357555f524f5f4d41505f4d53475f544f5f5343414c41525f41535f484153485f");
let [domain] = await hash_to_scalar(dom_for_hash, 1, dst);
// console.log(`domain: ${domain}`);
// e_s_for_hash = encode_for_hash((SK, domain, msg_1, ..., msg_L))
let valArray = [{type: "Scalar", value: SK}, {type: "Scalar", value: domain}];
for (let i = 0; i < L; i++) {
valArray.push({type: "Scalar", value: messages[i]});
}
// console.log(valArray);
let e_s_for_hash = encode_to_hash(valArray);
let [e, s] = await hash_to_scalar(e_s_for_hash, 2, dst);
// B = P1 + Q_1 * s + Q_2 * domain + H_1 * msg_1 + ... + H_L * msg_L
let B = generators.P1;
B = B.add(generators.Q1.multiply(s));
B = B.add(generators.Q2.multiply(domain));
for (let i = 0; i < messages.length; i++) {
B = B.add(generators.H[i].multiply(messages[i]));
}
// A = B * (1 / (SK + e)) # For this we need to work in Fr which noble-BLS12-381 provides
let denom = bls.Fr.add(bls.Fr.create(SK), bls.Fr.create(e));
let num = bls.Fr.inv(denom);
let A = B.multiply(num);
// signature_octets = signature_to_octets(A, e, s)
console.log("Computed A:")
console.log(bytesToHex(A.toRawBytes(true)));
console.log("Computed e:");
console.log(e.toString(16));
console.log("Computed s:");
console.log(s.toString(16));
return signature_to_octets(A, e, s);
}
const OCTET_SCALAR_LENGTH = 32;
function signature_to_octets(A, e, s) {
let octets = A.toRawBytes(true);
octets = concat(octets, numberToBytesBE(e, OCTET_SCALAR_LENGTH));
octets = concat(octets, numberToBytesBE(s, OCTET_SCALAR_LENGTH));
return octets;
}
// From draft
let test_msgs = [
hexToBytes("9872ad089e452c7b6e283dfac2a80d58e8d0ff71cc4d5e310a1debdda4a45f02"),
hexToBytes("87a8bd656d49ee07b8110e1d8fd4f1dcef6fb9bc368c492d9bc8c4f98a739ac6"),
hexToBytes("96012096adda3f13dd4adbe4eea481a4c4b5717932b73b00e31807d3c5894b90"),
hexToBytes("ac55fb33a75909edac8994829b250779298aa75d69324a365733f16c333fa943"),
hexToBytes("d183ddc6e2665aa4e2f088af9297b78c0d22b4290273db637ed33ff5cf703151"),
hexToBytes("515ae153e22aae04ad16f759e07237b43022cb1ced4c176e0999c6a8ba5817cc"),
hexToBytes("496694774c5604ab1b2544eababcf0f53278ff5040c1e77c811656e8220417a2"),
hexToBytes("77fe97eb97a1ebe2e81e4e3597a3ee740a66e9ef2412472c23364568523f8b91"),
hexToBytes("7372e9daa5ed31e6cd5c825eac1b855e84476a1d94932aa348e07b7320912416"),
hexToBytes("c344136d9ab02da4dd5908bbba913ae6f58c2cc844b802a6f811f5fb075f9b80")
];
let msg_scalars = await messages_to_scalars(test_msgs);
let L = 1;
let gens = await prepareGenerators(test_msgs.length); // Generate enough for alls
// console.log(gens);
let sk_bytes = hexToBytes("4a39afffd624d69e81808b2e84385cc80bf86adadf764e030caa46c231f2a8d7");
let pointPk = bls.G2.ProjectivePoint.fromPrivateKey(sk_bytes);
let pk_bytes = pointPk.toRawBytes(true);
let sk_scalar = os2ip(sk_bytes);
// console.log(pk_bytes.byteLength);
let header = hexToBytes("11223344556677889900aabbccddeeff");
let result = await sign(sk_scalar, pk_bytes, header, msg_scalars.slice(0, L), gens);
console.log("Complete signature single message:")
let resultString = bytesToHex(result);
console.log(resultString);
// From https://github.com/decentralized-identity/bbs-signature/blob/main/tooling/fixtures/fixture_data/bls12-381-sha-256/signature/signature001.json
let expected = "8e65ee0ea2d5f8ccb5fe03e1f985960dab25cfd1f4035cddd74f1b48d12fe24c621c5f9f56f23845ecee82ae207371ac39f37eb451b7a1ea7e41afb1436d28eef1016674ff320f70ab1537d3da8ed48d201594558a35a8503b12abbe02b5ed805baec5d20c263134934f1991dd4d3125";
console.log(`verified: ${resultString === expected}`);
L = 10; // Try with all 10 messages
result = await sign(sk_scalar, pk_bytes, header, msg_scalars.slice(0, L), gens);
console.log("Complete signature 10 messages:")
resultString = bytesToHex(result);
console.log(resultString);
// From https://github.com/decentralized-identity/bbs-signature/blob/main/tooling/fixtures/fixture_data/bls12-381-sha-256/signature/signature004.json
expected = "b13ae29b49e313b1c0983056e80cfb8d84a81985ca7488557aaf9b923f1e67994cab0e5ab05c75ffcf3fde1c23207ce5218dcfec42e9cc0063ff488100f89ba08296ced4923052e597279e1f775b157c55ed6b32ba777c3eec754bda4ab096e4147f2587248ba47b22226aee2aeafd85";
console.log(`verified: ${resultString === expected}`);