shamir-mnemonic-ts 1.0.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.
- package/LICENSE +21 -0
- package/README.md +71 -0
- package/dist/cipher.d.ts +3 -0
- package/dist/cipher.d.ts.map +1 -0
- package/dist/cipher.js +91 -0
- package/dist/cipher.js.map +1 -0
- package/dist/constants.d.ts +41 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +45 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/recovery.d.ts +18 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +126 -0
- package/dist/recovery.js.map +1 -0
- package/dist/rs1024.d.ts +5 -0
- package/dist/rs1024.d.ts.map +1 -0
- package/dist/rs1024.js +50 -0
- package/dist/rs1024.js.map +1 -0
- package/dist/shamir.d.ts +36 -0
- package/dist/shamir.d.ts.map +1 -0
- package/dist/shamir.js +448 -0
- package/dist/shamir.js.map +1 -0
- package/dist/share.d.ts +39 -0
- package/dist/share.d.ts.map +1 -0
- package/dist/share.js +194 -0
- package/dist/share.js.map +1 -0
- package/dist/src/cipher.js +90 -0
- package/dist/src/constants.js +44 -0
- package/dist/src/index.js +22 -0
- package/dist/src/recovery.js +127 -0
- package/dist/src/rs1024.js +55 -0
- package/dist/src/shamir.js +486 -0
- package/dist/src/share.js +196 -0
- package/dist/src/utils.js +97 -0
- package/dist/src/wordlist.js +136 -0
- package/dist/test-manual.js +78 -0
- package/dist/utils.d.ts +7 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +39 -0
- package/dist/utils.js.map +1 -0
- package/dist/wordlist.d.ts +4 -0
- package/dist/wordlist.d.ts.map +1 -0
- package/dist/wordlist.js +1069 -0
- package/dist/wordlist.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 FRAG-MENT
|
|
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, sublicense, 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.
|
package/README.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Shamir Mnemonic TypeScript
|
|
2
|
+
|
|
3
|
+
TypeScript implementation of SLIP-0039 Shamir Secret Sharing for mnemonic seed phrases.
|
|
4
|
+
|
|
5
|
+
This is a complete conversion of the Python [python-shamir-mnemonic](https://github.com/trezor/python-shamir-mnemonic/tree/master) reference implementation to TypeScript for Node.js.
|
|
6
|
+
|
|
7
|
+
## Specification
|
|
8
|
+
|
|
9
|
+
See [https://github.com/satoshilabs/slips/blob/master/slip-0039.md](https://github.com/satoshilabs/slips/blob/master/slip-0039.md) for full specification.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
### From npm (for users)
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install shamir-mnemonic-ts
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Building
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm run build
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Testing
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npm test
|
|
29
|
+
npm run test:vectors
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
```typescript
|
|
35
|
+
import * as shamir from 'shamir-mnemonic-ts';
|
|
36
|
+
|
|
37
|
+
// Generate mnemonic shares
|
|
38
|
+
const masterSecret = Buffer.from('your-secret-here');
|
|
39
|
+
const mnemonics = shamir.generateMnemonics(
|
|
40
|
+
1, // group threshold
|
|
41
|
+
[[3, 5]], // (member threshold, member count) for each group
|
|
42
|
+
masterSecret,
|
|
43
|
+
Buffer.from('passphrase', 'utf8')
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
// Recover master secret
|
|
47
|
+
const recovered = shamir.combineMnemonics(
|
|
48
|
+
mnemonics[0].slice(0, 3), // any 3 of 5 shares
|
|
49
|
+
Buffer.from('passphrase', 'utf8')
|
|
50
|
+
);
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Structure
|
|
54
|
+
|
|
55
|
+
- `src/constants.ts` - SLIP-0039 constants
|
|
56
|
+
- `src/utils.ts` - Utility functions and error classes
|
|
57
|
+
- `src/wordlist.ts` - Wordlist management
|
|
58
|
+
- `src/rs1024.ts` - RS1024 checksum implementation
|
|
59
|
+
- `src/cipher.ts` - Feistel cipher for encryption/decryption
|
|
60
|
+
- `src/share.ts` - Share encoding/decoding
|
|
61
|
+
- `src/shamir.ts` - Core Shamir secret sharing algorithms
|
|
62
|
+
- `src/recovery.ts` - Interactive recovery state management
|
|
63
|
+
- `src/index.ts` - Public API exports
|
|
64
|
+
|
|
65
|
+
## Compatibility
|
|
66
|
+
|
|
67
|
+
This implementation is binary-compatible with the SLIP-0039 standard and produces/consumes the standard mnemonic format.
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
package/dist/cipher.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export declare function encrypt(masterSecret: Buffer, passphrase: Buffer, iterationExponent: number, identifier: number, extendable: boolean): Buffer;
|
|
2
|
+
export declare function decrypt(encryptedMasterSecret: Buffer, passphrase: Buffer, iterationExponent: number, identifier: number, extendable: boolean): Buffer;
|
|
3
|
+
//# sourceMappingURL=cipher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cipher.d.ts","sourceRoot":"","sources":["../src/cipher.ts"],"names":[],"mappings":"AA4CA,wBAAgB,OAAO,CACrB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,OAAO,GAClB,MAAM,CAmBR;AAED,wBAAgB,OAAO,CACrB,qBAAqB,EAAE,MAAM,EAC7B,UAAU,EAAE,MAAM,EAClB,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,OAAO,GAClB,MAAM,CAmBR"}
|
package/dist/cipher.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.encrypt = encrypt;
|
|
37
|
+
exports.decrypt = decrypt;
|
|
38
|
+
const crypto = __importStar(require("crypto"));
|
|
39
|
+
const constants_1 = require("./constants");
|
|
40
|
+
const utils_1 = require("./utils");
|
|
41
|
+
function _xor(a, b) {
|
|
42
|
+
const result = Buffer.alloc(a.length);
|
|
43
|
+
for (let i = 0; i < a.length; i++) {
|
|
44
|
+
result[i] = a[i] ^ b[i];
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function _roundFunction(i, passphrase, e, salt, r) {
|
|
49
|
+
/** The round function used internally by the Feistel cipher. */
|
|
50
|
+
return crypto.pbkdf2Sync(Buffer.concat([Buffer.from([i]), passphrase]), Buffer.concat([salt, r]), (constants_1.BASE_ITERATION_COUNT << e) / constants_1.ROUND_COUNT, r.length, 'sha256');
|
|
51
|
+
}
|
|
52
|
+
function _getSalt(identifier, extendable) {
|
|
53
|
+
if (extendable) {
|
|
54
|
+
return Buffer.alloc(0);
|
|
55
|
+
}
|
|
56
|
+
const identifierLen = (0, utils_1.bitsToBytes)(constants_1.ID_LENGTH_BITS);
|
|
57
|
+
const identifierBuf = Buffer.allocUnsafe(identifierLen);
|
|
58
|
+
identifierBuf.writeUIntBE(identifier, 0, identifierLen);
|
|
59
|
+
return Buffer.concat([constants_1.CUSTOMIZATION_STRING_ORIG, identifierBuf]);
|
|
60
|
+
}
|
|
61
|
+
function encrypt(masterSecret, passphrase, iterationExponent, identifier, extendable) {
|
|
62
|
+
if (masterSecret.length % 2 !== 0) {
|
|
63
|
+
throw new Error('The length of the master secret in bytes must be an even number.');
|
|
64
|
+
}
|
|
65
|
+
let l = masterSecret.slice(0, masterSecret.length / 2);
|
|
66
|
+
let r = masterSecret.slice(masterSecret.length / 2);
|
|
67
|
+
const salt = _getSalt(identifier, extendable);
|
|
68
|
+
for (let i = 0; i < constants_1.ROUND_COUNT; i++) {
|
|
69
|
+
const f = _roundFunction(i, passphrase, iterationExponent, salt, r);
|
|
70
|
+
const temp = l;
|
|
71
|
+
l = r;
|
|
72
|
+
r = _xor(temp, f);
|
|
73
|
+
}
|
|
74
|
+
return Buffer.concat([r, l]);
|
|
75
|
+
}
|
|
76
|
+
function decrypt(encryptedMasterSecret, passphrase, iterationExponent, identifier, extendable) {
|
|
77
|
+
if (encryptedMasterSecret.length % 2 !== 0) {
|
|
78
|
+
throw new Error('The length of the encrypted master secret in bytes must be an even number.');
|
|
79
|
+
}
|
|
80
|
+
let l = encryptedMasterSecret.slice(0, encryptedMasterSecret.length / 2);
|
|
81
|
+
let r = encryptedMasterSecret.slice(encryptedMasterSecret.length / 2);
|
|
82
|
+
const salt = _getSalt(identifier, extendable);
|
|
83
|
+
for (let i = constants_1.ROUND_COUNT - 1; i >= 0; i--) {
|
|
84
|
+
const f = _roundFunction(i, passphrase, iterationExponent, salt, r);
|
|
85
|
+
const temp = l;
|
|
86
|
+
l = r;
|
|
87
|
+
r = _xor(temp, f);
|
|
88
|
+
}
|
|
89
|
+
return Buffer.concat([r, l]);
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=cipher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cipher.js","sourceRoot":"","sources":["../src/cipher.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CA,0BAyBC;AAED,0BAyBC;AAhGD,+CAAiC;AACjC,2CAKqB;AACrB,mCAAsC;AAEtC,SAAS,IAAI,CAAC,CAAS,EAAE,CAAS;IAChC,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACtC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAClC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CACrB,CAAS,EACT,UAAkB,EAClB,CAAS,EACT,IAAY,EACZ,CAAS;IAET,gEAAgE;IAChE,OAAO,MAAM,CAAC,UAAU,CACtB,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,EAC7C,MAAM,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EACxB,CAAC,gCAAoB,IAAI,CAAC,CAAC,GAAG,uBAAW,EACzC,CAAC,CAAC,MAAM,EACR,QAAQ,CACT,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,UAAkB,EAAE,UAAmB;IACvD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACzB,CAAC;IACD,MAAM,aAAa,GAAG,IAAA,mBAAW,EAAC,0BAAc,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,MAAM,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;IACxD,aAAa,CAAC,WAAW,CAAC,UAAU,EAAE,CAAC,EAAE,aAAa,CAAC,CAAC;IACxD,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,qCAAyB,EAAE,aAAa,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,SAAgB,OAAO,CACrB,YAAoB,EACpB,UAAkB,EAClB,iBAAyB,EACzB,UAAkB,EAClB,UAAmB;IAEnB,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,kEAAkE,CACnE,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAW,YAAY,CAAC,KAAK,CAAC,CAAC,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/D,IAAI,CAAC,GAAW,YAAY,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC5D,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAE9C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,uBAAW,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,CAAC,CAAC;QACf,CAAC,GAAG,CAAC,CAAC;QACN,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,SAAgB,OAAO,CACrB,qBAA6B,EAC7B,UAAkB,EAClB,iBAAyB,EACzB,UAAkB,EAClB,UAAmB;IAEnB,IAAI,qBAAqB,CAAC,MAAM,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,4EAA4E,CAC7E,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,GAAW,qBAAqB,CAAC,KAAK,CAAC,CAAC,EAAE,qBAAqB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjF,IAAI,CAAC,GAAW,qBAAqB,CAAC,KAAK,CAAC,qBAAqB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC9E,MAAM,IAAI,GAAG,QAAQ,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;IAE9C,KAAK,IAAI,CAAC,GAAG,uBAAW,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC1C,MAAM,CAAC,GAAG,cAAc,CAAC,CAAC,EAAE,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QACpE,MAAM,IAAI,GAAG,CAAC,CAAC;QACf,CAAC,GAAG,CAAC,CAAC;QACN,CAAC,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const RADIX_BITS = 10;
|
|
2
|
+
/** The length of the radix in bits. */
|
|
3
|
+
export declare const RADIX: number;
|
|
4
|
+
/** The number of words in the wordlist. */
|
|
5
|
+
export declare const ID_LENGTH_BITS = 15;
|
|
6
|
+
/** The length of the random identifier in bits. */
|
|
7
|
+
export declare const EXTENDABLE_FLAG_LENGTH_BITS = 1;
|
|
8
|
+
/** The length of the extendable backup flag in bits. */
|
|
9
|
+
export declare const ITERATION_EXP_LENGTH_BITS = 4;
|
|
10
|
+
/** The length of the iteration exponent in bits. */
|
|
11
|
+
export declare const ID_EXP_LENGTH_WORDS: number;
|
|
12
|
+
/** The length of the random identifier, extendable backup flag and iteration exponent in words. */
|
|
13
|
+
export declare const MAX_SHARE_COUNT = 16;
|
|
14
|
+
/** The maximum number of shares that can be created. */
|
|
15
|
+
export declare const CHECKSUM_LENGTH_WORDS = 3;
|
|
16
|
+
/** The length of the RS1024 checksum in words. */
|
|
17
|
+
export declare const DIGEST_LENGTH_BYTES = 4;
|
|
18
|
+
/** The length of the digest of the shared secret in bytes. */
|
|
19
|
+
export declare const CUSTOMIZATION_STRING_ORIG: Buffer<ArrayBuffer>;
|
|
20
|
+
/** The customization string used in the RS1024 checksum and in the PBKDF2 salt for
|
|
21
|
+
shares _without_ the extendable backup flag. */
|
|
22
|
+
export declare const CUSTOMIZATION_STRING_EXTENDABLE: Buffer<ArrayBuffer>;
|
|
23
|
+
/** The customization string used in the RS1024 checksum for
|
|
24
|
+
shares _with_ the extendable backup flag. */
|
|
25
|
+
export declare const GROUP_PREFIX_LENGTH_WORDS: number;
|
|
26
|
+
/** The length of the prefix of the mnemonic that is common to a share group. */
|
|
27
|
+
export declare const METADATA_LENGTH_WORDS: number;
|
|
28
|
+
/** The length of the mnemonic in words without the share value. */
|
|
29
|
+
export declare const MIN_STRENGTH_BITS = 128;
|
|
30
|
+
/** The minimum allowed entropy of the master secret. */
|
|
31
|
+
export declare const MIN_MNEMONIC_LENGTH_WORDS: number;
|
|
32
|
+
/** The minimum allowed length of the mnemonic in words. */
|
|
33
|
+
export declare const BASE_ITERATION_COUNT = 10000;
|
|
34
|
+
/** The minimum number of iterations to use in PBKDF2. */
|
|
35
|
+
export declare const ROUND_COUNT = 4;
|
|
36
|
+
/** The number of rounds to use in the Feistel cipher. */
|
|
37
|
+
export declare const SECRET_INDEX = 255;
|
|
38
|
+
/** The index of the share containing the shared secret. */
|
|
39
|
+
export declare const DIGEST_INDEX = 254;
|
|
40
|
+
/** The index of the share containing the digest of the shared secret. */
|
|
41
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,UAAU,KAAK,CAAC;AAC7B,uCAAuC;AAEvC,eAAO,MAAM,KAAK,QAAkB,CAAC;AACrC,2CAA2C;AAE3C,eAAO,MAAM,cAAc,KAAK,CAAC;AACjC,mDAAmD;AAEnD,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,wDAAwD;AAExD,eAAO,MAAM,yBAAyB,IAAI,CAAC;AAC3C,oDAAoD;AAEpD,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AACF,mGAAmG;AAEnG,eAAO,MAAM,eAAe,KAAK,CAAC;AAClC,wDAAwD;AAExD,eAAO,MAAM,qBAAqB,IAAI,CAAC;AACvC,kDAAkD;AAElD,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,8DAA8D;AAE9D,eAAO,MAAM,yBAAyB,qBAAgC,CAAC;AACvE;+CAC+C;AAE/C,eAAO,MAAM,+BAA+B,qBAA2C,CAAC;AACxF;4CAC4C;AAE5C,eAAO,MAAM,yBAAyB,QAA0B,CAAC;AACjE,gFAAgF;AAEhF,eAAO,MAAM,qBAAqB,QAAkD,CAAC;AACrF,mEAAmE;AAEnE,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,wDAAwD;AAExD,eAAO,MAAM,yBAAyB,QAAyD,CAAC;AAChG,2DAA2D;AAE3D,eAAO,MAAM,oBAAoB,QAAQ,CAAC;AAC1C,yDAAyD;AAEzD,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,yDAAyD;AAEzD,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,2DAA2D;AAE3D,eAAO,MAAM,YAAY,MAAM,CAAC;AAChC,yEAAyE"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DIGEST_INDEX = exports.SECRET_INDEX = exports.ROUND_COUNT = exports.BASE_ITERATION_COUNT = exports.MIN_MNEMONIC_LENGTH_WORDS = exports.MIN_STRENGTH_BITS = exports.METADATA_LENGTH_WORDS = exports.GROUP_PREFIX_LENGTH_WORDS = exports.CUSTOMIZATION_STRING_EXTENDABLE = exports.CUSTOMIZATION_STRING_ORIG = exports.DIGEST_LENGTH_BYTES = exports.CHECKSUM_LENGTH_WORDS = exports.MAX_SHARE_COUNT = exports.ID_EXP_LENGTH_WORDS = exports.ITERATION_EXP_LENGTH_BITS = exports.EXTENDABLE_FLAG_LENGTH_BITS = exports.ID_LENGTH_BITS = exports.RADIX = exports.RADIX_BITS = void 0;
|
|
4
|
+
const utils_1 = require("./utils");
|
|
5
|
+
exports.RADIX_BITS = 10;
|
|
6
|
+
/** The length of the radix in bits. */
|
|
7
|
+
exports.RADIX = 2 ** exports.RADIX_BITS;
|
|
8
|
+
/** The number of words in the wordlist. */
|
|
9
|
+
exports.ID_LENGTH_BITS = 15;
|
|
10
|
+
/** The length of the random identifier in bits. */
|
|
11
|
+
exports.EXTENDABLE_FLAG_LENGTH_BITS = 1;
|
|
12
|
+
/** The length of the extendable backup flag in bits. */
|
|
13
|
+
exports.ITERATION_EXP_LENGTH_BITS = 4;
|
|
14
|
+
/** The length of the iteration exponent in bits. */
|
|
15
|
+
exports.ID_EXP_LENGTH_WORDS = (0, utils_1.bitsToWords)(exports.ID_LENGTH_BITS + exports.EXTENDABLE_FLAG_LENGTH_BITS + exports.ITERATION_EXP_LENGTH_BITS);
|
|
16
|
+
/** The length of the random identifier, extendable backup flag and iteration exponent in words. */
|
|
17
|
+
exports.MAX_SHARE_COUNT = 16;
|
|
18
|
+
/** The maximum number of shares that can be created. */
|
|
19
|
+
exports.CHECKSUM_LENGTH_WORDS = 3;
|
|
20
|
+
/** The length of the RS1024 checksum in words. */
|
|
21
|
+
exports.DIGEST_LENGTH_BYTES = 4;
|
|
22
|
+
/** The length of the digest of the shared secret in bytes. */
|
|
23
|
+
exports.CUSTOMIZATION_STRING_ORIG = Buffer.from('shamir', 'utf8');
|
|
24
|
+
/** The customization string used in the RS1024 checksum and in the PBKDF2 salt for
|
|
25
|
+
shares _without_ the extendable backup flag. */
|
|
26
|
+
exports.CUSTOMIZATION_STRING_EXTENDABLE = Buffer.from('shamir_extendable', 'utf8');
|
|
27
|
+
/** The customization string used in the RS1024 checksum for
|
|
28
|
+
shares _with_ the extendable backup flag. */
|
|
29
|
+
exports.GROUP_PREFIX_LENGTH_WORDS = exports.ID_EXP_LENGTH_WORDS + 1;
|
|
30
|
+
/** The length of the prefix of the mnemonic that is common to a share group. */
|
|
31
|
+
exports.METADATA_LENGTH_WORDS = exports.ID_EXP_LENGTH_WORDS + 2 + exports.CHECKSUM_LENGTH_WORDS;
|
|
32
|
+
/** The length of the mnemonic in words without the share value. */
|
|
33
|
+
exports.MIN_STRENGTH_BITS = 128;
|
|
34
|
+
/** The minimum allowed entropy of the master secret. */
|
|
35
|
+
exports.MIN_MNEMONIC_LENGTH_WORDS = exports.METADATA_LENGTH_WORDS + (0, utils_1.bitsToWords)(exports.MIN_STRENGTH_BITS);
|
|
36
|
+
/** The minimum allowed length of the mnemonic in words. */
|
|
37
|
+
exports.BASE_ITERATION_COUNT = 10000;
|
|
38
|
+
/** The minimum number of iterations to use in PBKDF2. */
|
|
39
|
+
exports.ROUND_COUNT = 4;
|
|
40
|
+
/** The number of rounds to use in the Feistel cipher. */
|
|
41
|
+
exports.SECRET_INDEX = 255;
|
|
42
|
+
/** The index of the share containing the shared secret. */
|
|
43
|
+
exports.DIGEST_INDEX = 254;
|
|
44
|
+
/** The index of the share containing the digest of the shared secret. */
|
|
45
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":";;;AAAA,mCAAsC;AAEzB,QAAA,UAAU,GAAG,EAAE,CAAC;AAC7B,uCAAuC;AAE1B,QAAA,KAAK,GAAG,CAAC,IAAI,kBAAU,CAAC;AACrC,2CAA2C;AAE9B,QAAA,cAAc,GAAG,EAAE,CAAC;AACjC,mDAAmD;AAEtC,QAAA,2BAA2B,GAAG,CAAC,CAAC;AAC7C,wDAAwD;AAE3C,QAAA,yBAAyB,GAAG,CAAC,CAAC;AAC3C,oDAAoD;AAEvC,QAAA,mBAAmB,GAAG,IAAA,mBAAW,EAC5C,sBAAc,GAAG,mCAA2B,GAAG,iCAAyB,CACzE,CAAC;AACF,mGAAmG;AAEtF,QAAA,eAAe,GAAG,EAAE,CAAC;AAClC,wDAAwD;AAE3C,QAAA,qBAAqB,GAAG,CAAC,CAAC;AACvC,kDAAkD;AAErC,QAAA,mBAAmB,GAAG,CAAC,CAAC;AACrC,8DAA8D;AAEjD,QAAA,yBAAyB,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AACvE;+CAC+C;AAElC,QAAA,+BAA+B,GAAG,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;AACxF;4CAC4C;AAE/B,QAAA,yBAAyB,GAAG,2BAAmB,GAAG,CAAC,CAAC;AACjE,gFAAgF;AAEnE,QAAA,qBAAqB,GAAG,2BAAmB,GAAG,CAAC,GAAG,6BAAqB,CAAC;AACrF,mEAAmE;AAEtD,QAAA,iBAAiB,GAAG,GAAG,CAAC;AACrC,wDAAwD;AAE3C,QAAA,yBAAyB,GAAG,6BAAqB,GAAG,IAAA,mBAAW,EAAC,yBAAiB,CAAC,CAAC;AAChG,2DAA2D;AAE9C,QAAA,oBAAoB,GAAG,KAAK,CAAC;AAC1C,yDAAyD;AAE5C,QAAA,WAAW,GAAG,CAAC,CAAC;AAC7B,yDAAyD;AAE5C,QAAA,YAAY,GAAG,GAAG,CAAC;AAChC,2DAA2D;AAE9C,QAAA,YAAY,GAAG,GAAG,CAAC;AAChC,yEAAyE"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { encrypt, decrypt } from './cipher';
|
|
2
|
+
export { EncryptedMasterSecret, ShareGroup, RawShare, combineMnemonics, decodeMnemonics, generateMnemonics, splitEms, recoverEms, RANDOM_BYTES, } from './shamir';
|
|
3
|
+
export { Share, ShareCommonParameters, ShareGroupParameters } from './share';
|
|
4
|
+
export { MnemonicError } from './utils';
|
|
5
|
+
export { RecoveryState, UNDETERMINED } from './recovery';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAC5C,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,QAAQ,EACR,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,YAAY,GACb,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UNDETERMINED = exports.RecoveryState = exports.MnemonicError = exports.Share = exports.RANDOM_BYTES = exports.recoverEms = exports.splitEms = exports.generateMnemonics = exports.decodeMnemonics = exports.combineMnemonics = exports.ShareGroup = exports.EncryptedMasterSecret = exports.decrypt = exports.encrypt = void 0;
|
|
4
|
+
var cipher_1 = require("./cipher");
|
|
5
|
+
Object.defineProperty(exports, "encrypt", { enumerable: true, get: function () { return cipher_1.encrypt; } });
|
|
6
|
+
Object.defineProperty(exports, "decrypt", { enumerable: true, get: function () { return cipher_1.decrypt; } });
|
|
7
|
+
var shamir_1 = require("./shamir");
|
|
8
|
+
Object.defineProperty(exports, "EncryptedMasterSecret", { enumerable: true, get: function () { return shamir_1.EncryptedMasterSecret; } });
|
|
9
|
+
Object.defineProperty(exports, "ShareGroup", { enumerable: true, get: function () { return shamir_1.ShareGroup; } });
|
|
10
|
+
Object.defineProperty(exports, "combineMnemonics", { enumerable: true, get: function () { return shamir_1.combineMnemonics; } });
|
|
11
|
+
Object.defineProperty(exports, "decodeMnemonics", { enumerable: true, get: function () { return shamir_1.decodeMnemonics; } });
|
|
12
|
+
Object.defineProperty(exports, "generateMnemonics", { enumerable: true, get: function () { return shamir_1.generateMnemonics; } });
|
|
13
|
+
Object.defineProperty(exports, "splitEms", { enumerable: true, get: function () { return shamir_1.splitEms; } });
|
|
14
|
+
Object.defineProperty(exports, "recoverEms", { enumerable: true, get: function () { return shamir_1.recoverEms; } });
|
|
15
|
+
Object.defineProperty(exports, "RANDOM_BYTES", { enumerable: true, get: function () { return shamir_1.RANDOM_BYTES; } });
|
|
16
|
+
var share_1 = require("./share");
|
|
17
|
+
Object.defineProperty(exports, "Share", { enumerable: true, get: function () { return share_1.Share; } });
|
|
18
|
+
var utils_1 = require("./utils");
|
|
19
|
+
Object.defineProperty(exports, "MnemonicError", { enumerable: true, get: function () { return utils_1.MnemonicError; } });
|
|
20
|
+
var recovery_1 = require("./recovery");
|
|
21
|
+
Object.defineProperty(exports, "RecoveryState", { enumerable: true, get: function () { return recovery_1.RecoveryState; } });
|
|
22
|
+
Object.defineProperty(exports, "UNDETERMINED", { enumerable: true, get: function () { return recovery_1.UNDETERMINED; } });
|
|
23
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAA4C;AAAnC,iGAAA,OAAO,OAAA;AAAE,iGAAA,OAAO,OAAA;AACzB,mCAUkB;AAThB,+GAAA,qBAAqB,OAAA;AACrB,oGAAA,UAAU,OAAA;AAEV,0GAAA,gBAAgB,OAAA;AAChB,yGAAA,eAAe,OAAA;AACf,2GAAA,iBAAiB,OAAA;AACjB,kGAAA,QAAQ,OAAA;AACR,oGAAA,UAAU,OAAA;AACV,sGAAA,YAAY,OAAA;AAEd,iCAA6E;AAApE,8FAAA,KAAK,OAAA;AACd,iCAAwC;AAA/B,sGAAA,aAAa,OAAA;AACtB,uCAAyD;AAAhD,yGAAA,aAAa,OAAA;AAAE,wGAAA,YAAY,OAAA"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Share } from './share';
|
|
2
|
+
export declare const UNDETERMINED = -1;
|
|
3
|
+
export declare class RecoveryState {
|
|
4
|
+
/** Object for keeping track of running Shamir recovery. */
|
|
5
|
+
private lastShare;
|
|
6
|
+
private groups;
|
|
7
|
+
private parameters;
|
|
8
|
+
groupPrefix(groupIndex: number): string;
|
|
9
|
+
groupStatus(groupIndex: number): [number, number];
|
|
10
|
+
groupIsComplete(groupIndex: number): boolean;
|
|
11
|
+
groupsComplete(): number;
|
|
12
|
+
isComplete(): boolean;
|
|
13
|
+
matches(share: Share): boolean;
|
|
14
|
+
addShare(share: Share): boolean;
|
|
15
|
+
has(obj: Share): boolean;
|
|
16
|
+
recover(passphrase: Buffer): Buffer;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=recovery.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.d.ts","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,EAAyB,MAAM,SAAS,CAAC;AAGvD,eAAO,MAAM,YAAY,KAAK,CAAC;AAE/B,qBAAa,aAAa;IACxB,2DAA2D;IAC3D,OAAO,CAAC,SAAS,CAAsB;IACvC,OAAO,CAAC,MAAM,CAAsC;IACpD,OAAO,CAAC,UAAU,CAAsC;IAExD,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;IAqBvC,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;IAcjD,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO;IAM5C,cAAc,IAAI,MAAM;IAexB,UAAU,IAAI,OAAO;IAWrB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAiB9B,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO;IAsB/B,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO;IAaxB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CAuBpC"}
|
package/dist/recovery.js
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RecoveryState = exports.UNDETERMINED = void 0;
|
|
4
|
+
const constants_1 = require("./constants");
|
|
5
|
+
const shamir_1 = require("./shamir");
|
|
6
|
+
const share_1 = require("./share");
|
|
7
|
+
const utils_1 = require("./utils");
|
|
8
|
+
exports.UNDETERMINED = -1;
|
|
9
|
+
class RecoveryState {
|
|
10
|
+
constructor() {
|
|
11
|
+
/** Object for keeping track of running Shamir recovery. */
|
|
12
|
+
this.lastShare = null;
|
|
13
|
+
this.groups = new Map();
|
|
14
|
+
this.parameters = null;
|
|
15
|
+
}
|
|
16
|
+
groupPrefix(groupIndex) {
|
|
17
|
+
/** Return three starting words of a given group. */
|
|
18
|
+
if (!this.lastShare) {
|
|
19
|
+
throw new Error('Add at least one share first');
|
|
20
|
+
}
|
|
21
|
+
// Create a fake share with the requested group index
|
|
22
|
+
const fakeShare = new share_1.Share(this.lastShare.identifier, this.lastShare.extendable, this.lastShare.iterationExponent, groupIndex, this.lastShare.groupThreshold, this.lastShare.groupCount, this.lastShare.index, this.lastShare.memberThreshold, this.lastShare.value);
|
|
23
|
+
return fakeShare.words().slice(0, constants_1.GROUP_PREFIX_LENGTH_WORDS).join(' ');
|
|
24
|
+
}
|
|
25
|
+
groupStatus(groupIndex) {
|
|
26
|
+
/** Return completion status of given group.
|
|
27
|
+
*
|
|
28
|
+
* Result consists of the number of shares already entered, and the threshold
|
|
29
|
+
* for recovering the group.
|
|
30
|
+
*/
|
|
31
|
+
const group = this.groups.get(groupIndex);
|
|
32
|
+
if (!group || group.isEmpty) {
|
|
33
|
+
return [0, exports.UNDETERMINED];
|
|
34
|
+
}
|
|
35
|
+
return [group.length, group.memberThreshold()];
|
|
36
|
+
}
|
|
37
|
+
groupIsComplete(groupIndex) {
|
|
38
|
+
/** Check whether a given group is already complete. */
|
|
39
|
+
const group = this.groups.get(groupIndex);
|
|
40
|
+
return group ? group.isComplete() : false;
|
|
41
|
+
}
|
|
42
|
+
groupsComplete() {
|
|
43
|
+
/** Return the number of groups that are already complete. */
|
|
44
|
+
if (this.parameters === null) {
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
let count = 0;
|
|
48
|
+
for (let i = 0; i < this.parameters.groupCount; i++) {
|
|
49
|
+
if (this.groupIsComplete(i)) {
|
|
50
|
+
count++;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return count;
|
|
54
|
+
}
|
|
55
|
+
isComplete() {
|
|
56
|
+
/** Check whether the recovery set is complete.
|
|
57
|
+
*
|
|
58
|
+
* That is, at least M groups must be complete, where M is the global threshold.
|
|
59
|
+
*/
|
|
60
|
+
if (this.parameters === null) {
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
return this.groupsComplete() >= this.parameters.groupThreshold;
|
|
64
|
+
}
|
|
65
|
+
matches(share) {
|
|
66
|
+
/** Check whether the provided share matches the current set, i.e., has the same
|
|
67
|
+
* common parameters.
|
|
68
|
+
*/
|
|
69
|
+
if (this.parameters === null) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
const shareParams = share.commonParameters();
|
|
73
|
+
return (shareParams.identifier === this.parameters.identifier &&
|
|
74
|
+
shareParams.extendable === this.parameters.extendable &&
|
|
75
|
+
shareParams.iterationExponent === this.parameters.iterationExponent &&
|
|
76
|
+
shareParams.groupThreshold === this.parameters.groupThreshold &&
|
|
77
|
+
shareParams.groupCount === this.parameters.groupCount);
|
|
78
|
+
}
|
|
79
|
+
addShare(share) {
|
|
80
|
+
/** Add a share to the recovery set. */
|
|
81
|
+
if (!this.matches(share)) {
|
|
82
|
+
throw new utils_1.MnemonicError('This mnemonic is not part of the current set. Please try again.');
|
|
83
|
+
}
|
|
84
|
+
if (!this.groups.has(share.groupIndex)) {
|
|
85
|
+
this.groups.set(share.groupIndex, new shamir_1.ShareGroup());
|
|
86
|
+
}
|
|
87
|
+
const group = this.groups.get(share.groupIndex);
|
|
88
|
+
group.add(share);
|
|
89
|
+
this.lastShare = share;
|
|
90
|
+
if (this.parameters === null) {
|
|
91
|
+
this.parameters = share.commonParameters();
|
|
92
|
+
}
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
has(obj) {
|
|
96
|
+
if (!this.matches(obj)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
if (this.groups.size === 0) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const group = this.groups.get(obj.groupIndex);
|
|
103
|
+
return group ? group.has(obj) : false;
|
|
104
|
+
}
|
|
105
|
+
recover(passphrase) {
|
|
106
|
+
/** Recover the master secret, given a passphrase. */
|
|
107
|
+
// Select a subset of shares which meets the thresholds.
|
|
108
|
+
const reducedGroups = new Map();
|
|
109
|
+
for (const [groupIndex, group] of this.groups.entries()) {
|
|
110
|
+
if (group.isComplete()) {
|
|
111
|
+
reducedGroups.set(groupIndex, group.getMinimalGroup());
|
|
112
|
+
}
|
|
113
|
+
// some groups have been added so parameters must be known
|
|
114
|
+
if (this.parameters === null) {
|
|
115
|
+
throw new Error('Parameters should be known at this point');
|
|
116
|
+
}
|
|
117
|
+
if (reducedGroups.size >= this.parameters.groupThreshold) {
|
|
118
|
+
break;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
const encryptedMasterSecret = (0, shamir_1.recoverEms)(reducedGroups);
|
|
122
|
+
return encryptedMasterSecret.decrypt(passphrase);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
exports.RecoveryState = RecoveryState;
|
|
126
|
+
//# sourceMappingURL=recovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"recovery.js","sourceRoot":"","sources":["../src/recovery.ts"],"names":[],"mappings":";;;AAAA,2CAAwD;AACxD,qCAAkD;AAClD,mCAAuD;AACvD,mCAAwC;AAE3B,QAAA,YAAY,GAAG,CAAC,CAAC,CAAC;AAE/B,MAAa,aAAa;IAA1B;QACE,2DAA2D;QACnD,cAAS,GAAiB,IAAI,CAAC;QAC/B,WAAM,GAA4B,IAAI,GAAG,EAAE,CAAC;QAC5C,eAAU,GAAiC,IAAI,CAAC;IAgJ1D,CAAC;IA9IC,WAAW,CAAC,UAAkB;QAC5B,oDAAoD;QACpD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,IAAI,KAAK,CAAC,8BAA8B,CAAC,CAAC;QAClD,CAAC;QAED,qDAAqD;QACrD,MAAM,SAAS,GAAG,IAAI,aAAK,CACzB,IAAI,CAAC,SAAS,CAAC,UAAU,EACzB,IAAI,CAAC,SAAS,CAAC,UAAU,EACzB,IAAI,CAAC,SAAS,CAAC,iBAAiB,EAChC,UAAU,EACV,IAAI,CAAC,SAAS,CAAC,cAAc,EAC7B,IAAI,CAAC,SAAS,CAAC,UAAU,EACzB,IAAI,CAAC,SAAS,CAAC,KAAK,EACpB,IAAI,CAAC,SAAS,CAAC,eAAe,EAC9B,IAAI,CAAC,SAAS,CAAC,KAAK,CACrB,CAAC;QACF,OAAO,SAAS,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,qCAAyB,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzE,CAAC;IAED,WAAW,CAAC,UAAkB;QAC5B;;;;WAIG;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,EAAE,oBAAY,CAAC,CAAC;QAC3B,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;IACjD,CAAC;IAED,eAAe,CAAC,UAAkB;QAChC,uDAAuD;QACvD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC1C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;IAC5C,CAAC;IAED,cAAc;QACZ,6DAA6D;QAC7D,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,OAAO,CAAC,CAAC;QACX,CAAC;QAED,IAAI,KAAK,GAAG,CAAC,CAAC;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC,EAAE,EAAE,CAAC;YACpD,IAAI,IAAI,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5B,KAAK,EAAE,CAAC;YACV,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,UAAU;QACR;;;WAGG;QACH,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC,cAAc,EAAE,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC;IACjE,CAAC;IAED,OAAO,CAAC,KAAY;QAClB;;WAEG;QACH,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,WAAW,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC7C,OAAO,CACL,WAAW,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CAAC,UAAU;YACrD,WAAW,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CAAC,UAAU;YACrD,WAAW,CAAC,iBAAiB,KAAK,IAAI,CAAC,UAAU,CAAC,iBAAiB;YACnE,WAAW,CAAC,cAAc,KAAK,IAAI,CAAC,UAAU,CAAC,cAAc;YAC7D,WAAW,CAAC,UAAU,KAAK,IAAI,CAAC,UAAU,CAAC,UAAU,CACtD,CAAC;IACJ,CAAC;IAED,QAAQ,CAAC,KAAY;QACnB,uCAAuC;QACvC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,MAAM,IAAI,qBAAa,CACrB,iEAAiE,CAClE,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,IAAI,mBAAU,EAAE,CAAC,CAAC;QACtD,CAAC;QACD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAE,CAAC;QACjD,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC;QAEvB,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;YAC7B,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,gBAAgB,EAAE,CAAC;QAC7C,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,GAAG,CAAC,GAAU;QACZ,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YACvB,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO,KAAK,CAAC;QACf,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IACxC,CAAC;IAED,OAAO,CAAC,UAAkB;QACxB,qDAAqD;QACrD,wDAAwD;QACxD,MAAM,aAAa,GAA4B,IAAI,GAAG,EAAE,CAAC;QAEzD,KAAK,MAAM,CAAC,UAAU,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EAAE,CAAC;YACxD,IAAI,KAAK,CAAC,UAAU,EAAE,EAAE,CAAC;gBACvB,aAAa,CAAC,GAAG,CAAC,UAAU,EAAE,KAAK,CAAC,eAAe,EAAE,CAAC,CAAC;YACzD,CAAC;YAED,0DAA0D;YAC1D,IAAI,IAAI,CAAC,UAAU,KAAK,IAAI,EAAE,CAAC;gBAC7B,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;YAC9D,CAAC;YAED,IAAI,aAAa,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc,EAAE,CAAC;gBACzD,MAAM;YACR,CAAC;QACH,CAAC;QAED,MAAM,qBAAqB,GAAG,IAAA,mBAAU,EAAC,aAAa,CAAC,CAAC;QACxD,OAAO,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACnD,CAAC;CACF;AApJD,sCAoJC"}
|
package/dist/rs1024.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { CHECKSUM_LENGTH_WORDS } from './constants';
|
|
2
|
+
export { CHECKSUM_LENGTH_WORDS };
|
|
3
|
+
export declare function createChecksum(data: Iterable<number>, customizationString: Buffer): number[];
|
|
4
|
+
export declare function verifyChecksum(data: Iterable<number>, customizationString: Buffer): boolean;
|
|
5
|
+
//# sourceMappingURL=rs1024.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rs1024.d.ts","sourceRoot":"","sources":["../src/rs1024.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,OAAO,EAAE,qBAAqB,EAAE,CAAC;AA6BjC,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,EAAE,MAAM,GAAG,MAAM,EAAE,CAY5F;AAED,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,EAAE,MAAM,GAAG,OAAO,CAG3F"}
|
package/dist/rs1024.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CHECKSUM_LENGTH_WORDS = void 0;
|
|
4
|
+
exports.createChecksum = createChecksum;
|
|
5
|
+
exports.verifyChecksum = verifyChecksum;
|
|
6
|
+
const constants_1 = require("./constants");
|
|
7
|
+
Object.defineProperty(exports, "CHECKSUM_LENGTH_WORDS", { enumerable: true, get: function () { return constants_1.CHECKSUM_LENGTH_WORDS; } });
|
|
8
|
+
function _polymod(values) {
|
|
9
|
+
const GEN = [
|
|
10
|
+
0xE0E040,
|
|
11
|
+
0x1C1C080,
|
|
12
|
+
0x3838100,
|
|
13
|
+
0x7070200,
|
|
14
|
+
0xE0E0009,
|
|
15
|
+
0x1C0C2412,
|
|
16
|
+
0x38086C24,
|
|
17
|
+
0x3090FC48,
|
|
18
|
+
0x21B1F890,
|
|
19
|
+
0x3F3F120,
|
|
20
|
+
];
|
|
21
|
+
let chk = 1;
|
|
22
|
+
for (const v of values) {
|
|
23
|
+
const b = chk >> 20;
|
|
24
|
+
chk = ((chk & 0xFFFFF) << 10) ^ v;
|
|
25
|
+
for (let i = 0; i < 10; i++) {
|
|
26
|
+
if ((b >> i) & 1) {
|
|
27
|
+
chk ^= GEN[i];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return chk;
|
|
32
|
+
}
|
|
33
|
+
function createChecksum(data, customizationString) {
|
|
34
|
+
const values = [
|
|
35
|
+
...Array.from(customizationString),
|
|
36
|
+
...Array.from(data),
|
|
37
|
+
...Array(constants_1.CHECKSUM_LENGTH_WORDS).fill(0),
|
|
38
|
+
];
|
|
39
|
+
const polymod = _polymod(values) ^ 1;
|
|
40
|
+
const checksum = [];
|
|
41
|
+
for (let i = constants_1.CHECKSUM_LENGTH_WORDS - 1; i >= 0; i--) {
|
|
42
|
+
checksum.push((polymod >> (10 * i)) & 1023);
|
|
43
|
+
}
|
|
44
|
+
return checksum;
|
|
45
|
+
}
|
|
46
|
+
function verifyChecksum(data, customizationString) {
|
|
47
|
+
const values = [...Array.from(customizationString), ...Array.from(data)];
|
|
48
|
+
return _polymod(values) === 1;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=rs1024.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rs1024.js","sourceRoot":"","sources":["../src/rs1024.ts"],"names":[],"mappings":";;;AA+BA,wCAYC;AAED,wCAGC;AAhDD,2CAAoD;AAE3C,sGAFA,iCAAqB,OAEA;AAE9B,SAAS,QAAQ,CAAC,MAAwB;IACxC,MAAM,GAAG,GAAG;QACV,QAAQ;QACR,SAAS;QACT,SAAS;QACT,SAAS;QACT,SAAS;QACT,UAAU;QACV,UAAU;QACV,UAAU;QACV,UAAU;QACV,SAAS;KACV,CAAC;IAEF,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,GAAG,IAAI,EAAE,CAAC;QACpB,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;QAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC;YAC5B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;gBACjB,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED,SAAgB,cAAc,CAAC,IAAsB,EAAE,mBAA2B;IAChF,MAAM,MAAM,GAAa;QACvB,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC;QAClC,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACnB,GAAG,KAAK,CAAC,iCAAqB,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;KACxC,CAAC;IACF,MAAM,OAAO,GAAG,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACrC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,KAAK,IAAI,CAAC,GAAG,iCAAqB,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;IAC9C,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAgB,cAAc,CAAC,IAAsB,EAAE,mBAA2B;IAChF,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,mBAAmB,CAAC,EAAE,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;IACzE,OAAO,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAChC,CAAC"}
|
package/dist/shamir.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Share, ShareCommonParameters, ShareGroupParameters } from './share';
|
|
2
|
+
export interface RawShare {
|
|
3
|
+
x: number;
|
|
4
|
+
data: Buffer;
|
|
5
|
+
}
|
|
6
|
+
export declare class ShareGroup {
|
|
7
|
+
protected _shares: Set<Share>;
|
|
8
|
+
[Symbol.iterator](): Iterator<Share>;
|
|
9
|
+
get length(): number;
|
|
10
|
+
get isEmpty(): boolean;
|
|
11
|
+
has(obj: Share): boolean;
|
|
12
|
+
add(share: Share): void;
|
|
13
|
+
private _groupParametersMatch;
|
|
14
|
+
toRawShares(): RawShare[];
|
|
15
|
+
getMinimalGroup(): ShareGroup;
|
|
16
|
+
commonParameters(): ShareCommonParameters;
|
|
17
|
+
groupParameters(): ShareGroupParameters;
|
|
18
|
+
memberThreshold(): number;
|
|
19
|
+
isComplete(): boolean;
|
|
20
|
+
}
|
|
21
|
+
export declare class EncryptedMasterSecret {
|
|
22
|
+
readonly identifier: number;
|
|
23
|
+
readonly extendable: boolean;
|
|
24
|
+
readonly iterationExponent: number;
|
|
25
|
+
readonly ciphertext: Buffer;
|
|
26
|
+
constructor(identifier: number, extendable: boolean, iterationExponent: number, ciphertext: Buffer);
|
|
27
|
+
static fromMasterSecret(masterSecret: Buffer, passphrase: Buffer, identifier: number, extendable: boolean, iterationExponent: number): EncryptedMasterSecret;
|
|
28
|
+
decrypt(passphrase: Buffer): Buffer;
|
|
29
|
+
}
|
|
30
|
+
export declare let RANDOM_BYTES: (length: number) => Buffer;
|
|
31
|
+
export declare function decodeMnemonics(mnemonics: Iterable<string>): Map<number, ShareGroup>;
|
|
32
|
+
export declare function splitEms(groupThreshold: number, groups: Array<[number, number]>, encryptedMasterSecret: EncryptedMasterSecret): Share[][];
|
|
33
|
+
export declare function generateMnemonics(groupThreshold: number, groups: Array<[number, number]>, masterSecret: Buffer, passphrase?: Buffer, extendable?: boolean, iterationExponent?: number): string[][];
|
|
34
|
+
export declare function recoverEms(groups: Map<number, ShareGroup>): EncryptedMasterSecret;
|
|
35
|
+
export declare function combineMnemonics(mnemonics: Iterable<string>, passphrase?: Buffer): Buffer;
|
|
36
|
+
//# sourceMappingURL=shamir.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shamir.d.ts","sourceRoot":"","sources":["../src/shamir.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AAG7E,MAAM,WAAW,QAAQ;IACvB,CAAC,EAAE,MAAM,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qBAAa,UAAU;IACrB,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,KAAK,CAAC,CAAa;IAE1C,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC;IAIpC,IAAI,MAAM,IAAI,MAAM,CAEnB;IAED,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,GAAG,CAAC,GAAG,EAAE,KAAK,GAAG,OAAO;IAoBxB,GAAG,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI;IA4BvB,OAAO,CAAC,qBAAqB;IAmB7B,WAAW,IAAI,QAAQ,EAAE;IAIzB,eAAe,IAAI,UAAU;IAQ7B,gBAAgB,IAAI,qBAAqB;IAIzC,eAAe,IAAI,oBAAoB;IAIvC,eAAe,IAAI,MAAM;IAIzB,UAAU,IAAI,OAAO;CAMtB;AAED,qBAAa,qBAAqB;IAChC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;IACnC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;gBAG1B,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,OAAO,EACnB,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM;IAQpB,MAAM,CAAC,gBAAgB,CACrB,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,OAAO,EACnB,iBAAiB,EAAE,MAAM,GACxB,qBAAqB;IAgBxB,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM;CASpC;AAED,eAAO,IAAI,YAAY,GAAI,QAAQ,MAAM,KAAG,MAE3C,CAAC;AAmKF,wBAAgB,eAAe,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAsCpF;AAED,wBAAgB,QAAQ,CACtB,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC/B,qBAAqB,EAAE,qBAAqB,GAC3C,KAAK,EAAE,EAAE,CA2DX;AAaD,wBAAgB,iBAAiB,CAC/B,cAAc,EAAE,MAAM,EACtB,MAAM,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC/B,YAAY,EAAE,MAAM,EACpB,UAAU,GAAE,MAAwB,EACpC,UAAU,GAAE,OAAc,EAC1B,iBAAiB,GAAE,MAAU,GAC5B,MAAM,EAAE,EAAE,CAuCZ;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,qBAAqB,CA0DjF;AAED,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,EAC3B,UAAU,GAAE,MAAwB,GACnC,MAAM,CAmBR"}
|