@theqrl/dilithium5 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/package.json +51 -0
- package/src/const.js +60 -0
- package/src/fips202.js +430 -0
- package/src/ntt.js +40 -0
- package/src/packing.js +185 -0
- package/src/poly.js +438 -0
- package/src/polyvec.js +217 -0
- package/src/reduce.js +19 -0
- package/src/rounding.js +35 -0
- package/src/sign.js +312 -0
- package/src/symmetric-shake.js +37 -0
package/src/polyvec.js
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Poly,
|
|
3
|
+
polyAdd,
|
|
4
|
+
polyCAddQ,
|
|
5
|
+
polyChkNorm,
|
|
6
|
+
polyDecompose,
|
|
7
|
+
polyInvNTTToMont,
|
|
8
|
+
polyMakeHint,
|
|
9
|
+
polyNTT,
|
|
10
|
+
polyPointWiseMontgomery,
|
|
11
|
+
polyPower2round,
|
|
12
|
+
polyReduce,
|
|
13
|
+
polyShiftL,
|
|
14
|
+
polySub,
|
|
15
|
+
polyUniform,
|
|
16
|
+
polyUniformEta,
|
|
17
|
+
polyUniformGamma1,
|
|
18
|
+
polyUseHint,
|
|
19
|
+
polyW1Pack,
|
|
20
|
+
} from './poly.js';
|
|
21
|
+
import { CRHBytes, K, L, PolyW1PackedBytes, SeedBytes } from './const.js';
|
|
22
|
+
|
|
23
|
+
export class PolyVecK {
|
|
24
|
+
constructor() {
|
|
25
|
+
this.vec = new Array(K).fill().map((_) => new Poly());
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export class PolyVecL {
|
|
30
|
+
constructor() {
|
|
31
|
+
this.vec = new Array(L).fill().map((_) => new Poly());
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
copy(polyVecL) {
|
|
35
|
+
for (let i = L - 1; i >= 0; i--) {
|
|
36
|
+
this.vec[i].copy(polyVecL.vec[i]);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function polyVecMatrixExpand(mat, rho) {
|
|
42
|
+
if (rho.length !== SeedBytes) {
|
|
43
|
+
throw new Error(`invalid rho length ${rho.length} | Expected length ${SeedBytes}`);
|
|
44
|
+
}
|
|
45
|
+
for (let i = 0; i < K; ++i) {
|
|
46
|
+
for (let j = 0; j < L; ++j) {
|
|
47
|
+
polyUniform(mat[i].vec[j], rho, (i << 8) + j);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function polyVecMatrixPointWiseMontgomery(t, mat, v) {
|
|
53
|
+
for (let i = 0; i < K; ++i) {
|
|
54
|
+
polyVecLPointWiseAccMontgomery(t.vec[i], mat[i], v);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function polyVecLUniformEta(v, seed, nonce) {
|
|
59
|
+
if (seed.length !== CRHBytes) {
|
|
60
|
+
throw new Error(`invalid seed length ${seed.length} | Expected length ${CRHBytes}`);
|
|
61
|
+
}
|
|
62
|
+
for (let i = 0; i < L; i++) {
|
|
63
|
+
polyUniformEta(v.vec[i], seed, nonce++);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function polyVecLUniformGamma1(v, seed, nonce) {
|
|
68
|
+
if (seed.length !== CRHBytes) {
|
|
69
|
+
throw new Error(`invalid seed length ${seed.length} | Expected length ${CRHBytes}`);
|
|
70
|
+
}
|
|
71
|
+
for (let i = 0; i < L; i++) {
|
|
72
|
+
polyUniformGamma1(v.vec[i], seed, L * nonce + i);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function polyVecLReduce(v) {
|
|
77
|
+
for (let i = 0; i < L; i++) {
|
|
78
|
+
polyReduce(v.vec[i]);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function polyVecLAdd(w, u, v) {
|
|
83
|
+
for (let i = 0; i < L; ++i) {
|
|
84
|
+
polyAdd(w.vec[i], u.vec[i], v.vec[i]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function polyVecLNTT(v) {
|
|
89
|
+
for (let i = 0; i < L; ++i) {
|
|
90
|
+
polyNTT(v.vec[i]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function polyVecLInvNTTToMont(v) {
|
|
95
|
+
for (let i = 0; i < L; ++i) {
|
|
96
|
+
polyInvNTTToMont(v.vec[i]);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function polyVecLPointWisePolyMontgomery(r, a, v) {
|
|
101
|
+
for (let i = 0; i < L; ++i) {
|
|
102
|
+
polyPointWiseMontgomery(r.vec[i], a, v.vec[i]);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function polyVecLPointWiseAccMontgomery(w, u, v) {
|
|
107
|
+
const t = new Poly();
|
|
108
|
+
polyPointWiseMontgomery(w, u.vec[0], v.vec[0]);
|
|
109
|
+
for (let i = 1; i < L; i++) {
|
|
110
|
+
polyPointWiseMontgomery(t, u.vec[i], v.vec[i]);
|
|
111
|
+
polyAdd(w, w, t);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function polyVecLChkNorm(v, bound) {
|
|
116
|
+
for (let i = 0; i < L; i++) {
|
|
117
|
+
if (polyChkNorm(v.vec[i], bound) !== 0) {
|
|
118
|
+
return 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function polyVecKUniformEta(v, seed, nonce) {
|
|
125
|
+
for (let i = 0; i < K; ++i) {
|
|
126
|
+
polyUniformEta(v.vec[i], seed, nonce++);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function polyVecKReduce(v) {
|
|
131
|
+
for (let i = 0; i < K; ++i) {
|
|
132
|
+
polyReduce(v.vec[i]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function polyVecKCAddQ(v) {
|
|
137
|
+
for (let i = 0; i < K; ++i) {
|
|
138
|
+
polyCAddQ(v.vec[i]);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function polyVecKAdd(w, u, v) {
|
|
143
|
+
for (let i = 0; i < K; ++i) {
|
|
144
|
+
polyAdd(w.vec[i], u.vec[i], v.vec[i]);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function polyVecKSub(w, u, v) {
|
|
149
|
+
for (let i = 0; i < K; ++i) {
|
|
150
|
+
polySub(w.vec[i], u.vec[i], v.vec[i]);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export function polyVecKShiftL(v) {
|
|
155
|
+
for (let i = 0; i < K; ++i) {
|
|
156
|
+
polyShiftL(v.vec[i]);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export function polyVecKNTT(v) {
|
|
161
|
+
for (let i = 0; i < K; i++) {
|
|
162
|
+
polyNTT(v.vec[i]);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export function polyVecKInvNTTToMont(v) {
|
|
167
|
+
for (let i = 0; i < K; i++) {
|
|
168
|
+
polyInvNTTToMont(v.vec[i]);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function polyVecKPointWisePolyMontgomery(r, a, v) {
|
|
173
|
+
for (let i = 0; i < K; i++) {
|
|
174
|
+
polyPointWiseMontgomery(r.vec[i], a, v.vec[i]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export function polyVecKChkNorm(v, bound) {
|
|
179
|
+
for (let i = 0; i < K; i++) {
|
|
180
|
+
if (polyChkNorm(v.vec[i], bound) !== 0) {
|
|
181
|
+
return 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return 0;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function polyVecKPower2round(v1, v0, v) {
|
|
188
|
+
for (let i = 0; i < K; i++) {
|
|
189
|
+
polyPower2round(v1.vec[i], v0.vec[i], v.vec[i]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function polyVecKDecompose(v1, v0, v) {
|
|
194
|
+
for (let i = 0; i < K; i++) {
|
|
195
|
+
polyDecompose(v1.vec[i], v0.vec[i], v.vec[i]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function polyVecKMakeHint(h, v0, v1) {
|
|
200
|
+
let s = 0;
|
|
201
|
+
for (let i = 0; i < K; i++) {
|
|
202
|
+
s += polyMakeHint(h.vec[i], v0.vec[i], v1.vec[i]);
|
|
203
|
+
}
|
|
204
|
+
return s;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function polyVecKUseHint(w, u, h) {
|
|
208
|
+
for (let i = 0; i < K; ++i) {
|
|
209
|
+
polyUseHint(w.vec[i], u.vec[i], h.vec[i]);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
export function polyVecKPackW1(r, w1) {
|
|
214
|
+
for (let i = 0; i < K; ++i) {
|
|
215
|
+
polyW1Pack(r, i * PolyW1PackedBytes, w1.vec[i]);
|
|
216
|
+
}
|
|
217
|
+
}
|
package/src/reduce.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Q, QInv } from './const.js';
|
|
2
|
+
|
|
3
|
+
export function montgomeryReduce(a) {
|
|
4
|
+
let t = BigInt.asIntN(32, BigInt.asIntN(64, BigInt.asIntN(32, a)) * BigInt(QInv));
|
|
5
|
+
t = BigInt.asIntN(32, (a - t * BigInt(Q)) >> 32n);
|
|
6
|
+
return t;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function reduce32(a) {
|
|
10
|
+
let t = (a + (1 << 22)) >> 23;
|
|
11
|
+
t = a - t * Q;
|
|
12
|
+
return t;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function cAddQ(a) {
|
|
16
|
+
let ar = a;
|
|
17
|
+
ar += (ar >> 31) & Q;
|
|
18
|
+
return ar;
|
|
19
|
+
}
|
package/src/rounding.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { D, GAMMA2, Q } from './const.js';
|
|
2
|
+
|
|
3
|
+
export function power2round(a0p, i, a) {
|
|
4
|
+
const a0 = a0p;
|
|
5
|
+
const a1 = (a + (1 << (D - 1)) - 1) >> D;
|
|
6
|
+
a0[i] = a - (a1 << D);
|
|
7
|
+
return a1;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function decompose(a0p, i, a) {
|
|
11
|
+
const a0 = a0p;
|
|
12
|
+
let a1 = (a + 127) >> 7;
|
|
13
|
+
a1 = (a1 * 1025 + (1 << 21)) >> 22;
|
|
14
|
+
a1 &= 15;
|
|
15
|
+
|
|
16
|
+
a0[i] = a - a1 * 2 * GAMMA2;
|
|
17
|
+
a0[i] -= (((Q - 1) / 2 - a0[i]) >> 31) & Q;
|
|
18
|
+
return a1;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function makeHint(a0, a1) {
|
|
22
|
+
if (a0 > GAMMA2 || a0 < -GAMMA2 || (a0 === -GAMMA2 && a1 !== 0)) return 1;
|
|
23
|
+
|
|
24
|
+
return 0;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function useHint(a, hint) {
|
|
28
|
+
const a0 = new Int32Array(1);
|
|
29
|
+
const a1 = decompose(a0, 0, a);
|
|
30
|
+
|
|
31
|
+
if (hint === 0) return a1;
|
|
32
|
+
|
|
33
|
+
if (a0[0] > 0) return (a1 + 1) & 15;
|
|
34
|
+
return (a1 - 1) & 15;
|
|
35
|
+
}
|
package/src/sign.js
ADDED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { createHash, randomBytes } from 'crypto';
|
|
2
|
+
import {
|
|
3
|
+
PolyVecK,
|
|
4
|
+
polyVecKAdd,
|
|
5
|
+
polyVecKCAddQ,
|
|
6
|
+
polyVecKChkNorm,
|
|
7
|
+
polyVecKDecompose,
|
|
8
|
+
polyVecKInvNTTToMont,
|
|
9
|
+
polyVecKMakeHint,
|
|
10
|
+
polyVecKNTT,
|
|
11
|
+
polyVecKPackW1,
|
|
12
|
+
polyVecKPointWisePolyMontgomery,
|
|
13
|
+
polyVecKPower2round,
|
|
14
|
+
polyVecKReduce,
|
|
15
|
+
polyVecKShiftL,
|
|
16
|
+
polyVecKSub,
|
|
17
|
+
polyVecKUniformEta,
|
|
18
|
+
polyVecKUseHint,
|
|
19
|
+
PolyVecL,
|
|
20
|
+
polyVecLAdd,
|
|
21
|
+
polyVecLChkNorm,
|
|
22
|
+
polyVecLInvNTTToMont,
|
|
23
|
+
polyVecLNTT,
|
|
24
|
+
polyVecLPointWisePolyMontgomery,
|
|
25
|
+
polyVecLReduce,
|
|
26
|
+
polyVecLUniformEta,
|
|
27
|
+
polyVecLUniformGamma1,
|
|
28
|
+
polyVecMatrixExpand,
|
|
29
|
+
polyVecMatrixPointWiseMontgomery,
|
|
30
|
+
} from './polyvec.js';
|
|
31
|
+
import {
|
|
32
|
+
BETA,
|
|
33
|
+
CRHBytes,
|
|
34
|
+
CryptoBytes,
|
|
35
|
+
CryptoPublicKeyBytes,
|
|
36
|
+
CryptoSecretKeyBytes,
|
|
37
|
+
GAMMA1,
|
|
38
|
+
GAMMA2,
|
|
39
|
+
K,
|
|
40
|
+
L,
|
|
41
|
+
OMEGA,
|
|
42
|
+
PolyW1PackedBytes,
|
|
43
|
+
SeedBytes,
|
|
44
|
+
} from './const.js';
|
|
45
|
+
import { Poly, polyChallenge, polyNTT } from './poly.js';
|
|
46
|
+
import { packPk, packSig, packSk, unpackPk, unpackSig, unpackSk } from './packing.js';
|
|
47
|
+
|
|
48
|
+
export function cryptoSignKeypair(passedSeed, pk, sk) {
|
|
49
|
+
try {
|
|
50
|
+
if (pk.length !== CryptoPublicKeyBytes) {
|
|
51
|
+
throw new Error(`invalid pk length ${pk.length} | Expected length ${CryptoPublicKeyBytes}`);
|
|
52
|
+
}
|
|
53
|
+
if (sk.length !== CryptoSecretKeyBytes) {
|
|
54
|
+
throw new Error(`invalid sk length ${sk.length} | Expected length ${CryptoSecretKeyBytes}`);
|
|
55
|
+
}
|
|
56
|
+
} catch (e) {
|
|
57
|
+
if (e instanceof TypeError) {
|
|
58
|
+
throw new Error(`pk/sk cannot be null`);
|
|
59
|
+
} else {
|
|
60
|
+
throw new Error(`${e.message}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// eslint-disable-next-line no-unused-vars
|
|
64
|
+
const mat = new Array(K).fill().map((_) => new PolyVecL());
|
|
65
|
+
const s1 = new PolyVecL();
|
|
66
|
+
const s2 = new PolyVecK();
|
|
67
|
+
const t1 = new PolyVecK();
|
|
68
|
+
const t0 = new PolyVecK();
|
|
69
|
+
|
|
70
|
+
// Get randomness for rho, rhoPrime and key
|
|
71
|
+
const seed = passedSeed || new Uint8Array(randomBytes(SeedBytes));
|
|
72
|
+
|
|
73
|
+
const state = createHash('shake256', { outputLength: 2 * SeedBytes + CRHBytes });
|
|
74
|
+
state.update(seed);
|
|
75
|
+
const seedBuf = state.digest();
|
|
76
|
+
const rho = seedBuf.slice(0, SeedBytes);
|
|
77
|
+
const rhoPrime = seedBuf.slice(SeedBytes, SeedBytes + CRHBytes);
|
|
78
|
+
const key = seedBuf.slice(SeedBytes + CRHBytes);
|
|
79
|
+
|
|
80
|
+
// Expand matrix
|
|
81
|
+
polyVecMatrixExpand(mat, rho);
|
|
82
|
+
|
|
83
|
+
// Sample short vectors s1 and s2
|
|
84
|
+
polyVecLUniformEta(s1, rhoPrime, 0);
|
|
85
|
+
polyVecKUniformEta(s2, rhoPrime, L);
|
|
86
|
+
|
|
87
|
+
// Matrix-vector multiplication
|
|
88
|
+
const s1hat = new PolyVecL();
|
|
89
|
+
s1hat.copy(s1);
|
|
90
|
+
polyVecLNTT(s1hat);
|
|
91
|
+
polyVecMatrixPointWiseMontgomery(t1, mat, s1hat);
|
|
92
|
+
polyVecKReduce(t1);
|
|
93
|
+
polyVecKInvNTTToMont(t1);
|
|
94
|
+
|
|
95
|
+
// Add error vector s2
|
|
96
|
+
polyVecKAdd(t1, t1, s2);
|
|
97
|
+
|
|
98
|
+
// Extract t1 and write public key
|
|
99
|
+
polyVecKCAddQ(t1);
|
|
100
|
+
polyVecKPower2round(t1, t0, t1);
|
|
101
|
+
packPk(pk, rho, t1);
|
|
102
|
+
|
|
103
|
+
// Compute H(rho, t1) and write secret key
|
|
104
|
+
const hasher = createHash('shake256', { outputLength: SeedBytes });
|
|
105
|
+
hasher.update(pk);
|
|
106
|
+
const tr = new Uint8Array(hasher.digest());
|
|
107
|
+
packSk(sk, rho, tr, key, t0, s1, s2);
|
|
108
|
+
|
|
109
|
+
return seed;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function cryptoSignSignature(sig, m, sk, randomizedSigning) {
|
|
113
|
+
if (sk.length !== CryptoSecretKeyBytes) {
|
|
114
|
+
throw new Error(`invalid sk length ${sk.length} | Expected length ${CryptoSecretKeyBytes}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const rho = new Uint8Array(SeedBytes);
|
|
118
|
+
const tr = new Uint8Array(SeedBytes);
|
|
119
|
+
const key = new Uint8Array(SeedBytes);
|
|
120
|
+
let rhoPrime = new Uint8Array(CRHBytes);
|
|
121
|
+
let nonce = 0;
|
|
122
|
+
let state = null;
|
|
123
|
+
const mat = Array(K)
|
|
124
|
+
.fill()
|
|
125
|
+
// eslint-disable-next-line no-unused-vars
|
|
126
|
+
.map((_) => new PolyVecL());
|
|
127
|
+
const s1 = new PolyVecL();
|
|
128
|
+
const y = new PolyVecL();
|
|
129
|
+
const z = new PolyVecL();
|
|
130
|
+
const t0 = new PolyVecK();
|
|
131
|
+
const s2 = new PolyVecK();
|
|
132
|
+
const w1 = new PolyVecK();
|
|
133
|
+
const w0 = new PolyVecK();
|
|
134
|
+
const h = new PolyVecK();
|
|
135
|
+
const cp = new Poly();
|
|
136
|
+
|
|
137
|
+
unpackSk(rho, tr, key, t0, s1, s2, sk);
|
|
138
|
+
|
|
139
|
+
state = createHash('shake256', { outputLength: CRHBytes });
|
|
140
|
+
state.update(tr);
|
|
141
|
+
state.update(m);
|
|
142
|
+
const mu = new Uint8Array(state.digest());
|
|
143
|
+
|
|
144
|
+
if (randomizedSigning) rhoPrime = new Uint8Array(randomBytes(CRHBytes));
|
|
145
|
+
else {
|
|
146
|
+
state = createHash('shake256', { outputLength: CRHBytes });
|
|
147
|
+
state.update(key);
|
|
148
|
+
state.update(mu);
|
|
149
|
+
rhoPrime.set(state.digest());
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
polyVecMatrixExpand(mat, rho);
|
|
153
|
+
polyVecLNTT(s1);
|
|
154
|
+
polyVecKNTT(s2);
|
|
155
|
+
polyVecKNTT(t0);
|
|
156
|
+
|
|
157
|
+
// eslint-disable-next-line no-constant-condition
|
|
158
|
+
while (true) {
|
|
159
|
+
polyVecLUniformGamma1(y, rhoPrime, nonce++);
|
|
160
|
+
// Matrix-vector multiplication
|
|
161
|
+
z.copy(y);
|
|
162
|
+
polyVecLNTT(z);
|
|
163
|
+
polyVecMatrixPointWiseMontgomery(w1, mat, z);
|
|
164
|
+
polyVecKReduce(w1);
|
|
165
|
+
polyVecKInvNTTToMont(w1);
|
|
166
|
+
|
|
167
|
+
// Decompose w and call the random oracle
|
|
168
|
+
polyVecKCAddQ(w1);
|
|
169
|
+
polyVecKDecompose(w1, w0, w1);
|
|
170
|
+
polyVecKPackW1(sig, w1);
|
|
171
|
+
|
|
172
|
+
state = createHash('shake256', { outputLength: SeedBytes });
|
|
173
|
+
state.update(mu);
|
|
174
|
+
state.update(sig.slice(0, K * PolyW1PackedBytes));
|
|
175
|
+
sig.set(state.digest());
|
|
176
|
+
|
|
177
|
+
polyChallenge(cp, sig);
|
|
178
|
+
polyNTT(cp);
|
|
179
|
+
|
|
180
|
+
// Compute z, reject if it reveals secret
|
|
181
|
+
polyVecLPointWisePolyMontgomery(z, cp, s1);
|
|
182
|
+
polyVecLInvNTTToMont(z);
|
|
183
|
+
polyVecLAdd(z, z, y);
|
|
184
|
+
polyVecLReduce(z);
|
|
185
|
+
if (polyVecLChkNorm(z, GAMMA1 - BETA) !== 0) {
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
polyVecKPointWisePolyMontgomery(h, cp, s2);
|
|
190
|
+
polyVecKInvNTTToMont(h);
|
|
191
|
+
polyVecKSub(w0, w0, h);
|
|
192
|
+
polyVecKReduce(w0);
|
|
193
|
+
if (polyVecKChkNorm(w0, GAMMA2 - BETA) !== 0) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
polyVecKPointWisePolyMontgomery(h, cp, t0);
|
|
198
|
+
polyVecKInvNTTToMont(h);
|
|
199
|
+
polyVecKReduce(h);
|
|
200
|
+
if (polyVecKChkNorm(h, GAMMA2) !== 0) {
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
polyVecKAdd(w0, w0, h);
|
|
205
|
+
const n = polyVecKMakeHint(h, w0, w1);
|
|
206
|
+
if (n > OMEGA) {
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
packSig(sig, sig, z, h);
|
|
211
|
+
return 0;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function cryptoSign(msg, sk, randomizedSigning) {
|
|
216
|
+
const sm = new Uint8Array(CryptoBytes + msg.length);
|
|
217
|
+
const mLen = msg.length;
|
|
218
|
+
for (let i = 0; i < mLen; ++i) {
|
|
219
|
+
sm[CryptoBytes + mLen - 1 - i] = msg[mLen - 1 - i];
|
|
220
|
+
}
|
|
221
|
+
const result = cryptoSignSignature(sm, msg, sk, randomizedSigning);
|
|
222
|
+
|
|
223
|
+
if (result !== 0) {
|
|
224
|
+
throw new Error('failed to sign');
|
|
225
|
+
}
|
|
226
|
+
return sm;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export function cryptoSignVerify(sig, m, pk) {
|
|
230
|
+
let i;
|
|
231
|
+
const buf = new Uint8Array(K * PolyW1PackedBytes);
|
|
232
|
+
const rho = new Uint8Array(SeedBytes);
|
|
233
|
+
const mu = new Uint8Array(CRHBytes);
|
|
234
|
+
const c = new Uint8Array(SeedBytes);
|
|
235
|
+
const c2 = new Uint8Array(SeedBytes);
|
|
236
|
+
const cp = new Poly();
|
|
237
|
+
// eslint-disable-next-line no-unused-vars
|
|
238
|
+
const mat = new Array(K).fill().map((_) => new PolyVecL());
|
|
239
|
+
const z = new PolyVecL();
|
|
240
|
+
const t1 = new PolyVecK();
|
|
241
|
+
const w1 = new PolyVecK();
|
|
242
|
+
const h = new PolyVecK();
|
|
243
|
+
|
|
244
|
+
if (sig.length !== CryptoBytes) {
|
|
245
|
+
return false;
|
|
246
|
+
}
|
|
247
|
+
if (pk.length !== CryptoPublicKeyBytes) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
unpackPk(rho, t1, pk);
|
|
252
|
+
if (unpackSig(c, z, h, sig)) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
if (polyVecLChkNorm(z, GAMMA1 - BETA)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* Compute CRH(H(rho, t1), msg) */
|
|
260
|
+
let state = createHash('shake256', { outputLength: SeedBytes });
|
|
261
|
+
state.update(pk.slice(0, CryptoPublicKeyBytes));
|
|
262
|
+
mu.set(state.digest());
|
|
263
|
+
|
|
264
|
+
state = createHash('shake256', { outputLength: CRHBytes });
|
|
265
|
+
state.update(mu.slice(0, SeedBytes));
|
|
266
|
+
state.update(m);
|
|
267
|
+
mu.set(state.digest());
|
|
268
|
+
|
|
269
|
+
/* Matrix-vector multiplication; compute Az - c2^dt1 */
|
|
270
|
+
polyChallenge(cp, c);
|
|
271
|
+
polyVecMatrixExpand(mat, rho);
|
|
272
|
+
|
|
273
|
+
polyVecLNTT(z);
|
|
274
|
+
polyVecMatrixPointWiseMontgomery(w1, mat, z);
|
|
275
|
+
|
|
276
|
+
polyNTT(cp);
|
|
277
|
+
polyVecKShiftL(t1);
|
|
278
|
+
polyVecKNTT(t1);
|
|
279
|
+
polyVecKPointWisePolyMontgomery(t1, cp, t1);
|
|
280
|
+
|
|
281
|
+
polyVecKSub(w1, w1, t1);
|
|
282
|
+
polyVecKReduce(w1);
|
|
283
|
+
polyVecKInvNTTToMont(w1);
|
|
284
|
+
|
|
285
|
+
/* Reconstruct w1 */
|
|
286
|
+
polyVecKCAddQ(w1);
|
|
287
|
+
polyVecKUseHint(w1, w1, h);
|
|
288
|
+
polyVecKPackW1(buf, w1);
|
|
289
|
+
|
|
290
|
+
/* Call random oracle and verify challenge */
|
|
291
|
+
state = createHash('shake256', { outputLength: SeedBytes });
|
|
292
|
+
state.update(mu);
|
|
293
|
+
state.update(buf);
|
|
294
|
+
c2.set(state.digest());
|
|
295
|
+
|
|
296
|
+
for (i = 0; i < SeedBytes; ++i) if (c[i] !== c2[i]) return false;
|
|
297
|
+
return true;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function cryptoSignOpen(sm, pk) {
|
|
301
|
+
if (sm.length < CryptoBytes) {
|
|
302
|
+
return undefined;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const sig = sm.slice(0, CryptoBytes);
|
|
306
|
+
const msg = sm.slice(CryptoBytes);
|
|
307
|
+
if (!cryptoSignVerify(sig, msg, pk)) {
|
|
308
|
+
return undefined;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return msg;
|
|
312
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import {
|
|
2
|
+
shake128Absorb,
|
|
3
|
+
shake128Finalize,
|
|
4
|
+
shake128Init,
|
|
5
|
+
shake256Absorb,
|
|
6
|
+
shake256Finalize,
|
|
7
|
+
shake256Init,
|
|
8
|
+
} from './fips202.js';
|
|
9
|
+
import { CRHBytes, SeedBytes } from './const.js';
|
|
10
|
+
|
|
11
|
+
export function dilithiumShake128StreamInit(state, seed, nonce) {
|
|
12
|
+
if (seed.length !== SeedBytes) {
|
|
13
|
+
throw new Error(`invalid seed length ${seed.length} | expected ${SeedBytes}`);
|
|
14
|
+
}
|
|
15
|
+
const t = new Uint8Array(2);
|
|
16
|
+
t[0] = nonce & 0xff;
|
|
17
|
+
t[1] = nonce >> 8;
|
|
18
|
+
|
|
19
|
+
shake128Init(state);
|
|
20
|
+
shake128Absorb(state, seed);
|
|
21
|
+
shake128Absorb(state, t);
|
|
22
|
+
shake128Finalize(state);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function dilithiumShake256StreamInit(state, seed, nonce) {
|
|
26
|
+
if (seed.length !== CRHBytes) {
|
|
27
|
+
throw new Error(`invalid seed length ${seed.length} | expected ${CRHBytes}`);
|
|
28
|
+
}
|
|
29
|
+
const t = new Uint8Array(2);
|
|
30
|
+
t[0] = nonce & 0xff;
|
|
31
|
+
t[1] = nonce >> 8;
|
|
32
|
+
|
|
33
|
+
shake256Init(state);
|
|
34
|
+
shake256Absorb(state, seed);
|
|
35
|
+
shake256Absorb(state, t);
|
|
36
|
+
shake256Finalize(state);
|
|
37
|
+
}
|