@forgesworn/shamir-core 0.0.0-development → 1.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 +88 -0
- package/dist/index.js +29 -24
- package/package.json +2 -3
- package/src/index.ts +0 -264
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @forgesworn/shamir-core
|
|
2
|
+
|
|
3
|
+
GF(256) Shamir's Secret Sharing for TypeScript. Split a secret into threshold-of-n shares and reconstruct from any threshold-sized subset.
|
|
4
|
+
|
|
5
|
+
**Zero runtime dependencies.** Pure TypeScript, Web Crypto only.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @forgesworn/shamir-core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
import { splitSecret, reconstructSecret } from '@forgesworn/shamir-core';
|
|
17
|
+
|
|
18
|
+
// Split a 32-byte key into 5 shares, any 3 can reconstruct
|
|
19
|
+
const secret = crypto.getRandomValues(new Uint8Array(32));
|
|
20
|
+
const shares = splitSecret(secret, 3, 5);
|
|
21
|
+
|
|
22
|
+
// Reconstruct from any 3 shares
|
|
23
|
+
const recovered = reconstructSecret([shares[0], shares[2], shares[4]], 3);
|
|
24
|
+
// recovered is identical to secret
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## API
|
|
28
|
+
|
|
29
|
+
### `splitSecret(secret, threshold, shares)`
|
|
30
|
+
|
|
31
|
+
Split a secret into Shamir shares.
|
|
32
|
+
|
|
33
|
+
| Parameter | Type | Description |
|
|
34
|
+
|-----------|------|-------------|
|
|
35
|
+
| `secret` | `Uint8Array` | The secret bytes to split (any length) |
|
|
36
|
+
| `threshold` | `number` | Minimum shares needed to reconstruct (2--255) |
|
|
37
|
+
| `shares` | `number` | Total shares to create (threshold--255) |
|
|
38
|
+
|
|
39
|
+
Returns `ShamirShare[]`. Each share has `{ id, threshold, data }`.
|
|
40
|
+
|
|
41
|
+
### `reconstructSecret(shares, threshold)`
|
|
42
|
+
|
|
43
|
+
Reconstruct a secret from shares using Lagrange interpolation.
|
|
44
|
+
|
|
45
|
+
| Parameter | Type | Description |
|
|
46
|
+
|-----------|------|-------------|
|
|
47
|
+
| `shares` | `ShamirShare[]` | At least `threshold` shares |
|
|
48
|
+
| `threshold` | `number` | The threshold used during splitting |
|
|
49
|
+
|
|
50
|
+
Returns `Uint8Array` (the reconstructed secret).
|
|
51
|
+
|
|
52
|
+
Only the first `threshold` shares are used. Extra shares are ignored.
|
|
53
|
+
|
|
54
|
+
### `ShamirShare`
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface ShamirShare {
|
|
58
|
+
id: number; // 1--255 (GF(256) evaluation point)
|
|
59
|
+
threshold: number; // 2--255 (minimum shares to reconstruct)
|
|
60
|
+
data: Uint8Array; // Share data (same length as original secret)
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Error Classes
|
|
65
|
+
|
|
66
|
+
- `ShamirError` -- base class
|
|
67
|
+
- `ShamirValidationError` -- invalid parameters
|
|
68
|
+
- `ShamirCryptoError` -- internal crypto errors
|
|
69
|
+
|
|
70
|
+
## Why This Library
|
|
71
|
+
|
|
72
|
+
- **Zero dependencies.** No transitive supply chain. Only Web Crypto (`crypto.getRandomValues`).
|
|
73
|
+
- **GF(256) log/exp table lookup.** O(1) field multiplication, same polynomial as AES (0x11b).
|
|
74
|
+
- **Memory zeroing.** Polynomial coefficients are zeroed after use (defence-in-depth).
|
|
75
|
+
- **Strict validation.** Duplicate share IDs, threshold mismatches, and malformed inputs are caught with typed errors.
|
|
76
|
+
- **No secret length limit.** Split any size secret. The maths has no ceiling.
|
|
77
|
+
- **TypeScript-first.** Strict mode, `noUncheckedIndexedAccess`, full type declarations.
|
|
78
|
+
|
|
79
|
+
## Ecosystem
|
|
80
|
+
|
|
81
|
+
| Package | Purpose |
|
|
82
|
+
|---------|---------|
|
|
83
|
+
| [`@forgesworn/shamir-words`](https://github.com/forgesworn/shamir-words) | BIP-39 word encoding for shares (depends on this package) |
|
|
84
|
+
| [`dominion-protocol`](https://github.com/forgesworn/dominion) | Epoch-based encrypted access control (depends on this package) |
|
|
85
|
+
|
|
86
|
+
## Licence
|
|
87
|
+
|
|
88
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -112,8 +112,8 @@ export function splitSecret(secret, threshold, shares) {
|
|
|
112
112
|
if (!Number.isSafeInteger(threshold) || !Number.isSafeInteger(shares)) {
|
|
113
113
|
throw new ShamirValidationError('Threshold and shares must be safe integers');
|
|
114
114
|
}
|
|
115
|
-
if (threshold < 2) {
|
|
116
|
-
throw new ShamirValidationError('Threshold must be
|
|
115
|
+
if (threshold < 2 || threshold > 255) {
|
|
116
|
+
throw new ShamirValidationError('Threshold must be in [2, 255]');
|
|
117
117
|
}
|
|
118
118
|
if (shares < threshold) {
|
|
119
119
|
throw new ShamirValidationError('Number of shares must be >= threshold');
|
|
@@ -128,17 +128,21 @@ export function splitSecret(secret, threshold, shares) {
|
|
|
128
128
|
}
|
|
129
129
|
for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
|
|
130
130
|
const coeffs = new Uint8Array(threshold);
|
|
131
|
-
coeffs[0] = secret[byteIdx];
|
|
132
131
|
const rand = new Uint8Array(threshold - 1);
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
132
|
+
try {
|
|
133
|
+
coeffs[0] = secret[byteIdx];
|
|
134
|
+
crypto.getRandomValues(rand);
|
|
135
|
+
for (let j = 1; j < threshold; j++) {
|
|
136
|
+
coeffs[j] = rand[j - 1];
|
|
137
|
+
}
|
|
138
|
+
for (let i = 0; i < shares; i++) {
|
|
139
|
+
result[i].data[byteIdx] = evalPoly(coeffs, i + 1);
|
|
140
|
+
}
|
|
136
141
|
}
|
|
137
|
-
|
|
138
|
-
|
|
142
|
+
finally {
|
|
143
|
+
zeroBytes(coeffs);
|
|
144
|
+
zeroBytes(rand);
|
|
139
145
|
}
|
|
140
|
-
zeroBytes(coeffs);
|
|
141
|
-
zeroBytes(rand);
|
|
142
146
|
}
|
|
143
147
|
return result;
|
|
144
148
|
}
|
|
@@ -156,31 +160,32 @@ export function reconstructSecret(shares, threshold) {
|
|
|
156
160
|
if (!Array.isArray(shares) || shares.length < threshold) {
|
|
157
161
|
throw new ShamirValidationError(`Need at least ${threshold} shares, got ${Array.isArray(shares) ? shares.length : 0}`);
|
|
158
162
|
}
|
|
159
|
-
//
|
|
160
|
-
const
|
|
161
|
-
|
|
163
|
+
// Snapshot share properties to prevent TOCTOU via Proxy/getter objects
|
|
164
|
+
const raw = shares.slice(0, threshold);
|
|
165
|
+
const used = [];
|
|
162
166
|
const ids = new Set();
|
|
163
|
-
|
|
164
|
-
for (const share of used) {
|
|
167
|
+
for (const share of raw) {
|
|
165
168
|
if (!share || typeof share !== 'object') {
|
|
166
169
|
throw new ShamirValidationError('Each share must be an object with id and data properties');
|
|
167
170
|
}
|
|
168
|
-
|
|
171
|
+
// Snapshot once to prevent getter-based TOCTOU
|
|
172
|
+
const id = share.id;
|
|
173
|
+
const shareThreshold = share.threshold;
|
|
174
|
+
const data = share.data;
|
|
175
|
+
if (!Number.isInteger(id) || id < 1 || id > 255) {
|
|
169
176
|
throw new ShamirValidationError('Invalid share ID: must be an integer in [1, 255]');
|
|
170
177
|
}
|
|
171
|
-
if (!(
|
|
178
|
+
if (!(data instanceof Uint8Array)) {
|
|
172
179
|
throw new ShamirValidationError('Share data must be a Uint8Array');
|
|
173
180
|
}
|
|
174
|
-
if (ids.has(
|
|
181
|
+
if (ids.has(id)) {
|
|
175
182
|
throw new ShamirValidationError('Duplicate share IDs detected — each share must have a unique ID');
|
|
176
183
|
}
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
else if (share.threshold !== firstThreshold) {
|
|
181
|
-
throw new ShamirValidationError('Inconsistent threshold metadata across shares — shares may be from different splits');
|
|
184
|
+
if (Number.isInteger(shareThreshold) && shareThreshold !== threshold) {
|
|
185
|
+
throw new ShamirValidationError(`Share threshold (${shareThreshold}) does not match supplied threshold (${threshold})`);
|
|
182
186
|
}
|
|
183
|
-
ids.add(
|
|
187
|
+
ids.add(id);
|
|
188
|
+
used.push({ id, threshold: shareThreshold, data });
|
|
184
189
|
}
|
|
185
190
|
const secretLen = used[0].data.length;
|
|
186
191
|
if (secretLen === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@forgesworn/shamir-core",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "GF(256) Shamir's Secret Sharing — split and reconstruct secrets with threshold schemes",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -12,8 +12,7 @@
|
|
|
12
12
|
}
|
|
13
13
|
},
|
|
14
14
|
"files": [
|
|
15
|
-
"dist"
|
|
16
|
-
"src"
|
|
15
|
+
"dist"
|
|
17
16
|
],
|
|
18
17
|
"scripts": {
|
|
19
18
|
"build": "tsc",
|
package/src/index.ts
DELETED
|
@@ -1,264 +0,0 @@
|
|
|
1
|
-
// Shamir's Secret Sharing over GF(256)
|
|
2
|
-
// Split secrets into threshold-of-n shares using polynomial interpolation
|
|
3
|
-
// Zero runtime dependencies — only Web Crypto for randomness
|
|
4
|
-
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Errors
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
|
|
9
|
-
export class ShamirError extends Error {
|
|
10
|
-
constructor(message: string) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = 'ShamirError';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export class ShamirValidationError extends ShamirError {
|
|
17
|
-
constructor(message: string) {
|
|
18
|
-
super(message);
|
|
19
|
-
this.name = 'ShamirValidationError';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export class ShamirCryptoError extends ShamirError {
|
|
24
|
-
constructor(message: string) {
|
|
25
|
-
super(message);
|
|
26
|
-
this.name = 'ShamirCryptoError';
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// ---------------------------------------------------------------------------
|
|
31
|
-
// Types
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
|
|
34
|
-
export interface ShamirShare {
|
|
35
|
-
id: number; // 1-255 (the x coordinate in GF(256))
|
|
36
|
-
threshold: number; // 2-255 (minimum shares needed to reconstruct)
|
|
37
|
-
data: Uint8Array; // evaluated polynomial bytes
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ---------------------------------------------------------------------------
|
|
41
|
-
// GF(256) Arithmetic — irreducible polynomial 0x11b (same as AES)
|
|
42
|
-
// ---------------------------------------------------------------------------
|
|
43
|
-
|
|
44
|
-
const IRREDUCIBLE = 0x11b;
|
|
45
|
-
const GENERATOR = 0x03;
|
|
46
|
-
|
|
47
|
-
/** Log table: log_g(i) for i in [0..255]. LOG[0] is unused. */
|
|
48
|
-
const LOG = new Uint8Array(256);
|
|
49
|
-
/** Exp table: g^i for i in [0..255]. EXP[255] wraps to EXP[0]. */
|
|
50
|
-
const EXP = new Uint8Array(256);
|
|
51
|
-
|
|
52
|
-
/** Carryless multiplication used only during table construction */
|
|
53
|
-
function gf256MulSlow(a: number, b: number): number {
|
|
54
|
-
let result = 0;
|
|
55
|
-
let aa = a;
|
|
56
|
-
let bb = b;
|
|
57
|
-
while (bb > 0) {
|
|
58
|
-
if (bb & 1) result ^= aa;
|
|
59
|
-
aa <<= 1;
|
|
60
|
-
if (aa & 0x100) aa ^= IRREDUCIBLE;
|
|
61
|
-
bb >>= 1;
|
|
62
|
-
}
|
|
63
|
-
return result;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Build log/exp tables at module load time using generator 0x03
|
|
67
|
-
{
|
|
68
|
-
let val = 1;
|
|
69
|
-
for (let i = 0; i < 255; i++) {
|
|
70
|
-
EXP[i] = val;
|
|
71
|
-
LOG[val] = i;
|
|
72
|
-
val = gf256MulSlow(val, GENERATOR);
|
|
73
|
-
}
|
|
74
|
-
// Wrap: makes modular indexing simpler
|
|
75
|
-
EXP[255] = EXP[0]!;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/** Addition in GF(256) is XOR */
|
|
79
|
-
function gf256Add(a: number, b: number): number {
|
|
80
|
-
return a ^ b;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/** Multiplication in GF(256) using log/exp tables */
|
|
84
|
-
function gf256Mul(a: number, b: number): number {
|
|
85
|
-
if (a === 0 || b === 0) return 0;
|
|
86
|
-
return EXP[(LOG[a]! + LOG[b]!) % 255]!;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Multiplicative inverse in GF(256) */
|
|
90
|
-
function gf256Inv(a: number): number {
|
|
91
|
-
if (a === 0) throw new ShamirCryptoError('No inverse for zero in GF(256)');
|
|
92
|
-
return EXP[(255 - LOG[a]!) % 255]!;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ---------------------------------------------------------------------------
|
|
96
|
-
// Internal helpers
|
|
97
|
-
// ---------------------------------------------------------------------------
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Evaluate a polynomial at x in GF(256) using Horner's method.
|
|
101
|
-
* coeffs[0] is the constant term (the secret byte).
|
|
102
|
-
*/
|
|
103
|
-
function evalPoly(coeffs: Uint8Array, x: number): number {
|
|
104
|
-
let result = 0;
|
|
105
|
-
for (let i = coeffs.length - 1; i >= 0; i--) {
|
|
106
|
-
result = gf256Add(gf256Mul(result, x), coeffs[i]!);
|
|
107
|
-
}
|
|
108
|
-
return result;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Zero a byte array (defence-in-depth for secret material) */
|
|
112
|
-
function zeroBytes(arr: Uint8Array): void {
|
|
113
|
-
arr.fill(0);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// ---------------------------------------------------------------------------
|
|
117
|
-
// Shamir's Secret Sharing
|
|
118
|
-
// ---------------------------------------------------------------------------
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Split a secret into shares using Shamir's Secret Sharing over GF(256).
|
|
122
|
-
*
|
|
123
|
-
* @param secret The secret bytes to split (any length)
|
|
124
|
-
* @param threshold Minimum shares needed to reconstruct (>= 2)
|
|
125
|
-
* @param shares Total number of shares to create (>= threshold, <= 255)
|
|
126
|
-
* @returns Array of ShamirShare objects
|
|
127
|
-
*/
|
|
128
|
-
export function splitSecret(
|
|
129
|
-
secret: Uint8Array,
|
|
130
|
-
threshold: number,
|
|
131
|
-
shares: number,
|
|
132
|
-
): ShamirShare[] {
|
|
133
|
-
if (!(secret instanceof Uint8Array)) {
|
|
134
|
-
throw new ShamirValidationError('Secret must be a Uint8Array');
|
|
135
|
-
}
|
|
136
|
-
if (secret.length === 0) {
|
|
137
|
-
throw new ShamirValidationError('Secret must not be empty');
|
|
138
|
-
}
|
|
139
|
-
if (!Number.isSafeInteger(threshold) || !Number.isSafeInteger(shares)) {
|
|
140
|
-
throw new ShamirValidationError('Threshold and shares must be safe integers');
|
|
141
|
-
}
|
|
142
|
-
if (threshold < 2) {
|
|
143
|
-
throw new ShamirValidationError('Threshold must be at least 2');
|
|
144
|
-
}
|
|
145
|
-
if (shares < threshold) {
|
|
146
|
-
throw new ShamirValidationError('Number of shares must be >= threshold');
|
|
147
|
-
}
|
|
148
|
-
if (shares > 255) {
|
|
149
|
-
throw new ShamirValidationError('Number of shares must be <= 255');
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const secretLen = secret.length;
|
|
153
|
-
const result: ShamirShare[] = [];
|
|
154
|
-
|
|
155
|
-
for (let i = 0; i < shares; i++) {
|
|
156
|
-
result.push({ id: i + 1, threshold, data: new Uint8Array(secretLen) });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
|
|
160
|
-
const coeffs = new Uint8Array(threshold);
|
|
161
|
-
coeffs[0] = secret[byteIdx]!;
|
|
162
|
-
|
|
163
|
-
const rand = new Uint8Array(threshold - 1);
|
|
164
|
-
crypto.getRandomValues(rand);
|
|
165
|
-
for (let j = 1; j < threshold; j++) {
|
|
166
|
-
coeffs[j] = rand[j - 1]!;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
for (let i = 0; i < shares; i++) {
|
|
170
|
-
result[i]!.data[byteIdx] = evalPoly(coeffs, i + 1);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
zeroBytes(coeffs);
|
|
174
|
-
zeroBytes(rand);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return result;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Reconstruct a secret from shares using Lagrange interpolation over GF(256).
|
|
182
|
-
*
|
|
183
|
-
* @param shares Array of shares (at least `threshold` shares)
|
|
184
|
-
* @param threshold The threshold used during splitting
|
|
185
|
-
* @returns The reconstructed secret bytes
|
|
186
|
-
*/
|
|
187
|
-
export function reconstructSecret(
|
|
188
|
-
shares: ShamirShare[],
|
|
189
|
-
threshold: number,
|
|
190
|
-
): Uint8Array {
|
|
191
|
-
if (!Number.isSafeInteger(threshold) || threshold < 2) {
|
|
192
|
-
throw new ShamirValidationError('Threshold must be an integer >= 2');
|
|
193
|
-
}
|
|
194
|
-
if (!Array.isArray(shares) || shares.length < threshold) {
|
|
195
|
-
throw new ShamirValidationError(
|
|
196
|
-
`Need at least ${threshold} shares, got ${Array.isArray(shares) ? shares.length : 0}`,
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Use only the first `threshold` shares
|
|
201
|
-
const used = shares.slice(0, threshold);
|
|
202
|
-
|
|
203
|
-
// Validate share structure
|
|
204
|
-
const ids = new Set<number>();
|
|
205
|
-
let firstThreshold: number | undefined;
|
|
206
|
-
for (const share of used) {
|
|
207
|
-
if (!share || typeof share !== 'object') {
|
|
208
|
-
throw new ShamirValidationError('Each share must be an object with id and data properties');
|
|
209
|
-
}
|
|
210
|
-
if (!Number.isInteger(share.id) || share.id < 1 || share.id > 255) {
|
|
211
|
-
throw new ShamirValidationError('Invalid share ID: must be an integer in [1, 255]');
|
|
212
|
-
}
|
|
213
|
-
if (!(share.data instanceof Uint8Array)) {
|
|
214
|
-
throw new ShamirValidationError('Share data must be a Uint8Array');
|
|
215
|
-
}
|
|
216
|
-
if (ids.has(share.id)) {
|
|
217
|
-
throw new ShamirValidationError('Duplicate share IDs detected — each share must have a unique ID');
|
|
218
|
-
}
|
|
219
|
-
if (firstThreshold === undefined) {
|
|
220
|
-
firstThreshold = share.threshold;
|
|
221
|
-
} else if (share.threshold !== firstThreshold) {
|
|
222
|
-
throw new ShamirValidationError(
|
|
223
|
-
'Inconsistent threshold metadata across shares — shares may be from different splits',
|
|
224
|
-
);
|
|
225
|
-
}
|
|
226
|
-
ids.add(share.id);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
const secretLen = used[0]!.data.length;
|
|
230
|
-
if (secretLen === 0) {
|
|
231
|
-
throw new ShamirValidationError('Share data must not be empty');
|
|
232
|
-
}
|
|
233
|
-
for (const share of used) {
|
|
234
|
-
if (share.data.length !== secretLen) {
|
|
235
|
-
throw new ShamirValidationError('Inconsistent share lengths — shares may be from different secrets');
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
const result = new Uint8Array(secretLen);
|
|
240
|
-
|
|
241
|
-
// Lagrange interpolation at x = 0 for each byte position
|
|
242
|
-
for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
|
|
243
|
-
let value = 0;
|
|
244
|
-
|
|
245
|
-
for (let i = 0; i < threshold; i++) {
|
|
246
|
-
const xi = used[i]!.id;
|
|
247
|
-
const yi = used[i]!.data[byteIdx]!;
|
|
248
|
-
|
|
249
|
-
// Lagrange basis l_i(0) = product of x_j / (x_i ^ x_j) for j != i
|
|
250
|
-
let basis = 1;
|
|
251
|
-
for (let j = 0; j < threshold; j++) {
|
|
252
|
-
if (i === j) continue;
|
|
253
|
-
const xj = used[j]!.id;
|
|
254
|
-
basis = gf256Mul(basis, gf256Mul(xj, gf256Inv(gf256Add(xi, xj))));
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
value = gf256Add(value, gf256Mul(yi, basis));
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
result[byteIdx] = value;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
return result;
|
|
264
|
-
}
|