@idos-network/enclave 1.1.0

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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +50 -0
  3. package/dist/base-0xSNl4rW.d.mts +140 -0
  4. package/dist/base-0xSNl4rW.d.mts.map +1 -0
  5. package/dist/client-BSkUuHYm.mjs +1196 -0
  6. package/dist/client-BSkUuHYm.mjs.map +1 -0
  7. package/dist/client-Bu0aO5sT.d.mts +38 -0
  8. package/dist/client-Bu0aO5sT.d.mts.map +1 -0
  9. package/dist/index.d.mts +16 -0
  10. package/dist/index.d.mts.map +1 -0
  11. package/dist/index.mjs +2 -0
  12. package/dist/keys-RuC1M-_K.mjs +187 -0
  13. package/dist/keys-RuC1M-_K.mjs.map +1 -0
  14. package/dist/local.d.mts +96 -0
  15. package/dist/local.d.mts.map +1 -0
  16. package/dist/local.mjs +253 -0
  17. package/dist/local.mjs.map +1 -0
  18. package/dist/mpc/index.d.mts +3 -0
  19. package/dist/mpc/index.mjs +2 -0
  20. package/dist/types-DKsOwtWL.d.mts +73 -0
  21. package/dist/types-DKsOwtWL.d.mts.map +1 -0
  22. package/package.json +76 -0
  23. package/src/base.test.ts +137 -0
  24. package/src/base.ts +306 -0
  25. package/src/index.ts +3 -0
  26. package/src/keys.ts +10 -0
  27. package/src/local.test.ts +113 -0
  28. package/src/local.ts +495 -0
  29. package/src/mpc/api.ts +122 -0
  30. package/src/mpc/client.ts +411 -0
  31. package/src/mpc/engine-client.ts +144 -0
  32. package/src/mpc/generated/IdosContract.ts +92 -0
  33. package/src/mpc/index.ts +2 -0
  34. package/src/mpc/secretsharing/f256.ts +122 -0
  35. package/src/mpc/secretsharing/finite-field-element.ts +50 -0
  36. package/src/mpc/secretsharing/lagrange.ts +163 -0
  37. package/src/mpc/secretsharing/polynomial.ts +84 -0
  38. package/src/mpc/secretsharing/secret-share-factory.ts +30 -0
  39. package/src/mpc/secretsharing/secret-shares.ts +20 -0
  40. package/src/mpc/secretsharing/shamir-secret-shares.ts +164 -0
  41. package/src/mpc/secretsharing/xor-secret-shares.ts +68 -0
  42. package/src/mpc/types.ts +117 -0
  43. package/src/types.ts +37 -0
