@evolu/common 8.16.0 → 8.17.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/dist/src/Crypto.d.ts +21 -3
- package/dist/src/Crypto.d.ts.map +1 -1
- package/dist/src/Crypto.js +18 -2
- package/dist/src/local-first/Owner.d.ts +20 -8
- package/dist/src/local-first/Owner.d.ts.map +1 -1
- package/dist/src/local-first/Owner.js +23 -10
- package/dist/src/local-first/Schema.d.ts +1 -1
- package/package.json +1 -1
- package/src/Crypto.ts +23 -3
- package/src/local-first/Owner.test.ts +52 -1
- package/src/local-first/Owner.ts +26 -7
package/dist/src/Crypto.d.ts
CHANGED
|
@@ -84,6 +84,13 @@ type Entropy = typeof Entropy.Output;
|
|
|
84
84
|
*/
|
|
85
85
|
export declare const Entropy16: import("./Type.ts").BrandType<import("./Type.ts").BrandType<import("./Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("./Type.ts").ObjectTagError<"Uint8Array">, null, import("./Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length16", import("./Type.ts").LengthError<16>>;
|
|
86
86
|
export type Entropy16 = typeof Entropy16.Output;
|
|
87
|
+
/**
|
|
88
|
+
* Cryptographic entropy of exactly 20 bytes.
|
|
89
|
+
*
|
|
90
|
+
* @group Random bytes
|
|
91
|
+
*/
|
|
92
|
+
export declare const Entropy20: import("./Type.ts").BrandType<import("./Type.ts").BrandType<import("./Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("./Type.ts").ObjectTagError<"Uint8Array">, null, import("./Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length20", import("./Type.ts").LengthError<20>>;
|
|
93
|
+
export type Entropy20 = typeof Entropy20.Output;
|
|
87
94
|
/**
|
|
88
95
|
* Cryptographic entropy of exactly 24 bytes.
|
|
89
96
|
*
|
|
@@ -91,6 +98,13 @@ export type Entropy16 = typeof Entropy16.Output;
|
|
|
91
98
|
*/
|
|
92
99
|
export declare const Entropy24: import("./Type.ts").BrandType<import("./Type.ts").BrandType<import("./Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("./Type.ts").ObjectTagError<"Uint8Array">, null, import("./Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length24", import("./Type.ts").LengthError<24>>;
|
|
93
100
|
export type Entropy24 = typeof Entropy24.Output;
|
|
101
|
+
/**
|
|
102
|
+
* Cryptographic entropy of exactly 28 bytes.
|
|
103
|
+
*
|
|
104
|
+
* @group Random bytes
|
|
105
|
+
*/
|
|
106
|
+
export declare const Entropy28: import("./Type.ts").BrandType<import("./Type.ts").BrandType<import("./Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("./Type.ts").ObjectTagError<"Uint8Array">, null, import("./Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length28", import("./Type.ts").LengthError<28>>;
|
|
107
|
+
export type Entropy28 = typeof Entropy28.Output;
|
|
94
108
|
/**
|
|
95
109
|
* Cryptographic entropy of exactly 32 bytes.
|
|
96
110
|
*
|
|
@@ -119,13 +133,17 @@ export declare const createRandomBytes: () => RandomBytes;
|
|
|
119
133
|
*/
|
|
120
134
|
export declare const testCreateRandomBytes: (deps: RandomLibDep) => RandomBytes;
|
|
121
135
|
/**
|
|
122
|
-
*
|
|
136
|
+
* Derives a 32-byte key from a seed and a path using
|
|
137
|
+
* [SLIP-21](https://github.com/satoshilabs/slips/blob/master/slip-0021.md).
|
|
123
138
|
*
|
|
124
|
-
*
|
|
139
|
+
* Derived keys are no stronger than the seed. A 16-byte seed gives 128 bits of
|
|
140
|
+
* security, or about 64 against a quantum computer, whatever the output length.
|
|
141
|
+
* Use a 32-byte random seed for keys that must stay safe against a quantum
|
|
142
|
+
* computer.
|
|
125
143
|
*
|
|
126
144
|
* @group Key derivation
|
|
127
145
|
*/
|
|
128
|
-
export declare const createSlip21: (seed: Entropy16 | Entropy32 | Entropy64, path: ReadonlyArray<string | number>) => Entropy32;
|
|
146
|
+
export declare const createSlip21: (seed: Entropy16 | Entropy20 | Entropy24 | Entropy28 | Entropy32 | Entropy64, path: ReadonlyArray<string | number>) => Entropy32;
|
|
129
147
|
/**
|
|
130
148
|
* Derives a single node in the SLIP-21 hierarchical key derivation.
|
|
131
149
|
*
|
package/dist/src/Crypto.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../../src/Crypto.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C,OAAO,EAGL,cAAc,EAEd,KAAK,KAAK,EAEX,MAAM,WAAW,CAAC;AAEnB;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA4CG;IACH,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,WAAW,EAAE,EAAE,GAAG,SAAS,CAAC;IACnC,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC;CACtC;AAED;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;CACnC;AAED,QAAA,MAAM,OAAO,uSAA6C,CAAC;AAC3D,KAAK,OAAO,GAAG,OAAO,OAAO,CAAC,MAAM,CAAC;AAErC;;;;GAIG;AACH,eAAO,MAAM,SAAS,uXAAoC,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,SAAS,uXAAoC,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,SAAS,uXAAoC,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC;AAEhD;;;;GAIG;AACH,eAAO,MAAM,SAAS,uXAAoC,CAAC;AAC3D,MAAM,MAAM,SAAS,GAAG,OAAO,SAAS,CAAC,MAAM,CAAC;AAEhD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,QAAO,WAEnC,CAAC;AAEH;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,SAAU,YAAY,KAAG,WAMxC,CAAC;AAEpB
|
|
1
|
+
{"version":3,"file":"Crypto.d.ts","sourceRoot":"","sources":["../../src/Crypto.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Crypto.js
CHANGED
|
@@ -20,12 +20,24 @@ const Entropy = /*#__PURE__*/ brand("Entropy", Uint8Array);
|
|
|
20
20
|
* @group Random bytes
|
|
21
21
|
*/
|
|
22
22
|
export const Entropy16 = /*#__PURE__*/ length(16)(Entropy);
|
|
23
|
+
/**
|
|
24
|
+
* Cryptographic entropy of exactly 20 bytes.
|
|
25
|
+
*
|
|
26
|
+
* @group Random bytes
|
|
27
|
+
*/
|
|
28
|
+
export const Entropy20 = /*#__PURE__*/ length(20)(Entropy);
|
|
23
29
|
/**
|
|
24
30
|
* Cryptographic entropy of exactly 24 bytes.
|
|
25
31
|
*
|
|
26
32
|
* @group Random bytes
|
|
27
33
|
*/
|
|
28
34
|
export const Entropy24 = /*#__PURE__*/ length(24)(Entropy);
|
|
35
|
+
/**
|
|
36
|
+
* Cryptographic entropy of exactly 28 bytes.
|
|
37
|
+
*
|
|
38
|
+
* @group Random bytes
|
|
39
|
+
*/
|
|
40
|
+
export const Entropy28 = /*#__PURE__*/ length(28)(Entropy);
|
|
29
41
|
/**
|
|
30
42
|
* Cryptographic entropy of exactly 32 bytes.
|
|
31
43
|
*
|
|
@@ -56,9 +68,13 @@ export const testCreateRandomBytes = (deps) => ({
|
|
|
56
68
|
create: (bytesLength) => globalThis.Uint8Array.from({ length: bytesLength }, () => deps.randomLib.int(0, 255)),
|
|
57
69
|
});
|
|
58
70
|
/**
|
|
59
|
-
*
|
|
71
|
+
* Derives a 32-byte key from a seed and a path using
|
|
72
|
+
* [SLIP-21](https://github.com/satoshilabs/slips/blob/master/slip-0021.md).
|
|
60
73
|
*
|
|
61
|
-
*
|
|
74
|
+
* Derived keys are no stronger than the seed. A 16-byte seed gives 128 bits of
|
|
75
|
+
* security, or about 64 against a quantum computer, whatever the output length.
|
|
76
|
+
* Use a 32-byte random seed for keys that must stay safe against a quantum
|
|
77
|
+
* computer.
|
|
62
78
|
*
|
|
63
79
|
* @group Key derivation
|
|
64
80
|
*/
|
|
@@ -132,17 +132,29 @@ export type OwnerWriteKey = typeof OwnerWriteKey.Output;
|
|
|
132
132
|
*/
|
|
133
133
|
export declare const createOwnerWriteKey: (deps: RandomBytesDep) => OwnerWriteKey;
|
|
134
134
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
135
|
+
* BIP-39 entropy used to derive {@link Owner} keys.
|
|
136
|
+
*
|
|
137
|
+
* A {@link Mnemonic} represents an OwnerSecret, so each valid mnemonic converts
|
|
138
|
+
* to exactly one secret with {@link mnemonicToOwnerSecret} and back with
|
|
139
|
+
* {@link ownerSecretToMnemonic}. A 24-word mnemonic holds 32 bytes, a 12-word
|
|
140
|
+
* mnemonic holds 16 bytes, and 15, 18, and 21 words hold 20, 24, and 28 bytes.
|
|
141
|
+
*
|
|
142
|
+
* {@link createOwnerSecret} generates 32 random bytes, which keep the owner
|
|
143
|
+
* post-quantum safe. The {@link OwnerId} is public and derived from the secret,
|
|
144
|
+
* so anyone who stores an owner's encrypted data can test guesses of the secret
|
|
145
|
+
* offline, and a quantum computer needs only about the square root of the
|
|
146
|
+
* guesses. 32 random bytes leave about 128 bits of security, while 16 bytes
|
|
147
|
+
* leave about 64. A secret derived from a smaller root, such as a 20-word
|
|
148
|
+
* SLIP-39 share, is no stronger than that root, whatever its length. Shorter
|
|
149
|
+
* secrets are accepted for owners created outside Evolu, typically from 12-word
|
|
150
|
+
* mnemonics.
|
|
139
151
|
*
|
|
140
152
|
* @group Core
|
|
141
153
|
*/
|
|
142
|
-
export declare const OwnerSecret: import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length32", import("../Type.ts").LengthError<32
|
|
154
|
+
export declare const OwnerSecret: import("../Type.ts").BrandType<import("../Type.ts").UnionType<readonly [import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length16", import("../Type.ts").LengthError<16>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length20", import("../Type.ts").LengthError<20>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length24", import("../Type.ts").LengthError<24>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length28", import("../Type.ts").LengthError<28>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length32", import("../Type.ts").LengthError<32>>]>, "OwnerSecret", never>;
|
|
143
155
|
export type OwnerSecret = typeof OwnerSecret.Output;
|
|
144
156
|
/**
|
|
145
|
-
* Creates a cryptographically random {@link OwnerSecret}.
|
|
157
|
+
* Creates a cryptographically random 32-byte {@link OwnerSecret}.
|
|
146
158
|
*
|
|
147
159
|
* @group Core
|
|
148
160
|
*/
|
|
@@ -152,7 +164,7 @@ export declare const createOwnerSecret: (deps: RandomBytesDep) => OwnerSecret;
|
|
|
152
164
|
*
|
|
153
165
|
* @group Testing
|
|
154
166
|
*/
|
|
155
|
-
export declare const testOwnerSecret: Uint8Array<ArrayBufferLike> & import("../Brand.ts").Brand<"Entropy"> & import("../Brand.ts").Brand<"Length32"> & import("../Brand.ts").Brand<"OwnerSecret">;
|
|
167
|
+
export declare const testOwnerSecret: ((Uint8Array<ArrayBufferLike> & import("../Brand.ts").Brand<"Entropy"> & import("../Brand.ts").Brand<"Length16">) | (Uint8Array<ArrayBufferLike> & import("../Brand.ts").Brand<"Entropy"> & import("../Brand.ts").Brand<"Length20">) | (Uint8Array<ArrayBufferLike> & import("../Brand.ts").Brand<"Entropy"> & import("../Brand.ts").Brand<"Length24">) | (Uint8Array<ArrayBufferLike> & import("../Brand.ts").Brand<"Entropy"> & import("../Brand.ts").Brand<"Length28">) | (Uint8Array<ArrayBufferLike> & import("../Brand.ts").Brand<"Entropy"> & import("../Brand.ts").Brand<"Length32">)) & import("../Brand.ts").Brand<"OwnerSecret">;
|
|
156
168
|
/**
|
|
157
169
|
* Converts an {@link OwnerSecret} to a {@link Mnemonic}.
|
|
158
170
|
*
|
|
@@ -160,7 +172,7 @@ export declare const testOwnerSecret: Uint8Array<ArrayBufferLike> & import("../B
|
|
|
160
172
|
*/
|
|
161
173
|
export declare const ownerSecretToMnemonic: (secret: OwnerSecret) => Mnemonic;
|
|
162
174
|
/**
|
|
163
|
-
* Converts a {@link Mnemonic} to
|
|
175
|
+
* Converts a {@link Mnemonic} to the {@link OwnerSecret} it represents.
|
|
164
176
|
*
|
|
165
177
|
* @group Core
|
|
166
178
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Owner.d.ts","sourceRoot":"","sources":["../../../src/local-first/Owner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"Owner.d.ts","sourceRoot":"","sources":["../../../src/local-first/Owner.ts"],"names":[],"mappings":"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"}
|
|
@@ -41,10 +41,10 @@
|
|
|
41
41
|
*/
|
|
42
42
|
import * as bip39 from "@scure/bip39";
|
|
43
43
|
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
44
|
-
import { createSlip21, EncryptionKey, Entropy16, Entropy32, testCreateRandomBytes, } from "../Crypto.js";
|
|
44
|
+
import { createSlip21, EncryptionKey, Entropy16, Entropy20, Entropy24, Entropy28, Entropy32, testCreateRandomBytes, } from "../Crypto.js";
|
|
45
45
|
import { testCreateRandomLib } from "../Random.js";
|
|
46
46
|
import { getOrNull } from "../Result.js";
|
|
47
|
-
import { brand, Id, IdBytes, idBytesToId, idToIdBytes, Mnemonic, NonNegativeInt, } from "../Type.js";
|
|
47
|
+
import { brand, Id, IdBytes, idBytesToId, idToIdBytes, Mnemonic, NonNegativeInt, union, } from "../Type.js";
|
|
48
48
|
import { TimestampBytes } from "./Timestamp.js";
|
|
49
49
|
/**
|
|
50
50
|
* A branded {@link Id} that uniquely identifies an {@link Owner}.
|
|
@@ -99,16 +99,29 @@ export const OwnerWriteKey = /*#__PURE__*/ brand("OwnerWriteKey", Entropy16);
|
|
|
99
99
|
*/
|
|
100
100
|
export const createOwnerWriteKey = (deps) => deps.randomBytes.create(16);
|
|
101
101
|
/**
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
102
|
+
* BIP-39 entropy used to derive {@link Owner} keys.
|
|
103
|
+
*
|
|
104
|
+
* A {@link Mnemonic} represents an OwnerSecret, so each valid mnemonic converts
|
|
105
|
+
* to exactly one secret with {@link mnemonicToOwnerSecret} and back with
|
|
106
|
+
* {@link ownerSecretToMnemonic}. A 24-word mnemonic holds 32 bytes, a 12-word
|
|
107
|
+
* mnemonic holds 16 bytes, and 15, 18, and 21 words hold 20, 24, and 28 bytes.
|
|
108
|
+
*
|
|
109
|
+
* {@link createOwnerSecret} generates 32 random bytes, which keep the owner
|
|
110
|
+
* post-quantum safe. The {@link OwnerId} is public and derived from the secret,
|
|
111
|
+
* so anyone who stores an owner's encrypted data can test guesses of the secret
|
|
112
|
+
* offline, and a quantum computer needs only about the square root of the
|
|
113
|
+
* guesses. 32 random bytes leave about 128 bits of security, while 16 bytes
|
|
114
|
+
* leave about 64. A secret derived from a smaller root, such as a 20-word
|
|
115
|
+
* SLIP-39 share, is no stronger than that root, whatever its length. Shorter
|
|
116
|
+
* secrets are accepted for owners created outside Evolu, typically from 12-word
|
|
117
|
+
* mnemonics.
|
|
106
118
|
*
|
|
107
119
|
* @group Core
|
|
108
120
|
*/
|
|
109
|
-
export const OwnerSecret = /*#__PURE__*/ brand("OwnerSecret",
|
|
121
|
+
export const OwnerSecret = /*#__PURE__*/ brand("OwnerSecret",
|
|
122
|
+
/*#__PURE__*/ union(Entropy16, Entropy20, Entropy24, Entropy28, Entropy32));
|
|
110
123
|
/**
|
|
111
|
-
* Creates a cryptographically random {@link OwnerSecret}.
|
|
124
|
+
* Creates a cryptographically random 32-byte {@link OwnerSecret}.
|
|
112
125
|
*
|
|
113
126
|
* @group Core
|
|
114
127
|
*/
|
|
@@ -130,11 +143,11 @@ export const testOwnerSecret = /*#__PURE__*/ createOwnerSecret({
|
|
|
130
143
|
*/
|
|
131
144
|
export const ownerSecretToMnemonic = (secret) => bip39.entropyToMnemonic(secret, wordlist);
|
|
132
145
|
/**
|
|
133
|
-
* Converts a {@link Mnemonic} to
|
|
146
|
+
* Converts a {@link Mnemonic} to the {@link OwnerSecret} it represents.
|
|
134
147
|
*
|
|
135
148
|
* @group Core
|
|
136
149
|
*/
|
|
137
|
-
export const mnemonicToOwnerSecret = (mnemonic) => bip39.mnemonicToEntropy(mnemonic, wordlist);
|
|
150
|
+
export const mnemonicToOwnerSecret = (mnemonic) => OwnerSecret.orThrow(bip39.mnemonicToEntropy(mnemonic, wordlist));
|
|
138
151
|
/**
|
|
139
152
|
* Creates an {@link Owner} from a {@link OwnerSecret} using SLIP-21 key
|
|
140
153
|
* derivation.
|
|
@@ -192,7 +192,7 @@ export declare const testLocalOnlyEvoluSchema: {
|
|
|
192
192
|
readonly id: import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, "Id", import("../Type.ts").IdError>, "OwnerId", never>;
|
|
193
193
|
readonly encryptionKey: import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length32", import("../Type.ts").LengthError<32>>, "EncryptionKey", never>, "OwnerEncryptionKey", never>;
|
|
194
194
|
readonly writeKey: import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length16", import("../Type.ts").LengthError<16>>, "OwnerWriteKey", never>;
|
|
195
|
-
readonly secret: UnionType<readonly [import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length32", import("../Type.ts").LengthError<32
|
|
195
|
+
readonly secret: UnionType<readonly [import("../Type.ts").BrandType<UnionType<readonly [import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length16", import("../Type.ts").LengthError<16>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length20", import("../Type.ts").LengthError<20>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length24", import("../Type.ts").LengthError<24>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length28", import("../Type.ts").LengthError<28>>, import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"Uint8Array", Uint8Array<ArrayBufferLike>, Uint8Array<ArrayBufferLike>, import("../Type.ts").ObjectTagError<"Uint8Array">, null, import("../Type.ts").ObjectTagError<"Uint8Array">, never, Uint8Array<ArrayBufferLike>, true>, "Entropy", never>, "Length32", import("../Type.ts").LengthError<32>>]>, "OwnerSecret", never>, import("../Type.ts").LiteralType<null>]>;
|
|
196
196
|
readonly name: UnionType<readonly [import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").BrandType<import("../Type.ts").Type<"String", string, string, import("../Type.ts").TypeOfError<"String">, null, import("../Type.ts").TypeOfError<"String">, never, string, true>, "Trimmed", import("../Type.ts").TrimmedError>, "MinLength1", import("../Type.ts").MinLengthError<1>>, "MaxLength100", import("../Type.ts").MaxLengthError<100>>, import("../Type.ts").LiteralType<null>]>;
|
|
197
197
|
};
|
|
198
198
|
};
|
package/package.json
CHANGED
package/src/Crypto.ts
CHANGED
|
@@ -102,6 +102,14 @@ type Entropy = typeof Entropy.Output;
|
|
|
102
102
|
export const Entropy16 = /*#__PURE__*/ length(16)(Entropy);
|
|
103
103
|
export type Entropy16 = typeof Entropy16.Output;
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Cryptographic entropy of exactly 20 bytes.
|
|
107
|
+
*
|
|
108
|
+
* @group Random bytes
|
|
109
|
+
*/
|
|
110
|
+
export const Entropy20 = /*#__PURE__*/ length(20)(Entropy);
|
|
111
|
+
export type Entropy20 = typeof Entropy20.Output;
|
|
112
|
+
|
|
105
113
|
/**
|
|
106
114
|
* Cryptographic entropy of exactly 24 bytes.
|
|
107
115
|
*
|
|
@@ -110,6 +118,14 @@ export type Entropy16 = typeof Entropy16.Output;
|
|
|
110
118
|
export const Entropy24 = /*#__PURE__*/ length(24)(Entropy);
|
|
111
119
|
export type Entropy24 = typeof Entropy24.Output;
|
|
112
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Cryptographic entropy of exactly 28 bytes.
|
|
123
|
+
*
|
|
124
|
+
* @group Random bytes
|
|
125
|
+
*/
|
|
126
|
+
export const Entropy28 = /*#__PURE__*/ length(28)(Entropy);
|
|
127
|
+
export type Entropy28 = typeof Entropy28.Output;
|
|
128
|
+
|
|
113
129
|
/**
|
|
114
130
|
* Cryptographic entropy of exactly 32 bytes.
|
|
115
131
|
*
|
|
@@ -150,14 +166,18 @@ export const testCreateRandomBytes = (deps: RandomLibDep): RandomBytes =>
|
|
|
150
166
|
}) as RandomBytes;
|
|
151
167
|
|
|
152
168
|
/**
|
|
153
|
-
*
|
|
169
|
+
* Derives a 32-byte key from a seed and a path using
|
|
170
|
+
* [SLIP-21](https://github.com/satoshilabs/slips/blob/master/slip-0021.md).
|
|
154
171
|
*
|
|
155
|
-
*
|
|
172
|
+
* Derived keys are no stronger than the seed. A 16-byte seed gives 128 bits of
|
|
173
|
+
* security, or about 64 against a quantum computer, whatever the output length.
|
|
174
|
+
* Use a 32-byte random seed for keys that must stay safe against a quantum
|
|
175
|
+
* computer.
|
|
156
176
|
*
|
|
157
177
|
* @group Key derivation
|
|
158
178
|
*/
|
|
159
179
|
export const createSlip21 = (
|
|
160
|
-
seed: Entropy16 | Entropy32 | Entropy64,
|
|
180
|
+
seed: Entropy16 | Entropy20 | Entropy24 | Entropy28 | Entropy32 | Entropy64,
|
|
161
181
|
path: ReadonlyArray<string | number>,
|
|
162
182
|
): Entropy32 => {
|
|
163
183
|
let currentNode = hmac(
|
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
import { bytesToHex } from "@noble/hashes/utils.js";
|
|
2
|
+
import * as bip39 from "@scure/bip39";
|
|
3
|
+
import { wordlist } from "@scure/bip39/wordlists/english.js";
|
|
1
4
|
import { eqData } from "../Eq.ts";
|
|
2
5
|
import { test } from "node:test";
|
|
3
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
assertEqual,
|
|
8
|
+
assertFalse,
|
|
9
|
+
assertNotUndefined,
|
|
10
|
+
assertTrue,
|
|
11
|
+
} from "../Assert.ts";
|
|
4
12
|
|
|
5
13
|
import {
|
|
6
14
|
createAppOwner,
|
|
7
15
|
createOwnerSecret,
|
|
8
16
|
deriveShardOwner,
|
|
9
17
|
mnemonicToOwnerSecret,
|
|
18
|
+
OwnerSecret,
|
|
10
19
|
ownerIdBytesToOwnerId,
|
|
11
20
|
ownerIdToOwnerIdBytes,
|
|
12
21
|
ownerSecretToMnemonic,
|
|
@@ -14,6 +23,7 @@ import {
|
|
|
14
23
|
testOwnerSecret,
|
|
15
24
|
} from "./Owner.ts";
|
|
16
25
|
import { testCreateDeps } from "../Task.ts";
|
|
26
|
+
import { Mnemonic } from "../Type.ts";
|
|
17
27
|
|
|
18
28
|
const testOwnerSecret2 = createOwnerSecret(testCreateDeps({ seed: "owner-2" }));
|
|
19
29
|
|
|
@@ -31,6 +41,47 @@ test("ownerSecretToMnemonic and mnemonicToOwnerSecret are inverses", () => {
|
|
|
31
41
|
assertEqual(backToSecret, secret);
|
|
32
42
|
});
|
|
33
43
|
|
|
44
|
+
test("mnemonicToOwnerSecret converts every BIP-39 mnemonic length", () => {
|
|
45
|
+
for (const [words, bytes] of [
|
|
46
|
+
[12, 16],
|
|
47
|
+
[15, 20],
|
|
48
|
+
[18, 24],
|
|
49
|
+
[21, 28],
|
|
50
|
+
[24, 32],
|
|
51
|
+
] as const) {
|
|
52
|
+
const entropy = new Uint8Array(bytes).fill(words);
|
|
53
|
+
const mnemonic = Mnemonic.orThrow(
|
|
54
|
+
bip39.entropyToMnemonic(entropy, wordlist),
|
|
55
|
+
);
|
|
56
|
+
const secret = mnemonicToOwnerSecret(mnemonic);
|
|
57
|
+
|
|
58
|
+
assertEqual(mnemonic.split(" ").length, words);
|
|
59
|
+
assertTrue(OwnerSecret.is(secret));
|
|
60
|
+
assertEqual(secret, entropy);
|
|
61
|
+
assertEqual(ownerSecretToMnemonic(secret), mnemonic);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("OwnerSecret rejects lengths that no mnemonic holds", () => {
|
|
66
|
+
assertFalse(OwnerSecret.is(new Uint8Array(17)));
|
|
67
|
+
assertFalse(OwnerSecret.is(new Uint8Array(64)));
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("createAppOwner derives fixed keys from a 12-word mnemonic", () => {
|
|
71
|
+
const owner = createAppOwner(
|
|
72
|
+
mnemonicToOwnerSecret(
|
|
73
|
+
Mnemonic.orThrow("all all all all all all all all all all all all"),
|
|
74
|
+
),
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
assertEqual(owner.id, "njGMKFwCtldekpIYmB-VKA");
|
|
78
|
+
assertEqual(
|
|
79
|
+
bytesToHex(owner.encryptionKey),
|
|
80
|
+
"d9cae8d1e141e9f4824ba9b56b9b991d242c1a37f48a146053c3d69f432c5599",
|
|
81
|
+
);
|
|
82
|
+
assertEqual(bytesToHex(owner.writeKey), "8d77d62517c4d525223647a189261ab2");
|
|
83
|
+
});
|
|
84
|
+
|
|
34
85
|
test("createAppOwner is deterministic", () => {
|
|
35
86
|
const owner1 = createAppOwner(testOwnerSecret);
|
|
36
87
|
const owner2 = createAppOwner(testOwnerSecret);
|
package/src/local-first/Owner.ts
CHANGED
|
@@ -48,6 +48,9 @@ import {
|
|
|
48
48
|
createSlip21,
|
|
49
49
|
EncryptionKey,
|
|
50
50
|
Entropy16,
|
|
51
|
+
Entropy20,
|
|
52
|
+
Entropy24,
|
|
53
|
+
Entropy28,
|
|
51
54
|
Entropy32,
|
|
52
55
|
testCreateRandomBytes,
|
|
53
56
|
} from "../Crypto.ts";
|
|
@@ -62,6 +65,7 @@ import {
|
|
|
62
65
|
Mnemonic,
|
|
63
66
|
NonNegativeInt,
|
|
64
67
|
type Typed,
|
|
68
|
+
union,
|
|
65
69
|
} from "../Type.ts";
|
|
66
70
|
import type { EncryptedDbChange, Storage } from "./Storage.ts";
|
|
67
71
|
import { TimestampBytes } from "./Timestamp.ts";
|
|
@@ -172,18 +176,33 @@ export const createOwnerWriteKey = (deps: RandomBytesDep): OwnerWriteKey =>
|
|
|
172
176
|
deps.randomBytes.create(16) as OwnerWriteKey;
|
|
173
177
|
|
|
174
178
|
/**
|
|
175
|
-
*
|
|
179
|
+
* BIP-39 entropy used to derive {@link Owner} keys.
|
|
176
180
|
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
181
|
+
* A {@link Mnemonic} represents an OwnerSecret, so each valid mnemonic converts
|
|
182
|
+
* to exactly one secret with {@link mnemonicToOwnerSecret} and back with
|
|
183
|
+
* {@link ownerSecretToMnemonic}. A 24-word mnemonic holds 32 bytes, a 12-word
|
|
184
|
+
* mnemonic holds 16 bytes, and 15, 18, and 21 words hold 20, 24, and 28 bytes.
|
|
185
|
+
*
|
|
186
|
+
* {@link createOwnerSecret} generates 32 random bytes, which keep the owner
|
|
187
|
+
* post-quantum safe. The {@link OwnerId} is public and derived from the secret,
|
|
188
|
+
* so anyone who stores an owner's encrypted data can test guesses of the secret
|
|
189
|
+
* offline, and a quantum computer needs only about the square root of the
|
|
190
|
+
* guesses. 32 random bytes leave about 128 bits of security, while 16 bytes
|
|
191
|
+
* leave about 64. A secret derived from a smaller root, such as a 20-word
|
|
192
|
+
* SLIP-39 share, is no stronger than that root, whatever its length. Shorter
|
|
193
|
+
* secrets are accepted for owners created outside Evolu, typically from 12-word
|
|
194
|
+
* mnemonics.
|
|
179
195
|
*
|
|
180
196
|
* @group Core
|
|
181
197
|
*/
|
|
182
|
-
export const OwnerSecret = /*#__PURE__*/ brand(
|
|
198
|
+
export const OwnerSecret = /*#__PURE__*/ brand(
|
|
199
|
+
"OwnerSecret",
|
|
200
|
+
/*#__PURE__*/ union(Entropy16, Entropy20, Entropy24, Entropy28, Entropy32),
|
|
201
|
+
);
|
|
183
202
|
export type OwnerSecret = typeof OwnerSecret.Output;
|
|
184
203
|
|
|
185
204
|
/**
|
|
186
|
-
* Creates a cryptographically random {@link OwnerSecret}.
|
|
205
|
+
* Creates a cryptographically random 32-byte {@link OwnerSecret}.
|
|
187
206
|
*
|
|
188
207
|
* @group Core
|
|
189
208
|
*/
|
|
@@ -210,12 +229,12 @@ export const ownerSecretToMnemonic = (secret: OwnerSecret): Mnemonic =>
|
|
|
210
229
|
bip39.entropyToMnemonic(secret, wordlist) as Mnemonic;
|
|
211
230
|
|
|
212
231
|
/**
|
|
213
|
-
* Converts a {@link Mnemonic} to
|
|
232
|
+
* Converts a {@link Mnemonic} to the {@link OwnerSecret} it represents.
|
|
214
233
|
*
|
|
215
234
|
* @group Core
|
|
216
235
|
*/
|
|
217
236
|
export const mnemonicToOwnerSecret = (mnemonic: Mnemonic): OwnerSecret =>
|
|
218
|
-
bip39.mnemonicToEntropy(mnemonic, wordlist)
|
|
237
|
+
OwnerSecret.orThrow(bip39.mnemonicToEntropy(mnemonic, wordlist));
|
|
219
238
|
|
|
220
239
|
/**
|
|
221
240
|
* Creates an {@link Owner} from a {@link OwnerSecret} using SLIP-21 key
|