@bcts/shamir 1.0.0-alpha.5
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/LICENSE +48 -0
- package/README.md +11 -0
- package/dist/index.cjs +506 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +179 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +179 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.iife.js +509 -0
- package/dist/index.iife.js.map +1 -0
- package/dist/index.mjs +492 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +75 -0
- package/src/error.ts +51 -0
- package/src/hazmat.ts +287 -0
- package/src/index.ts +45 -0
- package/src/interpolate.ts +157 -0
- package/src/shamir.ts +185 -0
package/src/error.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Ported from bc-shamir-rust/src/error.rs
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Error types for Shamir secret sharing operations.
|
|
5
|
+
*/
|
|
6
|
+
export enum ShamirErrorType {
|
|
7
|
+
SecretTooLong = "SecretTooLong",
|
|
8
|
+
TooManyShares = "TooManyShares",
|
|
9
|
+
InterpolationFailure = "InterpolationFailure",
|
|
10
|
+
ChecksumFailure = "ChecksumFailure",
|
|
11
|
+
SecretTooShort = "SecretTooShort",
|
|
12
|
+
SecretNotEvenLen = "SecretNotEvenLen",
|
|
13
|
+
InvalidThreshold = "InvalidThreshold",
|
|
14
|
+
SharesUnequalLength = "SharesUnequalLength",
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Error class for Shamir secret sharing operations.
|
|
19
|
+
*/
|
|
20
|
+
export class ShamirError extends Error {
|
|
21
|
+
readonly type: ShamirErrorType;
|
|
22
|
+
|
|
23
|
+
constructor(type: ShamirErrorType, message?: string) {
|
|
24
|
+
super(message ?? ShamirError.defaultMessage(type));
|
|
25
|
+
this.type = type;
|
|
26
|
+
this.name = "ShamirError";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
private static defaultMessage(type: ShamirErrorType): string {
|
|
30
|
+
switch (type) {
|
|
31
|
+
case ShamirErrorType.SecretTooLong:
|
|
32
|
+
return "secret is too long";
|
|
33
|
+
case ShamirErrorType.TooManyShares:
|
|
34
|
+
return "too many shares";
|
|
35
|
+
case ShamirErrorType.InterpolationFailure:
|
|
36
|
+
return "interpolation failed";
|
|
37
|
+
case ShamirErrorType.ChecksumFailure:
|
|
38
|
+
return "checksum failure";
|
|
39
|
+
case ShamirErrorType.SecretTooShort:
|
|
40
|
+
return "secret is too short";
|
|
41
|
+
case ShamirErrorType.SecretNotEvenLen:
|
|
42
|
+
return "secret is not of even length";
|
|
43
|
+
case ShamirErrorType.InvalidThreshold:
|
|
44
|
+
return "invalid threshold";
|
|
45
|
+
case ShamirErrorType.SharesUnequalLength:
|
|
46
|
+
return "shares have unequal length";
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type ShamirResult<T> = T;
|
package/src/hazmat.ts
ADDED
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
// Ported from bc-shamir-rust/src/hazmat.rs
|
|
2
|
+
// GF(2^8) bitsliced polynomial operations for Shamir secret sharing
|
|
3
|
+
|
|
4
|
+
import { memzero } from "@bcts/crypto";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Convert an array of bytes into a bitsliced representation.
|
|
8
|
+
* Takes the first 32 bytes from x and produces 8 u32 values.
|
|
9
|
+
*
|
|
10
|
+
* @param r - Output array of 8 u32 values (bitsliced representation)
|
|
11
|
+
* @param x - Input array of at least 32 bytes
|
|
12
|
+
*/
|
|
13
|
+
export function bitslice(r: Uint32Array, x: Uint8Array): void {
|
|
14
|
+
if (x.length < 32) {
|
|
15
|
+
throw new Error("bitslice: input must be at least 32 bytes");
|
|
16
|
+
}
|
|
17
|
+
if (r.length !== 8) {
|
|
18
|
+
throw new Error("bitslice: output must have 8 elements");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
memzero(r);
|
|
22
|
+
|
|
23
|
+
for (let arrIdx = 0; arrIdx < 32; arrIdx++) {
|
|
24
|
+
const cur = x[arrIdx];
|
|
25
|
+
for (let bitIdx = 0; bitIdx < 8; bitIdx++) {
|
|
26
|
+
// r[bitIdx] |= ((cur & (1 << bitIdx)) >> bitIdx) << arrIdx
|
|
27
|
+
r[bitIdx] |= ((cur & (1 << bitIdx)) >>> bitIdx) << arrIdx;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Convert a bitsliced representation back to bytes.
|
|
34
|
+
*
|
|
35
|
+
* @param r - Output array of at least 32 bytes
|
|
36
|
+
* @param x - Input array of 8 u32 values (bitsliced representation)
|
|
37
|
+
*/
|
|
38
|
+
export function unbitslice(r: Uint8Array, x: Uint32Array): void {
|
|
39
|
+
if (r.length < 32) {
|
|
40
|
+
throw new Error("unbitslice: output must be at least 32 bytes");
|
|
41
|
+
}
|
|
42
|
+
if (x.length !== 8) {
|
|
43
|
+
throw new Error("unbitslice: input must have 8 elements");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
memzero(r.subarray(0, 32));
|
|
47
|
+
|
|
48
|
+
for (let bitIdx = 0; bitIdx < 8; bitIdx++) {
|
|
49
|
+
const cur = x[bitIdx];
|
|
50
|
+
for (let arrIdx = 0; arrIdx < 32; arrIdx++) {
|
|
51
|
+
// r[arrIdx] |= ((cur & (1 << arrIdx)) >> arrIdx) << bitIdx
|
|
52
|
+
r[arrIdx] |= ((cur & (1 << arrIdx)) >>> arrIdx) << bitIdx;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Set all 32 positions in a bitsliced array to the same byte value.
|
|
59
|
+
*
|
|
60
|
+
* @param r - Output array of 8 u32 values
|
|
61
|
+
* @param x - Byte value to set in all positions
|
|
62
|
+
*/
|
|
63
|
+
export function bitsliceSetall(r: Uint32Array, x: number): void {
|
|
64
|
+
if (r.length !== 8) {
|
|
65
|
+
throw new Error("bitsliceSetall: output must have 8 elements");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for (let idx = 0; idx < 8; idx++) {
|
|
69
|
+
// JavaScript needs special handling for the arithmetic right shift
|
|
70
|
+
// This mirrors: *r = (((((x as u32) & (1u32.wrapping_shl(idx as u32)))
|
|
71
|
+
// .wrapping_shl(31 - idx as u32)) as i32)
|
|
72
|
+
// .wrapping_shr(31)) as u32;
|
|
73
|
+
const bit = (x >>> idx) & 1;
|
|
74
|
+
r[idx] = bit === 1 ? 0xffffffff : 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Add (XOR) r with x and store the result in r.
|
|
80
|
+
* In GF(2^8), addition is XOR.
|
|
81
|
+
*
|
|
82
|
+
* @param r - First operand and result
|
|
83
|
+
* @param x - Second operand
|
|
84
|
+
*/
|
|
85
|
+
export function gf256Add(r: Uint32Array, x: Uint32Array): void {
|
|
86
|
+
if (r.length !== 8 || x.length !== 8) {
|
|
87
|
+
throw new Error("gf256Add: arrays must have 8 elements");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < 8; i++) {
|
|
91
|
+
r[i] ^= x[i];
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Safely multiply two bitsliced polynomials in GF(2^8) reduced by
|
|
97
|
+
* x^8 + x^4 + x^3 + x + 1. r and a may overlap, but overlapping of r
|
|
98
|
+
* and b will produce an incorrect result! If you need to square a polynomial
|
|
99
|
+
* use gf256Square instead.
|
|
100
|
+
*
|
|
101
|
+
* @param r - Result array (8 u32 values)
|
|
102
|
+
* @param a - First operand (may overlap with r)
|
|
103
|
+
* @param b - Second operand (must NOT overlap with r)
|
|
104
|
+
*/
|
|
105
|
+
export function gf256Mul(r: Uint32Array, a: Uint32Array, b: Uint32Array): void {
|
|
106
|
+
if (r.length !== 8 || a.length !== 8 || b.length !== 8) {
|
|
107
|
+
throw new Error("gf256Mul: arrays must have 8 elements");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Russian Peasant multiplication on two bitsliced polynomials
|
|
111
|
+
const a2 = new Uint32Array(a);
|
|
112
|
+
|
|
113
|
+
r[0] = a2[0] & b[0];
|
|
114
|
+
r[1] = a2[1] & b[0];
|
|
115
|
+
r[2] = a2[2] & b[0];
|
|
116
|
+
r[3] = a2[3] & b[0];
|
|
117
|
+
r[4] = a2[4] & b[0];
|
|
118
|
+
r[5] = a2[5] & b[0];
|
|
119
|
+
r[6] = a2[6] & b[0];
|
|
120
|
+
r[7] = a2[7] & b[0];
|
|
121
|
+
a2[0] ^= a2[7]; // reduce
|
|
122
|
+
a2[2] ^= a2[7];
|
|
123
|
+
a2[3] ^= a2[7];
|
|
124
|
+
|
|
125
|
+
r[0] ^= a2[7] & b[1]; // add
|
|
126
|
+
r[1] ^= a2[0] & b[1];
|
|
127
|
+
r[2] ^= a2[1] & b[1];
|
|
128
|
+
r[3] ^= a2[2] & b[1];
|
|
129
|
+
r[4] ^= a2[3] & b[1];
|
|
130
|
+
r[5] ^= a2[4] & b[1];
|
|
131
|
+
r[6] ^= a2[5] & b[1];
|
|
132
|
+
r[7] ^= a2[6] & b[1];
|
|
133
|
+
a2[7] ^= a2[6]; // reduce
|
|
134
|
+
a2[1] ^= a2[6];
|
|
135
|
+
a2[2] ^= a2[6];
|
|
136
|
+
|
|
137
|
+
r[0] ^= a2[6] & b[2]; // add
|
|
138
|
+
r[1] ^= a2[7] & b[2];
|
|
139
|
+
r[2] ^= a2[0] & b[2];
|
|
140
|
+
r[3] ^= a2[1] & b[2];
|
|
141
|
+
r[4] ^= a2[2] & b[2];
|
|
142
|
+
r[5] ^= a2[3] & b[2];
|
|
143
|
+
r[6] ^= a2[4] & b[2];
|
|
144
|
+
r[7] ^= a2[5] & b[2];
|
|
145
|
+
a2[6] ^= a2[5]; // reduce
|
|
146
|
+
a2[0] ^= a2[5];
|
|
147
|
+
a2[1] ^= a2[5];
|
|
148
|
+
|
|
149
|
+
r[0] ^= a2[5] & b[3]; // add
|
|
150
|
+
r[1] ^= a2[6] & b[3];
|
|
151
|
+
r[2] ^= a2[7] & b[3];
|
|
152
|
+
r[3] ^= a2[0] & b[3];
|
|
153
|
+
r[4] ^= a2[1] & b[3];
|
|
154
|
+
r[5] ^= a2[2] & b[3];
|
|
155
|
+
r[6] ^= a2[3] & b[3];
|
|
156
|
+
r[7] ^= a2[4] & b[3];
|
|
157
|
+
a2[5] ^= a2[4]; // reduce
|
|
158
|
+
a2[7] ^= a2[4];
|
|
159
|
+
a2[0] ^= a2[4];
|
|
160
|
+
|
|
161
|
+
r[0] ^= a2[4] & b[4]; // add
|
|
162
|
+
r[1] ^= a2[5] & b[4];
|
|
163
|
+
r[2] ^= a2[6] & b[4];
|
|
164
|
+
r[3] ^= a2[7] & b[4];
|
|
165
|
+
r[4] ^= a2[0] & b[4];
|
|
166
|
+
r[5] ^= a2[1] & b[4];
|
|
167
|
+
r[6] ^= a2[2] & b[4];
|
|
168
|
+
r[7] ^= a2[3] & b[4];
|
|
169
|
+
a2[4] ^= a2[3]; // reduce
|
|
170
|
+
a2[6] ^= a2[3];
|
|
171
|
+
a2[7] ^= a2[3];
|
|
172
|
+
|
|
173
|
+
r[0] ^= a2[3] & b[5]; // add
|
|
174
|
+
r[1] ^= a2[4] & b[5];
|
|
175
|
+
r[2] ^= a2[5] & b[5];
|
|
176
|
+
r[3] ^= a2[6] & b[5];
|
|
177
|
+
r[4] ^= a2[7] & b[5];
|
|
178
|
+
r[5] ^= a2[0] & b[5];
|
|
179
|
+
r[6] ^= a2[1] & b[5];
|
|
180
|
+
r[7] ^= a2[2] & b[5];
|
|
181
|
+
a2[3] ^= a2[2]; // reduce
|
|
182
|
+
a2[5] ^= a2[2];
|
|
183
|
+
a2[6] ^= a2[2];
|
|
184
|
+
|
|
185
|
+
r[0] ^= a2[2] & b[6]; // add
|
|
186
|
+
r[1] ^= a2[3] & b[6];
|
|
187
|
+
r[2] ^= a2[4] & b[6];
|
|
188
|
+
r[3] ^= a2[5] & b[6];
|
|
189
|
+
r[4] ^= a2[6] & b[6];
|
|
190
|
+
r[5] ^= a2[7] & b[6];
|
|
191
|
+
r[6] ^= a2[0] & b[6];
|
|
192
|
+
r[7] ^= a2[1] & b[6];
|
|
193
|
+
a2[2] ^= a2[1]; // reduce
|
|
194
|
+
a2[4] ^= a2[1];
|
|
195
|
+
a2[5] ^= a2[1];
|
|
196
|
+
|
|
197
|
+
r[0] ^= a2[1] & b[7]; // add
|
|
198
|
+
r[1] ^= a2[2] & b[7];
|
|
199
|
+
r[2] ^= a2[3] & b[7];
|
|
200
|
+
r[3] ^= a2[4] & b[7];
|
|
201
|
+
r[4] ^= a2[5] & b[7];
|
|
202
|
+
r[5] ^= a2[6] & b[7];
|
|
203
|
+
r[6] ^= a2[7] & b[7];
|
|
204
|
+
r[7] ^= a2[0] & b[7];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Square x in GF(2^8) and write the result to r.
|
|
209
|
+
* r and x may overlap.
|
|
210
|
+
*
|
|
211
|
+
* @param r - Result array (8 u32 values)
|
|
212
|
+
* @param x - Value to square
|
|
213
|
+
*/
|
|
214
|
+
export function gf256Square(r: Uint32Array, x: Uint32Array): void {
|
|
215
|
+
if (r.length !== 8 || x.length !== 8) {
|
|
216
|
+
throw new Error("gf256Square: arrays must have 8 elements");
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Use the Freshman's Dream rule to square the polynomial
|
|
220
|
+
// Assignments are done from 7 downto 0, because this allows
|
|
221
|
+
// in-place operation (e.g. gf256Square(r, r))
|
|
222
|
+
const r14 = x[7];
|
|
223
|
+
const r12 = x[6];
|
|
224
|
+
let r10 = x[5];
|
|
225
|
+
let r8 = x[4];
|
|
226
|
+
r[6] = x[3];
|
|
227
|
+
r[4] = x[2];
|
|
228
|
+
r[2] = x[1];
|
|
229
|
+
r[0] = x[0];
|
|
230
|
+
|
|
231
|
+
// Reduce with x^8 + x^4 + x^3 + x + 1 until order is less than 8
|
|
232
|
+
r[7] = r14; // r[7] was 0
|
|
233
|
+
r[6] ^= r14;
|
|
234
|
+
r10 ^= r14;
|
|
235
|
+
// Skip, because r13 is always 0
|
|
236
|
+
r[4] ^= r12;
|
|
237
|
+
r[5] = r12; // r[5] was 0
|
|
238
|
+
r[7] ^= r12;
|
|
239
|
+
r8 ^= r12;
|
|
240
|
+
// Skip, because r11 is always 0
|
|
241
|
+
r[2] ^= r10;
|
|
242
|
+
r[3] = r10; // r[3] was 0
|
|
243
|
+
r[5] ^= r10;
|
|
244
|
+
r[6] ^= r10;
|
|
245
|
+
r[1] = r14; // r[1] was 0
|
|
246
|
+
r[2] ^= r14; // Substitute r9 by r14 because they will always be equal
|
|
247
|
+
r[4] ^= r14;
|
|
248
|
+
r[5] ^= r14;
|
|
249
|
+
r[0] ^= r8;
|
|
250
|
+
r[1] ^= r8;
|
|
251
|
+
r[3] ^= r8;
|
|
252
|
+
r[4] ^= r8;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Invert x in GF(2^8) and write the result to r.
|
|
257
|
+
*
|
|
258
|
+
* @param r - Result array (8 u32 values)
|
|
259
|
+
* @param x - Value to invert (will be modified)
|
|
260
|
+
*/
|
|
261
|
+
export function gf256Inv(r: Uint32Array, x: Uint32Array): void {
|
|
262
|
+
if (r.length !== 8 || x.length !== 8) {
|
|
263
|
+
throw new Error("gf256Inv: arrays must have 8 elements");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const y = new Uint32Array(8);
|
|
267
|
+
const z = new Uint32Array(8);
|
|
268
|
+
|
|
269
|
+
gf256Square(y, x); // y = x^2
|
|
270
|
+
const y2 = new Uint32Array(y);
|
|
271
|
+
gf256Square(y, y2); // y = x^4
|
|
272
|
+
gf256Square(r, y); // r = x^8
|
|
273
|
+
gf256Mul(z, r, x); // z = x^9
|
|
274
|
+
const r2a = new Uint32Array(r);
|
|
275
|
+
gf256Square(r, r2a); // r = x^16
|
|
276
|
+
const r2b = new Uint32Array(r);
|
|
277
|
+
gf256Mul(r, r2b, z); // r = x^25
|
|
278
|
+
const r2c = new Uint32Array(r);
|
|
279
|
+
gf256Square(r, r2c); // r = x^50
|
|
280
|
+
gf256Square(z, r); // z = x^100
|
|
281
|
+
const z2 = new Uint32Array(z);
|
|
282
|
+
gf256Square(z, z2); // z = x^200
|
|
283
|
+
const r2d = new Uint32Array(r);
|
|
284
|
+
gf256Mul(r, r2d, z); // r = x^250
|
|
285
|
+
const r2e = new Uint32Array(r);
|
|
286
|
+
gf256Mul(r, r2e, y); // r = x^254
|
|
287
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// Blockchain Commons Shamir Secret Sharing
|
|
2
|
+
// Ported from bc-shamir-rust
|
|
3
|
+
//
|
|
4
|
+
// This is a pure TypeScript implementation of Shamir's Secret Sharing (SSS),
|
|
5
|
+
// a cryptographic technique in which a secret is divided into parts, called
|
|
6
|
+
// shares, in such a way that a threshold of several shares are needed to
|
|
7
|
+
// reconstruct the secret. The shares are distributed in a way that makes it
|
|
8
|
+
// impossible for an attacker to know anything about the secret without having
|
|
9
|
+
// a threshold of shares. If the number of shares is less than the threshold,
|
|
10
|
+
// then no information about the secret is revealed.
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* The minimum length of a secret.
|
|
14
|
+
*/
|
|
15
|
+
export const MIN_SECRET_LEN = 16;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The maximum length of a secret.
|
|
19
|
+
*/
|
|
20
|
+
export const MAX_SECRET_LEN = 32;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* The maximum number of shares that can be generated from a secret.
|
|
24
|
+
*/
|
|
25
|
+
export const MAX_SHARE_COUNT = 16;
|
|
26
|
+
|
|
27
|
+
// Error types
|
|
28
|
+
export { ShamirError, ShamirErrorType, type ShamirResult } from "./error.js";
|
|
29
|
+
|
|
30
|
+
// Main functions
|
|
31
|
+
export { splitSecret, recoverSecret } from "./shamir.js";
|
|
32
|
+
|
|
33
|
+
// Low-level operations (hazmat)
|
|
34
|
+
export {
|
|
35
|
+
bitslice,
|
|
36
|
+
unbitslice,
|
|
37
|
+
bitsliceSetall,
|
|
38
|
+
gf256Add,
|
|
39
|
+
gf256Mul,
|
|
40
|
+
gf256Square,
|
|
41
|
+
gf256Inv,
|
|
42
|
+
} from "./hazmat.js";
|
|
43
|
+
|
|
44
|
+
// Interpolation
|
|
45
|
+
export { interpolate } from "./interpolate.js";
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
// Ported from bc-shamir-rust/src/interpolate.rs
|
|
2
|
+
|
|
3
|
+
import { memzero, memzeroVecVecU8 } from "@bcts/crypto";
|
|
4
|
+
import { MAX_SECRET_LEN } from "./index.js";
|
|
5
|
+
import { bitslice, bitsliceSetall, gf256Add, gf256Inv, gf256Mul, unbitslice } from "./hazmat.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Calculate the lagrange basis coefficients for the lagrange polynomial
|
|
9
|
+
* defined by the x coordinates xc at the value x.
|
|
10
|
+
*
|
|
11
|
+
* After the function runs, the values array should hold data satisfying:
|
|
12
|
+
* --- (x-xc[j])
|
|
13
|
+
* values[i] = | | -------------
|
|
14
|
+
* j != i (xc[i]-xc[j])
|
|
15
|
+
*
|
|
16
|
+
* @param values - Output array for the lagrange basis values
|
|
17
|
+
* @param n - Number of points (length of the xc array, 0 < n <= 32)
|
|
18
|
+
* @param xc - Array of x components to use as interpolating points
|
|
19
|
+
* @param x - x coordinate to evaluate lagrange polynomials at
|
|
20
|
+
*/
|
|
21
|
+
function hazmatLagrangeBasis(values: Uint8Array, n: number, xc: Uint8Array, x: number): void {
|
|
22
|
+
// call the contents of xc [ x0 x1 x2 ... xn-1 ]
|
|
23
|
+
const xx = new Uint8Array(32 + 16);
|
|
24
|
+
const xSlice = new Uint32Array(8);
|
|
25
|
+
const lxi: Uint32Array[] = [];
|
|
26
|
+
for (let i = 0; i < n; i++) {
|
|
27
|
+
lxi.push(new Uint32Array(8));
|
|
28
|
+
}
|
|
29
|
+
const numerator = new Uint32Array(8);
|
|
30
|
+
const denominator = new Uint32Array(8);
|
|
31
|
+
const temp = new Uint32Array(8);
|
|
32
|
+
|
|
33
|
+
xx.set(xc.subarray(0, n), 0);
|
|
34
|
+
|
|
35
|
+
// xx now contains bitsliced [ x0 x1 x2 ... xn-1 0 0 0 ... ]
|
|
36
|
+
for (let i = 0; i < n; i++) {
|
|
37
|
+
// lxi = bitsliced [ xi xi+1 xi+2 ... xi-1 0 0 0 ]
|
|
38
|
+
bitslice(lxi[i], xx.subarray(i));
|
|
39
|
+
xx[i + n] = xx[i];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
bitsliceSetall(xSlice, x);
|
|
43
|
+
bitsliceSetall(numerator, 1);
|
|
44
|
+
bitsliceSetall(denominator, 1);
|
|
45
|
+
|
|
46
|
+
for (let i = 1; i < n; i++) {
|
|
47
|
+
temp.set(xSlice);
|
|
48
|
+
gf256Add(temp, lxi[i]);
|
|
49
|
+
// temp = [ x-xi+i x-xi+2 x-xi+3 ... x-xi x x x]
|
|
50
|
+
const numerator2 = new Uint32Array(numerator);
|
|
51
|
+
gf256Mul(numerator, numerator2, temp);
|
|
52
|
+
|
|
53
|
+
temp.set(lxi[0]);
|
|
54
|
+
gf256Add(temp, lxi[i]);
|
|
55
|
+
// temp = [x0-xi+1 x1-xi+1 x2-xi+2 ... xn-x0 0 0 0]
|
|
56
|
+
const denominator2 = new Uint32Array(denominator);
|
|
57
|
+
gf256Mul(denominator, denominator2, temp);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// At this stage the numerator contains
|
|
61
|
+
// [ num0 num1 num2 ... numn 0 0 0]
|
|
62
|
+
//
|
|
63
|
+
// where numi = prod(j, j!=i, x-xj )
|
|
64
|
+
//
|
|
65
|
+
// and the denominator contains
|
|
66
|
+
// [ d0 d1 d2 ... dn 0 0 0]
|
|
67
|
+
//
|
|
68
|
+
// where di = prod(j, j!=i, xi-xj)
|
|
69
|
+
|
|
70
|
+
gf256Inv(temp, denominator);
|
|
71
|
+
|
|
72
|
+
// gf256_inv uses exponentiation to calculate inverse, so the zeros end up
|
|
73
|
+
// remaining zeros.
|
|
74
|
+
|
|
75
|
+
// tmp = [ 1/d0 1/d1 1/d2 ... 1/dn 0 0 0]
|
|
76
|
+
|
|
77
|
+
const numerator2 = new Uint32Array(numerator);
|
|
78
|
+
gf256Mul(numerator, numerator2, temp);
|
|
79
|
+
|
|
80
|
+
// numerator now contains [ l_n_0(x) l_n_1(x) ... l_n_n-1(x) 0 0 0]
|
|
81
|
+
// use the xx array to unpack it
|
|
82
|
+
|
|
83
|
+
unbitslice(xx, numerator);
|
|
84
|
+
|
|
85
|
+
// copy results to output array
|
|
86
|
+
values.set(xx.subarray(0, n), 0);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Safely interpolate the polynomial going through
|
|
91
|
+
* the points (x0 [y0_0 y0_1 y0_2 ... y0_31]) , (x1 [y1_0 ...]), ...
|
|
92
|
+
*
|
|
93
|
+
* where
|
|
94
|
+
* xi points to [x0 x1 ... xn-1 ]
|
|
95
|
+
* y contains an array of pointers to 32-bit arrays of y values
|
|
96
|
+
* y contains [y0 y1 y2 ... yn-1]
|
|
97
|
+
* and each of the yi arrays contain [yi_0 yi_i ... yi_31].
|
|
98
|
+
*
|
|
99
|
+
* @param n - Number of points to interpolate
|
|
100
|
+
* @param xi - x coordinates for points (array of length n)
|
|
101
|
+
* @param yl - Length of y coordinate arrays
|
|
102
|
+
* @param yij - Array of n arrays of length yl
|
|
103
|
+
* @param x - Coordinate to interpolate at
|
|
104
|
+
* @returns The interpolated result of length yl
|
|
105
|
+
*/
|
|
106
|
+
export function interpolate(
|
|
107
|
+
n: number,
|
|
108
|
+
xi: Uint8Array,
|
|
109
|
+
yl: number,
|
|
110
|
+
yij: Uint8Array[],
|
|
111
|
+
x: number,
|
|
112
|
+
): Uint8Array {
|
|
113
|
+
// The hazmat gf256 implementation needs the y-coordinate data
|
|
114
|
+
// to be in 32-byte blocks
|
|
115
|
+
const y: Uint8Array[] = [];
|
|
116
|
+
for (let i = 0; i < n; i++) {
|
|
117
|
+
y.push(new Uint8Array(MAX_SECRET_LEN));
|
|
118
|
+
}
|
|
119
|
+
const values = new Uint8Array(MAX_SECRET_LEN);
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < n; i++) {
|
|
122
|
+
y[i].set(yij[i].subarray(0, yl), 0);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const lagrange = new Uint8Array(n);
|
|
126
|
+
const ySlice = new Uint32Array(8);
|
|
127
|
+
const resultSlice = new Uint32Array(8);
|
|
128
|
+
const temp = new Uint32Array(8);
|
|
129
|
+
|
|
130
|
+
hazmatLagrangeBasis(lagrange, n, xi, x);
|
|
131
|
+
|
|
132
|
+
bitsliceSetall(resultSlice, 0);
|
|
133
|
+
|
|
134
|
+
for (let i = 0; i < n; i++) {
|
|
135
|
+
bitslice(ySlice, y[i]);
|
|
136
|
+
bitsliceSetall(temp, lagrange[i]);
|
|
137
|
+
const temp2 = new Uint32Array(temp);
|
|
138
|
+
gf256Mul(temp, temp2, ySlice);
|
|
139
|
+
gf256Add(resultSlice, temp);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
unbitslice(values, resultSlice);
|
|
143
|
+
|
|
144
|
+
// the calling code is only expecting yl bytes back
|
|
145
|
+
const result = new Uint8Array(yl);
|
|
146
|
+
result.set(values.subarray(0, yl), 0);
|
|
147
|
+
|
|
148
|
+
// clean up stack
|
|
149
|
+
memzero(lagrange);
|
|
150
|
+
memzero(ySlice);
|
|
151
|
+
memzero(resultSlice);
|
|
152
|
+
memzero(temp);
|
|
153
|
+
memzeroVecVecU8(y);
|
|
154
|
+
memzero(values);
|
|
155
|
+
|
|
156
|
+
return result;
|
|
157
|
+
}
|