@@ -0,0 +1,122 @@
1
+ import type { FiniteFieldElement } from "./finite-field-element";
2
+ import { Polynomial } from "./polynomial";
3
+
4
+ /** Represents an element in GF(2^8) using the reduction polynomial x^8+x^4+x^3+x+1. */
5
+ export class F256 implements FiniteFieldElement<F256> {
6
+ readonly value: number;
7
+
8
+ private constructor(value: number) {
9
+ this.value = value;
10
+ }
11
+
12
+ /**
13
+ * The zero element.
14
+ */
15
+ static readonly ZERO: F256 = F256.createElement(0);
16
+
17
+ /**
18
+ * The one element.
19
+ */
20
+ static readonly ONE: F256 = F256.createElement(1);
21
+
22
+ /**
23
+ * Create a new field element with the given value.
24
+ * @param value the value
25
+ */
26
+ static createElement(value: number): F256 {
27
+ return new F256(value & 0xff);
28
+ }
29
+
30
+ /**
31
+ * Get the computation alphas for this field.
32
+ * @returns the computation alphas
33
+ */
34
+ static alphas(numNodes: number): readonly F256[] {
35
+ return Array.from({ length: numNodes }, (_value, key) => F256.createElement(key + 1));
36
+ }
37
+
38
+ /**
39
+ * Create a polynomial.
40
+ * @param coefficients the coefficients of the polynomial
41
+ */
42
+ static createPoly(coefficients: F256[]): Polynomial<F256> {
43
+ return Polynomial.create(coefficients, F256.ZERO);
44
+ }
45
+
46
+ /** @inheritDoc */
47
+ add(other: F256): F256 {
48
+ return Constants.elements[this.value ^ other.value];
49
+ }
50
+
51
+ /** @inheritDoc */
52
+ isEqualTo(other: F256): boolean {
53
+ return this.value === other.value;
54
+ }
55
+
56
+ /** @inheritDoc */
57
+ isOne(): boolean {
58
+ return this.value === 1;
59
+ }
60
+
61
+ /** @inheritDoc */
62
+ isZero(): boolean {
63
+ return this.value === 0;
64
+ }
65
+
66
+ /** @inheritDoc */
67
+ modInverse(): F256 {
68
+ return Constants.elements[Constants.multiplicativeInverse[this.value]];
69
+ }
70
+
71
+ /** @inheritDoc */
72
+ multiply(other: F256): F256 {
73
+ let a = this.value;
74
+ let b = other.value;
75
+ let m;
76
+ let p = 0;
77
+ for (let i = 0; i < 8; i++) {
78
+ p = (p ^ (-(b & 1) & a)) & 0xff;
79
+ m = -((a >> 7) & 1) & 0xff;
80
+ a = ((a << 1) ^ (0b100011011 & m)) & 0xff;
81
+ b = b >> 1;
82
+ }
83
+ return Constants.elements[p];
84
+ }
85
+
86
+ /** @inheritDoc */
87
+ negate(): F256 {
88
+ return this;
89
+ }
90
+
91
+ /** @inheritDoc */
92
+ squareRoot(): F256 {
93
+ throw new Error("Not implemented");
94
+ }
95
+
96
+ /** @inheritDoc */
97
+ subtract(other: F256): F256 {
98
+ return this.add(other);
99
+ }
100
+ }
101
+
102
+ const Constants = {
103
+ elements: Array.from({ length: 256 }, (_value, key) => F256.createElement(key)),
104
+ multiplicativeInverse: [
105
+ 0x00, 0x01, 0x8d, 0xf6, 0xcb, 0x52, 0x7b, 0xd1, 0xe8, 0x4f, 0x29, 0xc0, 0xb0, 0xe1, 0xe5, 0xc7,
106
+ 0x74, 0xb4, 0xaa, 0x4b, 0x99, 0x2b, 0x60, 0x5f, 0x58, 0x3f, 0xfd, 0xcc, 0xff, 0x40, 0xee, 0xb2,
107
+ 0x3a, 0x6e, 0x5a, 0xf1, 0x55, 0x4d, 0xa8, 0xc9, 0xc1, 0x0a, 0x98, 0x15, 0x30, 0x44, 0xa2, 0xc2,
108
+ 0x2c, 0x45, 0x92, 0x6c, 0xf3, 0x39, 0x66, 0x42, 0xf2, 0x35, 0x20, 0x6f, 0x77, 0xbb, 0x59, 0x19,
109
+ 0x1d, 0xfe, 0x37, 0x67, 0x2d, 0x31, 0xf5, 0x69, 0xa7, 0x64, 0xab, 0x13, 0x54, 0x25, 0xe9, 0x09,
110
+ 0xed, 0x5c, 0x05, 0xca, 0x4c, 0x24, 0x87, 0xbf, 0x18, 0x3e, 0x22, 0xf0, 0x51, 0xec, 0x61, 0x17,
111
+ 0x16, 0x5e, 0xaf, 0xd3, 0x49, 0xa6, 0x36, 0x43, 0xf4, 0x47, 0x91, 0xdf, 0x33, 0x93, 0x21, 0x3b,
112
+ 0x79, 0xb7, 0x97, 0x85, 0x10, 0xb5, 0xba, 0x3c, 0xb6, 0x70, 0xd0, 0x06, 0xa1, 0xfa, 0x81, 0x82,
113
+ 0x83, 0x7e, 0x7f, 0x80, 0x96, 0x73, 0xbe, 0x56, 0x9b, 0x9e, 0x95, 0xd9, 0xf7, 0x02, 0xb9, 0xa4,
114
+ 0xde, 0x6a, 0x32, 0x6d, 0xd8, 0x8a, 0x84, 0x72, 0x2a, 0x14, 0x9f, 0x88, 0xf9, 0xdc, 0x89, 0x9a,
115
+ 0xfb, 0x7c, 0x2e, 0xc3, 0x8f, 0xb8, 0x65, 0x48, 0x26, 0xc8, 0x12, 0x4a, 0xce, 0xe7, 0xd2, 0x62,
116
+ 0x0c, 0xe0, 0x1f, 0xef, 0x11, 0x75, 0x78, 0x71, 0xa5, 0x8e, 0x76, 0x3d, 0xbd, 0xbc, 0x86, 0x57,
117
+ 0x0b, 0x28, 0x2f, 0xa3, 0xda, 0xd4, 0xe4, 0x0f, 0xa9, 0x27, 0x53, 0x04, 0x1b, 0xfc, 0xac, 0xe6,
118
+ 0x7a, 0x07, 0xae, 0x63, 0xc5, 0xdb, 0xe2, 0xea, 0x94, 0x8b, 0xc4, 0xd5, 0x9d, 0xf8, 0x90, 0x6b,
119
+ 0xb1, 0x0d, 0xd6, 0xeb, 0xc6, 0x0e, 0xcf, 0xad, 0x08, 0x4e, 0xd7, 0xe3, 0x5d, 0x50, 0x1e, 0xb3,
120
+ 0x5b, 0x23, 0x38, 0x34, 0x68, 0x46, 0x03, 0x8c, 0xdd, 0x9c, 0x7d, 0xa0, 0xcd, 0x1a, 0x41, 0x1c,
121
+ ],
122
+ } as const;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * A finite field element.
3
+ *
4
+ * <p>Copied from <a href="https://gitlab.com/partisiablockchain/language/abi/zk-client"> zk-client</a>.
5
+ */
6
+ export interface FiniteFieldElement<T> {
7
+ /**
8
+ * Field element addition.
9
+ * @param other the right-hand side
10
+ */
11
+ readonly add: (other: T) => T;
12
+ /**
13
+ * Field element subtraction.
14
+ * @param other the right-hand side
15
+ */
16
+ readonly subtract: (other: T) => T;
17
+ /**
18
+ * Field element multiplication.
19
+ * @param other the right-hand side
20
+ */
21
+ readonly multiply: (other: T) => T;
22
+ /**
23
+ * Negate this element.
24
+ */
25
+ readonly negate: () => T;
26
+ /**
27
+ * Return the multiplicative inverse of this element.
28
+ */
29
+ readonly modInverse: () => T;
30
+ /**
31
+ * Get the square root of the element.
32
+ */
33
+ readonly squareRoot: () => T;
34
+ /**
35
+ * Whether this element is zero.
36
+ * @returns true if zero
37
+ */
38
+ readonly isZero: () => boolean;
39
+ /**
40
+ * Whether this element is one.
41
+ * @returns true if one
42
+ */
43
+ readonly isOne: () => boolean;
44
+ /**
45
+ * Whether this element is equal to the specified element.
46
+ * @param other the element to check
47
+ * @returns the equality
48
+ */
49
+ readonly isEqualTo: (other: T) => boolean;
50
+ }
@@ -0,0 +1,163 @@
1
+ import type { FiniteFieldElement } from "./finite-field-element";
2
+ import { Polynomial } from "./polynomial";
3
+
4
+ // File copied from <a href="https://gitlab.com/partisiablockchain/language/abi/zk-client"> zk-client</a>.
5
+
6
+ /**
7
+ * Try to interpolate a polynomial that passes through the supplied points.
8
+ *
9
+ * @param xs x-coordinates in the points
10
+ * @param ys y-coordinates in the points
11
+ * @param zero 0 element in the Finite Field.
12
+ * @param one 1 element in the Finite Field.
13
+ *
14
+ * @returns the interpolated polynomial or null if unable to interpolate
15
+ */
16
+ function interpolate<T extends FiniteFieldElement<T>>(
17
+ xs: readonly T[],
18
+ ys: T[],
19
+ zero: T,
20
+ one: T,
21
+ ): Polynomial<T> {
22
+ if (xs.length !== ys.length) {
23
+ throw new Error("xs and ys must be of same size");
24
+ }
25
+
26
+ if (xs.length === 0) {
27
+ throw new Error("xs and ys must have at least one element");
28
+ }
29
+
30
+ const n = xs.length;
31
+ const c: T[] = Array(n).fill(one);
32
+ c[0] = one;
33
+
34
+ for (let i = 0; i < n; i++) {
35
+ for (let j = i; j > 0; j--) {
36
+ c[j] = c[j - 1].subtract(c[j].multiply(xs[i]));
37
+ }
38
+ c[0] = c[0].multiply(xs[i].negate());
39
+ }
40
+
41
+ const coefficients: T[] = Array(n).fill(zero);
42
+ // Stryker disable next-line ArrayDeclaration
43
+ const tc: T[] = Array(n).fill(zero);
44
+ tc[n - 1] = one;
45
+ for (let i = 0; i < n; i++) {
46
+ let denominator = one;
47
+ for (let j = 0; j < n; j++) {
48
+ if (i !== j) {
49
+ denominator = denominator.multiply(xs[i].subtract(xs[j]));
50
+ }
51
+ }
52
+
53
+ const t: T = ys[i].multiply(denominator.modInverse());
54
+ coefficients[n - 1] = coefficients[n - 1].add(t.multiply(tc[n - 1]));
55
+
56
+ for (let j = n - 2; j >= 0; j--) {
57
+ tc[j] = c[j + 1].add(tc[j + 1].multiply(xs[i]));
58
+ coefficients[j] = coefficients[j].add(t.multiply(tc[j]));
59
+ }
60
+ }
61
+
62
+ return Polynomial.create(coefficients, zero);
63
+ }
64
+
65
+ /**
66
+ * Interpolate the minimal polynomial that passes through all the supplied points. And verify that
67
+ * the polynomial has a degree less that maximalDegree.
68
+ *
69
+ * @param xs x-coordinates in the points
70
+ * @param ys y-coordinates in the points
71
+ * @param zero 0 element in the Finite Field.
72
+ * @param one 1 element in the Finite Field.
73
+ * @param maximalDegree the expected maximal degree
74
+ * @return the interpolated polynomial
75
+ */
76
+ function interpolateCheckDegree<T extends FiniteFieldElement<T>>(
77
+ xs: readonly T[],
78
+ ys: T[],
79
+ maximalDegree: number,
80
+ zero: T,
81
+ one: T,
82
+ ): Polynomial<T> {
83
+ const poly = interpolate(xs, ys, zero, one);
84
+ if (poly.degree() > maximalDegree) {
85
+ throw new Error(
86
+ `Interpolated polynomial has too high degree. Expected maximal=${maximalDegree}, actual=${poly.degree()}`,
87
+ );
88
+ }
89
+ return poly;
90
+ }
91
+
92
+ /**
93
+ * Try to interpolate a polynomial that passes through at least <code>2 &sdot; maximalDegree + 1
94
+ * </code> of the supplied points.
95
+ *
96
+ * <p>Element lists ({@code xs} and {@code ys}) must have same size.
97
+ *
98
+ * @param xs x-coordinates in the points
99
+ * @param ys y-coordinates in the points
100
+ * @param maximalDegree the expected maximal degree
101
+ * @param zero 0 element in the Finite Field.
102
+ * @param one 1 element in the Finite Field.
103
+ *
104
+ * @returns the interpolated polynomial or undefined if unable to interpolate
105
+ */
106
+ function interpolateIfPossible<T extends FiniteFieldElement<T>>(
107
+ xs: readonly T[],
108
+ ys: T[],
109
+ maximalDegree: number,
110
+ zero: T,
111
+ one: T,
112
+ ): Polynomial<T> | undefined {
113
+ if (xs.length !== ys.length) {
114
+ throw new Error("xs and ys must be of same size");
115
+ }
116
+ return interpolateIfPossibleInner(xs, ys, maximalDegree, zero, one);
117
+ }
118
+
119
+ function interpolateIfPossibleInner<T extends FiniteFieldElement<T>>(
120
+ xs: readonly T[],
121
+ ys: T[],
122
+ maximalDegree: number,
123
+ zero: T,
124
+ one: T,
125
+ ): Polynomial<T> | undefined {
126
+ if (xs.length <= 2 * maximalDegree + 1) {
127
+ const interpolated = interpolate(xs, ys, zero, one);
128
+ if (interpolated.degree() > maximalDegree) {
129
+ return undefined;
130
+ }
131
+ return interpolated;
132
+ }
133
+ for (let removeIndex = 0; removeIndex < xs.length; removeIndex++) {
134
+ const interpolated = interpolateIfPossibleInner(
135
+ withoutIndex(xs, removeIndex),
136
+ withoutIndex(ys, removeIndex),
137
+ maximalDegree,
138
+ zero,
139
+ one,
140
+ );
141
+ if (interpolated !== undefined) {
142
+ return interpolated;
143
+ }
144
+ }
145
+ return undefined;
146
+ }
147
+
148
+ function withoutIndex<T>(values: readonly T[], removeIndex: number): T[] {
149
+ return values.filter((_value, index, _array) => index != removeIndex);
150
+ }
151
+
152
+ /**
153
+ * Utility for lagrange interpolation.
154
+ */
155
+ export const Lagrange: {
156
+ interpolate: typeof interpolate;
157
+ interpolateCheckDegree: typeof interpolateCheckDegree;
158
+ interpolateIfPossible: typeof interpolateIfPossible;
159
+ } = {
160
+ interpolate,
161
+ interpolateCheckDegree,
162
+ interpolateIfPossible,
163
+ };
@@ -0,0 +1,84 @@
1
+ import type { FiniteFieldElement } from "./finite-field-element";
2
+
3
+ /**
4
+ * Polynomials with coefficients in a finite field.
5
+ *
6
+ * <p>Copied from <a href="https://gitlab.com/partisiablockchain/language/abi/zk-client"> zk-client</a>.
7
+ */
8
+ export class Polynomial<T extends FiniteFieldElement<T>> {
9
+ private readonly coefficients: T[];
10
+
11
+ private constructor(coefficients: T[]) {
12
+ this.coefficients = coefficients;
13
+ }
14
+
15
+ /**
16
+ * Construct a polynomial from a set of coefficients. The degree of the resultant polynomial is
17
+ * assumed to be equal to the number of coefficients minus 1. The constant term of the polynomial
18
+ * is assumed to be stored in the first position of the coefficients.
19
+ *
20
+ * @param coefficients the coefficients of the polynomial
21
+ * @param zero zero element of the field
22
+ * @return the constructed polynomial
23
+ */
24
+ static create<T extends FiniteFieldElement<T>>(coefficients: T[], zero: T): Polynomial<T> {
25
+ return new Polynomial<T>(Polynomial.filterHighZeroes(coefficients, zero));
26
+ }
27
+
28
+ private static filterHighZeroes<T extends FiniteFieldElement<T>>(
29
+ coefficients: T[],
30
+ zero: T,
31
+ ): T[] {
32
+ for (let i = coefficients.length - 1; i >= 0; i--) {
33
+ if (!coefficients[i].isZero()) {
34
+ return coefficients.slice(0, i + 1);
35
+ }
36
+ }
37
+ return [zero];
38
+ }
39
+
40
+ /**
41
+ * Returns the coefficients of the polynomial.
42
+ *
43
+ * @return the coefficients of the polynomial
44
+ */
45
+ getCoefficients(): T[] {
46
+ return this.coefficients.slice();
47
+ }
48
+
49
+ /**
50
+ * Returns the degree of this polynomial.
51
+ *
52
+ * @return the degree of the polynomial
53
+ */
54
+ degree(): number {
55
+ return this.coefficients.length - 1;
56
+ }
57
+
58
+ /**
59
+ * Returns the constant term of the polynomial.
60
+ *
61
+ * @return the term stored on position 0 of the coefficients of this polynomial
62
+ */
63
+ getConstantTerm(): T {
64
+ return this.coefficients[0];
65
+ }
66
+
67
+ /**
68
+ * Evaluates this polynomial on a point. That is, if F is the polynomial and x a value, then this
69
+ * method returns F(x).
70
+ *
71
+ * @param point the point to evaluate this polynomial on
72
+ * @return F(point) where F is this polynomial
73
+ */
74
+ evaluate(point: T): T {
75
+ const degree = this.degree();
76
+
77
+ let result = this.coefficients[degree];
78
+ for (let i = degree - 1; i >= 0; --i) {
79
+ const current = this.coefficients[i];
80
+ result = current.add(point.multiply(result));
81
+ }
82
+ return result;
83
+ }
84
+ }
@@ -0,0 +1,30 @@
1
+ import type { SecretShares } from "./secret-shares";
2
+
3
+ /**
4
+ * Factory object for create {@link SecretShares}.
5
+ *
6
+ * <p>Based on the java implementation of the secret-sharing client.
7
+ *
8
+ * @param <ShareT> Type of {@link SecretShares} create by the factory object.
9
+ */
10
+ export interface SecretShareFactory<ShareT extends SecretShares> {
11
+ /**
12
+ * Create a new {@link SecretShares} from the given plaintext for the given amount of recipient
13
+ * nodes.
14
+ *
15
+ * <p>The factory is allowed to fail if it doesn't support the given amount of recipient nodes.
16
+ *
17
+ * @param numNodes Number of nodes to spread secret-shares across.
18
+ * @param plainText The plain text to be secret-shared. Not nullable.
19
+ * @returns Newly created {@link SecretShares}. Not nullable.
20
+ */
21
+ fromPlainText(numNodes: number, plainText: Buffer): ShareT;
22
+
23
+ /**
24
+ * Create a new {@link SecretShares} from the given shares.
25
+ *
26
+ * @param shares Shares to create from.
27
+ * @return Newly created {@link SecretShares}. Not nullable.
28
+ */
29
+ fromSharesBytes(shares: Array<Buffer | undefined>): ShareT;
30
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * All shares of a specific secure secret-sharing scheme.
3
+ *
4
+ * <p>Constructed either from a plain-text or from all shares of the plain-text. Can be converted
5
+ * into the individual shares, or into the plain-text.
6
+ *
7
+ * <p>Based on the java implementation of the secret-sharing client.
8
+ *
9
+ * @see <a href="https://en.wikipedia.org/wiki/Secret_sharing">Secret sharing, Wikipedia</a>
10
+ */
11
+ export interface SecretShares {
12
+ /** Get number of shares that this {@link SecretShares} have been split over. */
13
+ numShares(): number;
14
+
15
+ /** Get the secret-share for the given node index. */
16
+ getShareBytes(nodeIndex: number): Buffer;
17
+
18
+ /** Reconstruct plaintext from all the shares of the {@link SecretShares} object. */
19
+ reconstructPlainText(): Buffer;
20
+ }
@@ -0,0 +1,164 @@
1
+ import { randomBytes } from "crypto";
2
+ import { F256 } from "./f256";
3
+ import { Lagrange } from "./lagrange";
4
+ import type { SecretShareFactory } from "./secret-share-factory";
5
+ import type { SecretShares } from "./secret-shares";
6
+
7
+ /**
8
+ * Configuration for creating and reconstructing shamir secret shares.
9
+ *
10
+ * <p>The <code>numToReconstruct</code> value must be at least <code>numMalicious + 1</code> to be able
11
+ * to reconstruct.
12
+ *
13
+ * <p>If <code>numToReconstruct</code> is less than <code>2 * numMalicious + 1</code> then there must be
14
+ * some other way to guarantee that the shares are correct, e.g. through commitments on-chain.
15
+ */
16
+ export interface ShamirConfig {
17
+ /** Maximum number of malicious nodes. Depends on the threat model. */
18
+ numMalicious: number;
19
+ /** Total number of nodes to receive a share. */
20
+ numNodes: number;
21
+ /** Minimum number of shares needed to reconstruct a secret. */
22
+ numToReconstruct: number;
23
+ }
24
+
25
+ /** Factory for creating elements of {@link ShamirSecretShares}. */
26
+ export class ShamirFactory implements SecretShareFactory<ShamirSecretShares> {
27
+ private readonly shamirConfig: ShamirConfig;
28
+
29
+ /**
30
+ * Create the {@link ShamirFactory}.
31
+ *
32
+ * @param shamirConfig configuration for creating and reconstructing shamir secret shares.
33
+ */
34
+ constructor(shamirConfig: ShamirConfig) {
35
+ this.shamirConfig = shamirConfig;
36
+ }
37
+
38
+ fromPlainText(numNodes: number, plainText: Buffer): ShamirSecretShares {
39
+ if (numNodes != this.shamirConfig.numNodes) {
40
+ throw new Error(
41
+ `This shamir factory expects there to be ${this.shamirConfig.numNodes} nodes, but there was ${numNodes}.`,
42
+ );
43
+ }
44
+ const randomElements = randomBytes(plainText.length * this.shamirConfig.numMalicious);
45
+ const alphas = F256.alphas(this.shamirConfig.numNodes);
46
+ const shares: F256[][] = Array.from({ length: this.shamirConfig.numNodes }, () => []);
47
+ for (let i = 0; i < plainText.length; i++) {
48
+ const coefficients = [F256.createElement(plainText[i])];
49
+ for (let j = 0; j < this.shamirConfig.numMalicious; j++) {
50
+ coefficients.push(
51
+ F256.createElement(randomElements[this.shamirConfig.numMalicious * i + j]),
52
+ );
53
+ }
54
+ const poly = F256.createPoly(coefficients);
55
+ for (let j = 0; j < this.shamirConfig.numNodes; j++) {
56
+ shares[j].push(poly.evaluate(alphas[j]));
57
+ }
58
+ }
59
+
60
+ return new ShamirSecretShares(this.shamirConfig, shares);
61
+ }
62
+
63
+ fromSharesBytes(shares: Array<Buffer | undefined>): ShamirSecretShares {
64
+ const elementShares = shares.map((bytes) => {
65
+ if (bytes == undefined) {
66
+ return undefined;
67
+ }
68
+ const elements = [];
69
+ for (let i = 0; i < bytes.length; i++) {
70
+ elements.push(F256.createElement(bytes[i]));
71
+ }
72
+ return elements;
73
+ });
74
+ return new ShamirSecretShares(this.shamirConfig, elementShares);
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Secure secret-sharing scheme based on shamir-secret sharing.
80
+ *
81
+ * <p>Supported number of shares ({@code N}): {@code 4}
82
+ *
83
+ * <p>Based on the java implementation of the secret-sharing client.
84
+ *
85
+ * @see <a href="https://en.wikipedia.org/wiki/Shamir%27s_secret_sharing">Shamir's secret sharing,
86
+ * Wikipedia</a>
87
+ */
88
+ export class ShamirSecretShares implements SecretShares {
89
+ private readonly shares: Array<F256[] | undefined>;
90
+ private readonly shamirConfig: ShamirConfig;
91
+
92
+ constructor(shamirConfig: ShamirConfig, shares: Array<F256[] | undefined>) {
93
+ if (shares.length != shamirConfig.numNodes) {
94
+ throw new Error(`There must be ${shamirConfig.numNodes} nodes`);
95
+ }
96
+ const numReceivedShares = shares.filter((s) => s != undefined).length;
97
+ if (numReceivedShares < shamirConfig.numToReconstruct) {
98
+ throw new Error(
99
+ `Must have received at least ${shamirConfig.numToReconstruct} shares to reconstruct. Received ${numReceivedShares}.`,
100
+ );
101
+ }
102
+ this.shamirConfig = shamirConfig;
103
+ this.shares = shares;
104
+ }
105
+
106
+ getShareBytes(nodeIndex: number): Buffer {
107
+ const share = this.shares[nodeIndex];
108
+ if (share == undefined) {
109
+ throw new Error("Expected share to be defined");
110
+ }
111
+ return Buffer.from(share.map((field) => field.value));
112
+ }
113
+
114
+ numShares(): number {
115
+ return this.shamirConfig.numNodes;
116
+ }
117
+
118
+ reconstructPlainText(): Buffer {
119
+ const alphas = F256.alphas(this.shamirConfig.numNodes);
120
+ const definedAlphas: F256[] = [];
121
+ const definedShares: F256[][] = [];
122
+ for (let i = 0; i < this.shares.length; i++) {
123
+ const share = this.shares[i];
124
+ if (share != undefined) {
125
+ definedAlphas.push(alphas[i]);
126
+ definedShares.push(share);
127
+ }
128
+ }
129
+
130
+ const reconstructedElements = [];
131
+ const numElements = definedShares[0].length;
132
+ for (let i = 0; i < numElements; i++) {
133
+ const sharesOfIthElement = [];
134
+ for (let j = 0; j < definedShares.length; j++) {
135
+ sharesOfIthElement.push(definedShares[j][i]);
136
+ }
137
+ const interpolated = Lagrange.interpolateIfPossible(
138
+ definedAlphas,
139
+ sharesOfIthElement,
140
+ this.shamirConfig.numMalicious,
141
+ F256.ZERO,
142
+ F256.ONE,
143
+ );
144
+ if (interpolated === undefined) {
145
+ throw new Error("Unable to reconstruct secret");
146
+ }
147
+ reconstructedElements.push(interpolated.getConstantTerm().value);
148
+ }
149
+ return Buffer.from(reconstructedElements);
150
+ }
151
+
152
+ public static readonly FACTORY: SecretShareFactory<ShamirSecretShares> = new ShamirFactory({
153
+ numMalicious: 1,
154
+ numNodes: 4,
155
+ numToReconstruct: 2,
156
+ });
157
+ }
158
+
159
+ export function getRandomBytes(length: number): Buffer {
160
+ const array = new Uint8Array(length);
161
+ crypto.getRandomValues(array);
162
+
163
+ return Buffer.from(array);
164
+ }
@@ -0,0 +1,68 @@
1
+ import { randomBytes } from "crypto";
2
+ import type { SecretShareFactory } from "./secret-share-factory";
3
+ import type { SecretShares } from "./secret-shares";
4
+
5
+ /**
6
+ * Secure secret-sharing scheme based on XOR.
7
+ *
8
+ * <p>Supported number of shares (<code>N</code>): <code>2+</code>
9
+ *
10
+ * <p>Threshold for reconstruction: <code>N</code>
11
+ *
12
+ * <p>Based on the java implementation of the secret-sharing client.
13
+ *
14
+ * @see <a href="https://en.wikipedia.org/wiki/Secret_sharing">Secret sharing, Wikipedia</a>
15
+ */
16
+ export class XorSecretShares implements SecretShares {
17
+ private readonly shares: Buffer[];
18
+
19
+ /** The least number of shares supported by the secret sharing. */
20
+ public static readonly MIN_SHARES = 2;
21
+
22
+ constructor(shares: Buffer[]) {
23
+ if (shares.length < XorSecretShares.MIN_SHARES) {
24
+ throw new Error(
25
+ `XorSecretShares requires at least ${XorSecretShares.MIN_SHARES} shares, but specified only ${shares.length}`,
26
+ );
27
+ }
28
+ this.shares = shares;
29
+ }
30
+
31
+ getShareBytes(nodeIndex: number): Buffer {
32
+ return this.shares[nodeIndex];
33
+ }
34
+
35
+ numShares(): number {
36
+ return this.shares.length;
37
+ }
38
+
39
+ reconstructPlainText(): Buffer {
40
+ return xorByteArrays(this.shares[0].length, this.shares);
41
+ }
42
+
43
+ public static readonly FACTORY: SecretShareFactory<XorSecretShares> = {
44
+ fromPlainText(numNodes: number, plainText: Buffer): XorSecretShares {
45
+ const shares: Buffer[] = [];
46
+ for (let i = 0; i < numNodes - 1; i++) {
47
+ shares.push(randomBytes(plainText.length));
48
+ }
49
+ const finalShare = xorByteArrays(plainText.length, [...shares, plainText]);
50
+ shares.push(finalShare);
51
+ return new XorSecretShares(shares);
52
+ },
53
+ fromSharesBytes(shares: Array<Buffer | undefined>): XorSecretShares {
54
+ if (shares.filter((b) => b == undefined).length != 0) {
55
+ throw new Error("XorSecretShares must receive elements from all nodes");
56
+ }
57
+ return new XorSecretShares(shares as Buffer[]);
58
+ },
59
+ };
60
+ }
61
+
62
+ function xorByteArrays(length: number, manyBytes: Buffer[]) {
63
+ return Buffer.from(
64
+ manyBytes.reduce((previousValue, currentValue, _currentIndex, _array) => {
65
+ return Buffer.from(previousValue.map((value, index, _array) => value ^ currentValue[index]));
66
+ }, Buffer.alloc(length)),
67
+ );
68
+ }