@forgesworn/shamir-core 0.0.0-development

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/LICENCE ADDED
@@ -0,0 +1,21 @@
1
+ MIT Licence
2
+
3
+ Copyright (c) 2026 ForgeSworn
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicence, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,31 @@
1
+ export declare class ShamirError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare class ShamirValidationError extends ShamirError {
5
+ constructor(message: string);
6
+ }
7
+ export declare class ShamirCryptoError extends ShamirError {
8
+ constructor(message: string);
9
+ }
10
+ export interface ShamirShare {
11
+ id: number;
12
+ threshold: number;
13
+ data: Uint8Array;
14
+ }
15
+ /**
16
+ * Split a secret into shares using Shamir's Secret Sharing over GF(256).
17
+ *
18
+ * @param secret The secret bytes to split (any length)
19
+ * @param threshold Minimum shares needed to reconstruct (>= 2)
20
+ * @param shares Total number of shares to create (>= threshold, <= 255)
21
+ * @returns Array of ShamirShare objects
22
+ */
23
+ export declare function splitSecret(secret: Uint8Array, threshold: number, shares: number): ShamirShare[];
24
+ /**
25
+ * Reconstruct a secret from shares using Lagrange interpolation over GF(256).
26
+ *
27
+ * @param shares Array of shares (at least `threshold` shares)
28
+ * @param threshold The threshold used during splitting
29
+ * @returns The reconstructed secret bytes
30
+ */
31
+ export declare function reconstructSecret(shares: ShamirShare[], threshold: number): Uint8Array;
package/dist/index.js ADDED
@@ -0,0 +1,214 @@
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
+ // Errors
6
+ // ---------------------------------------------------------------------------
7
+ export class ShamirError extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = 'ShamirError';
11
+ }
12
+ }
13
+ export class ShamirValidationError extends ShamirError {
14
+ constructor(message) {
15
+ super(message);
16
+ this.name = 'ShamirValidationError';
17
+ }
18
+ }
19
+ export class ShamirCryptoError extends ShamirError {
20
+ constructor(message) {
21
+ super(message);
22
+ this.name = 'ShamirCryptoError';
23
+ }
24
+ }
25
+ // ---------------------------------------------------------------------------
26
+ // GF(256) Arithmetic — irreducible polynomial 0x11b (same as AES)
27
+ // ---------------------------------------------------------------------------
28
+ const IRREDUCIBLE = 0x11b;
29
+ const GENERATOR = 0x03;
30
+ /** Log table: log_g(i) for i in [0..255]. LOG[0] is unused. */
31
+ const LOG = new Uint8Array(256);
32
+ /** Exp table: g^i for i in [0..255]. EXP[255] wraps to EXP[0]. */
33
+ const EXP = new Uint8Array(256);
34
+ /** Carryless multiplication used only during table construction */
35
+ function gf256MulSlow(a, b) {
36
+ let result = 0;
37
+ let aa = a;
38
+ let bb = b;
39
+ while (bb > 0) {
40
+ if (bb & 1)
41
+ result ^= aa;
42
+ aa <<= 1;
43
+ if (aa & 0x100)
44
+ aa ^= IRREDUCIBLE;
45
+ bb >>= 1;
46
+ }
47
+ return result;
48
+ }
49
+ // Build log/exp tables at module load time using generator 0x03
50
+ {
51
+ let val = 1;
52
+ for (let i = 0; i < 255; i++) {
53
+ EXP[i] = val;
54
+ LOG[val] = i;
55
+ val = gf256MulSlow(val, GENERATOR);
56
+ }
57
+ // Wrap: makes modular indexing simpler
58
+ EXP[255] = EXP[0];
59
+ }
60
+ /** Addition in GF(256) is XOR */
61
+ function gf256Add(a, b) {
62
+ return a ^ b;
63
+ }
64
+ /** Multiplication in GF(256) using log/exp tables */
65
+ function gf256Mul(a, b) {
66
+ if (a === 0 || b === 0)
67
+ return 0;
68
+ return EXP[(LOG[a] + LOG[b]) % 255];
69
+ }
70
+ /** Multiplicative inverse in GF(256) */
71
+ function gf256Inv(a) {
72
+ if (a === 0)
73
+ throw new ShamirCryptoError('No inverse for zero in GF(256)');
74
+ return EXP[(255 - LOG[a]) % 255];
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Internal helpers
78
+ // ---------------------------------------------------------------------------
79
+ /**
80
+ * Evaluate a polynomial at x in GF(256) using Horner's method.
81
+ * coeffs[0] is the constant term (the secret byte).
82
+ */
83
+ function evalPoly(coeffs, x) {
84
+ let result = 0;
85
+ for (let i = coeffs.length - 1; i >= 0; i--) {
86
+ result = gf256Add(gf256Mul(result, x), coeffs[i]);
87
+ }
88
+ return result;
89
+ }
90
+ /** Zero a byte array (defence-in-depth for secret material) */
91
+ function zeroBytes(arr) {
92
+ arr.fill(0);
93
+ }
94
+ // ---------------------------------------------------------------------------
95
+ // Shamir's Secret Sharing
96
+ // ---------------------------------------------------------------------------
97
+ /**
98
+ * Split a secret into shares using Shamir's Secret Sharing over GF(256).
99
+ *
100
+ * @param secret The secret bytes to split (any length)
101
+ * @param threshold Minimum shares needed to reconstruct (>= 2)
102
+ * @param shares Total number of shares to create (>= threshold, <= 255)
103
+ * @returns Array of ShamirShare objects
104
+ */
105
+ export function splitSecret(secret, threshold, shares) {
106
+ if (!(secret instanceof Uint8Array)) {
107
+ throw new ShamirValidationError('Secret must be a Uint8Array');
108
+ }
109
+ if (secret.length === 0) {
110
+ throw new ShamirValidationError('Secret must not be empty');
111
+ }
112
+ if (!Number.isSafeInteger(threshold) || !Number.isSafeInteger(shares)) {
113
+ throw new ShamirValidationError('Threshold and shares must be safe integers');
114
+ }
115
+ if (threshold < 2) {
116
+ throw new ShamirValidationError('Threshold must be at least 2');
117
+ }
118
+ if (shares < threshold) {
119
+ throw new ShamirValidationError('Number of shares must be >= threshold');
120
+ }
121
+ if (shares > 255) {
122
+ throw new ShamirValidationError('Number of shares must be <= 255');
123
+ }
124
+ const secretLen = secret.length;
125
+ const result = [];
126
+ for (let i = 0; i < shares; i++) {
127
+ result.push({ id: i + 1, threshold, data: new Uint8Array(secretLen) });
128
+ }
129
+ for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
130
+ const coeffs = new Uint8Array(threshold);
131
+ coeffs[0] = secret[byteIdx];
132
+ const rand = new Uint8Array(threshold - 1);
133
+ crypto.getRandomValues(rand);
134
+ for (let j = 1; j < threshold; j++) {
135
+ coeffs[j] = rand[j - 1];
136
+ }
137
+ for (let i = 0; i < shares; i++) {
138
+ result[i].data[byteIdx] = evalPoly(coeffs, i + 1);
139
+ }
140
+ zeroBytes(coeffs);
141
+ zeroBytes(rand);
142
+ }
143
+ return result;
144
+ }
145
+ /**
146
+ * Reconstruct a secret from shares using Lagrange interpolation over GF(256).
147
+ *
148
+ * @param shares Array of shares (at least `threshold` shares)
149
+ * @param threshold The threshold used during splitting
150
+ * @returns The reconstructed secret bytes
151
+ */
152
+ export function reconstructSecret(shares, threshold) {
153
+ if (!Number.isSafeInteger(threshold) || threshold < 2) {
154
+ throw new ShamirValidationError('Threshold must be an integer >= 2');
155
+ }
156
+ if (!Array.isArray(shares) || shares.length < threshold) {
157
+ throw new ShamirValidationError(`Need at least ${threshold} shares, got ${Array.isArray(shares) ? shares.length : 0}`);
158
+ }
159
+ // Use only the first `threshold` shares
160
+ const used = shares.slice(0, threshold);
161
+ // Validate share structure
162
+ const ids = new Set();
163
+ let firstThreshold;
164
+ for (const share of used) {
165
+ if (!share || typeof share !== 'object') {
166
+ throw new ShamirValidationError('Each share must be an object with id and data properties');
167
+ }
168
+ if (!Number.isInteger(share.id) || share.id < 1 || share.id > 255) {
169
+ throw new ShamirValidationError('Invalid share ID: must be an integer in [1, 255]');
170
+ }
171
+ if (!(share.data instanceof Uint8Array)) {
172
+ throw new ShamirValidationError('Share data must be a Uint8Array');
173
+ }
174
+ if (ids.has(share.id)) {
175
+ throw new ShamirValidationError('Duplicate share IDs detected — each share must have a unique ID');
176
+ }
177
+ if (firstThreshold === undefined) {
178
+ firstThreshold = share.threshold;
179
+ }
180
+ else if (share.threshold !== firstThreshold) {
181
+ throw new ShamirValidationError('Inconsistent threshold metadata across shares — shares may be from different splits');
182
+ }
183
+ ids.add(share.id);
184
+ }
185
+ const secretLen = used[0].data.length;
186
+ if (secretLen === 0) {
187
+ throw new ShamirValidationError('Share data must not be empty');
188
+ }
189
+ for (const share of used) {
190
+ if (share.data.length !== secretLen) {
191
+ throw new ShamirValidationError('Inconsistent share lengths — shares may be from different secrets');
192
+ }
193
+ }
194
+ const result = new Uint8Array(secretLen);
195
+ // Lagrange interpolation at x = 0 for each byte position
196
+ for (let byteIdx = 0; byteIdx < secretLen; byteIdx++) {
197
+ let value = 0;
198
+ for (let i = 0; i < threshold; i++) {
199
+ const xi = used[i].id;
200
+ const yi = used[i].data[byteIdx];
201
+ // Lagrange basis l_i(0) = product of x_j / (x_i ^ x_j) for j != i
202
+ let basis = 1;
203
+ for (let j = 0; j < threshold; j++) {
204
+ if (i === j)
205
+ continue;
206
+ const xj = used[j].id;
207
+ basis = gf256Mul(basis, gf256Mul(xj, gf256Inv(gf256Add(xi, xj))));
208
+ }
209
+ value = gf256Add(value, gf256Mul(yi, basis));
210
+ }
211
+ result[byteIdx] = value;
212
+ }
213
+ return result;
214
+ }
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@forgesworn/shamir-core",
3
+ "version": "0.0.0-development",
4
+ "description": "GF(256) Shamir's Secret Sharing — split and reconstruct secrets with threshold schemes",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "test": "vitest run",
21
+ "test:watch": "vitest",
22
+ "typecheck": "tsc --noEmit",
23
+ "clean": "rm -rf dist"
24
+ },
25
+ "keywords": [
26
+ "shamir",
27
+ "secret-sharing",
28
+ "gf256",
29
+ "threshold",
30
+ "cryptography",
31
+ "key-splitting",
32
+ "galois-field"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "sideEffects": false,
38
+ "author": "ForgeSworn (https://github.com/forgesworn)",
39
+ "contributors": [
40
+ "TheCryptoDonkey (https://github.com/TheCryptoDonkey)",
41
+ "decented (https://github.com/decented)"
42
+ ],
43
+ "license": "MIT",
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "homepage": "https://github.com/forgesworn/shamir-core",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "https://github.com/forgesworn/shamir-core.git"
51
+ },
52
+ "bugs": {
53
+ "url": "https://github.com/forgesworn/shamir-core/issues"
54
+ },
55
+ "devDependencies": {
56
+ "@semantic-release/changelog": "^6.0.3",
57
+ "@semantic-release/git": "^10.0.1",
58
+ "semantic-release": "^25.0.3",
59
+ "typescript": "^5.7.0",
60
+ "vitest": "^4.1.0"
61
+ },
62
+ "funding": {
63
+ "type": "lightning",
64
+ "url": "lightning:thedonkey@strike.me"
65
+ }
66
+ }
package/src/index.ts ADDED
@@ -0,0 +1,264 @@
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
+ }