@dstorage-tech/dstorage-sdk 0.0.4
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 +530 -0
- package/dist/KeypairEncryptionAdapter-AE75YC4W-AOWNSH2M.mjs +9 -0
- package/dist/KeypairEncryptionAdapter-AE75YC4W-BAVHRNNJ.mjs +1 -0
- package/dist/KeypairEncryptionAdapter-AE75YC4W-BEYO4RUO.mjs +1 -0
- package/dist/KeypairEncryptionAdapter-AE75YC4W-VAOH6AAL.mjs +9 -0
- package/dist/KeypairEncryptionAdapter-XUKCLRFU-6ERIZN2M.mjs +1 -0
- package/dist/KeypairEncryptionAdapter-XUKCLRFU-AYDAZ2TZ.mjs +9 -0
- package/dist/KeypairEncryptionAdapter-XUKCLRFU-ERPYK5S4.mjs +8 -0
- package/dist/MnemonicEncryptionAdapter-J6IE5OKA-6DFVUVD6.mjs +1 -0
- package/dist/MnemonicEncryptionAdapter-J6IE5OKA-O5AJPX42.mjs +1 -0
- package/dist/MnemonicEncryptionAdapter-J6IE5OKA-SI6TBLTQ.mjs +9 -0
- package/dist/MnemonicEncryptionAdapter-J6IE5OKA-WEPFOTSJ.mjs +9 -0
- package/dist/MnemonicEncryptionAdapter-RCHUETBP-DZPBGQG6.mjs +8 -0
- package/dist/MnemonicEncryptionAdapter-RCHUETBP-ELAMJMMO.mjs +1 -0
- package/dist/MnemonicEncryptionAdapter-RCHUETBP-M7NHGSHO.mjs +9 -0
- package/dist/PasswordEncryptionAdapter-CWOXUGR4-2YZ64B4A.mjs +1 -0
- package/dist/PasswordEncryptionAdapter-CWOXUGR4-DCEGC2NX.mjs +1 -0
- package/dist/PasswordEncryptionAdapter-CWOXUGR4-QEGGYPFJ.mjs +13 -0
- package/dist/PasswordEncryptionAdapter-CWOXUGR4-ZYUBUYUI.mjs +13 -0
- package/dist/PasswordEncryptionAdapter-ECHND672-3DEFO6FP.mjs +13 -0
- package/dist/PasswordEncryptionAdapter-ECHND672-MUTHAMZY.mjs +12 -0
- package/dist/PasswordEncryptionAdapter-ECHND672-XVBPHPPT.mjs +1 -0
- package/dist/browser.d.mts +2418 -0
- package/dist/browser.mjs +5175 -0
- package/dist/chunk-25I6ZXUC.mjs +9403 -0
- package/dist/chunk-4X5K43JF.mjs +62 -0
- package/dist/chunk-4YMF57VY.mjs +1 -0
- package/dist/chunk-5MZ7FPPY.mjs +1 -0
- package/dist/chunk-5P4EW2YL.mjs +1 -0
- package/dist/chunk-5QOKJEH3.mjs +8 -0
- package/dist/chunk-5RW7SGA7.mjs +1 -0
- package/dist/chunk-62RSKZ6T.mjs +118 -0
- package/dist/chunk-6IAQVMSM.mjs +102 -0
- package/dist/chunk-BBJCQFWP.mjs +275 -0
- package/dist/chunk-BWLEAAFW.mjs +2599 -0
- package/dist/chunk-EYF3263P.mjs +2049 -0
- package/dist/chunk-FJ7LSZOW.mjs +5537 -0
- package/dist/chunk-GUYENY5N.mjs +1 -0
- package/dist/chunk-HDD6SUU4.mjs +276 -0
- package/dist/chunk-JMPRZK5D.mjs +62 -0
- package/dist/chunk-L5QD6GHB.mjs +101 -0
- package/dist/chunk-O37NHERH.mjs +17 -0
- package/dist/chunk-OLGVVM5Y.mjs +102 -0
- package/dist/chunk-PKHRAYFD.mjs +1 -0
- package/dist/chunk-PLAOCKSS.mjs +101 -0
- package/dist/chunk-QKA2DCR6.mjs +118 -0
- package/dist/chunk-RPSRFFXN.mjs +275 -0
- package/dist/chunk-TESUOXRJ.mjs +1 -0
- package/dist/chunk-TMLXCRJU.mjs +1 -0
- package/dist/chunk-TNRKN4LB.mjs +1 -0
- package/dist/chunk-TSZ37JDY.mjs +2596 -0
- package/dist/chunk-U3MS6KMF.mjs +1 -0
- package/dist/chunk-UBALMYIY.mjs +2049 -0
- package/dist/chunk-UDYXMII2.mjs +121 -0
- package/dist/chunk-UJCSKKID.mjs +30 -0
- package/dist/chunk-UOOF4V2Y.mjs +1 -0
- package/dist/chunk-VDZ7F7MP.mjs +1329 -0
- package/dist/chunk-X4LEBNK6.mjs +276 -0
- package/dist/chunk-Z2TH6U4C.mjs +3 -0
- package/dist/chunk-Z6DUAI5B.mjs +121 -0
- package/dist/chunk-ZEPZ4QDD.mjs +1 -0
- package/dist/chunk-ZGUY5EIR.mjs +1 -0
- package/dist/cjs-RMZ3L2IO.mjs +5 -0
- package/dist/contract-api-AO4RGPLT-4HRCMSCE.mjs +279 -0
- package/dist/contract-api-AO4RGPLT-ADH6ZDKC.mjs +279 -0
- package/dist/contract-api-AO4RGPLT-ENC72WCG.mjs +280 -0
- package/dist/contract-api-AO4RGPLT-PAMIPEI3.mjs +1 -0
- package/dist/contract-api-AO4RGPLT-W3RSVP5A.mjs +1 -0
- package/dist/contract-api-AO4RGPLT-WENYDV6I.mjs +1 -0
- package/dist/contract-api-TJ7JPHZA-6FUBJ6DS.mjs +279 -0
- package/dist/contract-api-TJ7JPHZA-ZWP3EH7D.mjs +279 -0
- package/dist/contracts/dataregistry/managed/keys/removeReference.prover +0 -0
- package/dist/contracts/dataregistry/managed/keys/removeReference.verifier +0 -0
- package/dist/contracts/dataregistry/managed/keys/storeReference.prover +0 -0
- package/dist/contracts/dataregistry/managed/keys/storeReference.verifier +0 -0
- package/dist/contracts/dataregistry/managed/keys/updateReference.prover +0 -0
- package/dist/contracts/dataregistry/managed/keys/updateReference.verifier +0 -0
- package/dist/contracts/dataregistry/managed/zkir/removeReference.bzkir +0 -0
- package/dist/contracts/dataregistry/managed/zkir/removeReference.zkir +138 -0
- package/dist/contracts/dataregistry/managed/zkir/storeReference.bzkir +0 -0
- package/dist/contracts/dataregistry/managed/zkir/storeReference.zkir +158 -0
- package/dist/contracts/dataregistry/managed/zkir/updateReference.bzkir +0 -0
- package/dist/contracts/dataregistry/managed/zkir/updateReference.zkir +190 -0
- package/dist/dataregistry-simulator-E6RTGGWT-5YEGBW7P.mjs +108 -0
- package/dist/dataregistry-simulator-E6RTGGWT-DJS4MHBD.mjs +109 -0
- package/dist/dataregistry-simulator-E6RTGGWT-QGCYR3VF.mjs +1 -0
- package/dist/dist-655VYUPJ.mjs +1 -0
- package/dist/dist-INTAFXQ7.mjs +65 -0
- package/dist/dist-MEZZWKP4.mjs +1 -0
- package/dist/dist-MOLWJ4Y7.mjs +64 -0
- package/dist/dist-UD4WRMYJ.mjs +65 -0
- package/dist/errors.d.mts +117 -0
- package/dist/errors.d.ts +117 -0
- package/dist/errors.js +2308 -0
- package/dist/errors.mjs +2278 -0
- package/dist/esm5-AGBDHMNS.mjs +1 -0
- package/dist/esm5-GFOBLUFI.mjs +350 -0
- package/dist/in-memory-private-state-NGOP37V7-DXWZE42Q.mjs +48 -0
- package/dist/in-memory-private-state-NGOP37V7-QXT3RUEH.mjs +49 -0
- package/dist/in-memory-private-state-NGOP37V7-XFJFNFCQ.mjs +1 -0
- package/dist/index.d.mts +2843 -0
- package/dist/index.d.ts +2843 -0
- package/dist/index.js +19639 -0
- package/dist/index.mjs +5177 -0
- package/package.json +87 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{a as n}from"./chunk-PKHRAYFD.mjs";import{a as i,q as p,r as w,s as h}from"./chunk-EYF3263P.mjs";import{scrypt as S}from"@noble/hashes/scrypt.js";var d={v1:{N:131072,r:8,p:1},"v1-lite":{N:65536,r:8,p:1}},N=class m{constructor(t){this.name="password",O(t.password,t.minPasswordLength??12,t.minEntropyBits??60,t.additionalValidators),this.salt=P(t.salt);let r=A(t.preset,t.params);this.N=r.N,this.r=r.r,this.p=r.p;let a=new TextEncoder().encode(t.password),s=S(a,this.salt,{N:this.N,r:this.r,p:this.p,dkLen:64});this.kekMaterialPromise=globalThis.crypto.subtle.importKey("raw",s,"HKDF",!1,["deriveBits"]).then(o=>(s.fill(0),o),o=>{throw s.fill(0),o})}static fromKdfParams(t){if(t.kdf!=="scrypt")throw new i(n.PASSWORD_UNSUPPORTED_KDF,`[dStorage] PasswordEncryptionAdapter.fromKdfParams: unsupported kdf "${t.kdf}". Only "scrypt" is supported.`);return new m({password:t.password,salt:t.salt,params:{N:t.N,r:t.r,p:t.p},...t.minPasswordLength!==void 0&&{minPasswordLength:t.minPasswordLength},...t.minEntropyBits!==void 0&&{minEntropyBits:t.minEntropyBits},...t.additionalValidators!==void 0&&{additionalValidators:t.additionalValidators}})}getKdfParams(){return{kdf:"scrypt",N:this.N,r:this.r,p:this.p}}async wrapDek(t){let r=await this.kekMaterialPromise,a=await p(r);try{return{algorithm:"xchacha20poly1305-v1",kekDerivation:"hkdf-sha256-v1",wrappedDek:await w(t,a)}}finally{a.fill(0)}}async unwrapDek({wrappedDek:t}){let r=await this.kekMaterialPromise,a=await p(r);try{return h(t,a)}finally{a.fill(0)}}async intoKeypairEncryptionAdapter(t,r){let{KeypairEncryptionAdapter:a}=await import("./KeypairEncryptionAdapter-XUKCLRFU-6ERIZN2M.mjs"),{mlkemGenerateKeypairFromSeed:s}=await import("./dist-MEZZWKP4.mjs"),o=await this.kekMaterialPromise,l=await globalThis.crypto.subtle.deriveBits({name:"HKDF",hash:"SHA-256",salt:new Uint8Array(32),info:new TextEncoder().encode("dstorage:mlkem-seed:v1")},o,512),c=new Uint8Array(l);try{let{pk:u,sk:k}=s(c,r);return a.fromKeypair(u,k,t,r)}finally{c.fill(0)}}};function P(e){let t=typeof e=="string"?new TextEncoder().encode(e):e;if(t.length===0)throw new i(n.PASSWORD_EMPTY_SALT,'[dStorage] PasswordEncryptionAdapter: salt must not be empty. Provide an app-specific domain label, e.g. "myapp:v1:alice@example.com".');return t}function A(e,t){if(e!==void 0){if(!(e in d))throw new i(n.PASSWORD_UNKNOWN_PRESET,`[dStorage] PasswordEncryptionAdapter: unknown preset "${e}". Valid presets: ${Object.keys(d).join(", ")}.`);return d[e]}return{N:t?.N??d.v1.N,r:t?.r??d.v1.r,p:t?.p??d.v1.p}}function E(e){let t=0;return/[a-z]/.test(e)&&(t+=26),/[A-Z]/.test(e)&&(t+=26),/[0-9]/.test(e)&&(t+=10),/[^a-zA-Z0-9]/.test(e)&&(t+=32),t===0?0:Math.log2(t)*e.length}function y(e,t=5){for(let r=0;r<=e.length-t;r++){let a=!0,s=!0;for(let o=1;o<t;o++){let l=e.charCodeAt(r+o)-e.charCodeAt(r+o-1);if(l!==1&&(a=!1),l!==-1&&(s=!1),!a&&!s)break}if(a||s)return!0}return!1}var v=["qwertyuiop","asdfghjkl","zxcvbnm","1234567890"];function f(e,t=5){let r=e.toLowerCase();for(let a of v)for(let s=0;s<=a.length-t;s++){let o=a.slice(s,s+t),l=o.split("").reverse().join("");if(r.includes(o)||r.includes(l))return!0}return!1}function _(){for(;;){let e=globalThis.crypto.getRandomValues(new Uint8Array(32)),r=btoa(Array.from(e,a=>String.fromCharCode(a)).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"");if(new Set(r).size>=3&&!y(r)&&!f(r))return r}}function O(e,t,r,a){if(e.length<t)throw new i(n.PASSWORD_TOO_SHORT,`[dStorage] Password too short: ${e.length} character(s) (minimum ${t}). Use a longer passphrase.`);if(new Set(e).size<3)throw new i(n.PASSWORD_TOO_FEW_DISTINCT,"[dStorage] Password is too weak: too few distinct characters.");if(y(e))throw new i(n.PASSWORD_SEQUENTIAL_RUN,"[dStorage] Password is too weak: contains a sequential character run (e.g. abcde, 12345).");if(f(e))throw new i(n.PASSWORD_KEYBOARD_WALK,"[dStorage] Password is too weak: contains a keyboard-row pattern (e.g. qwerty, asdfg).");let s=E(e);if(s<r)throw new i(n.PASSWORD_ENTROPY_TOO_LOW,`[dStorage] Password entropy too low: ~${Math.round(s)} bits (minimum ${r} bits). Use a longer password with a mix of uppercase, lowercase, digits, and symbols.`);if(a)for(let o of a)o(e)}export{d as a,N as b,_ as c};
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EncryptionErrorCode
|
|
3
|
+
} from "./chunk-O37NHERH.mjs";
|
|
4
|
+
import {
|
|
5
|
+
DStorageError,
|
|
6
|
+
deriveKek,
|
|
7
|
+
unwrapKey,
|
|
8
|
+
wrapKey
|
|
9
|
+
} from "./chunk-BWLEAAFW.mjs";
|
|
10
|
+
|
|
11
|
+
// ../encryption/dist/chunk-6IHXTW2Y.mjs
|
|
12
|
+
import { scrypt } from "@noble/hashes/scrypt.js";
|
|
13
|
+
var KDF_PRESETS = {
|
|
14
|
+
v1: { N: 131072, r: 8, p: 1 },
|
|
15
|
+
"v1-lite": { N: 65536, r: 8, p: 1 }
|
|
16
|
+
};
|
|
17
|
+
var PasswordEncryptionAdapter = class _PasswordEncryptionAdapter {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.name = "password";
|
|
20
|
+
validatePassword(
|
|
21
|
+
config.password,
|
|
22
|
+
config.minPasswordLength ?? 12,
|
|
23
|
+
config.minEntropyBits ?? 60,
|
|
24
|
+
config.additionalValidators
|
|
25
|
+
);
|
|
26
|
+
this.salt = resolveSalt(config.salt);
|
|
27
|
+
const resolved = resolveParams(config.preset, config.params);
|
|
28
|
+
this.N = resolved.N;
|
|
29
|
+
this.r = resolved.r;
|
|
30
|
+
this.p = resolved.p;
|
|
31
|
+
const passwordBytes = new TextEncoder().encode(config.password);
|
|
32
|
+
const seed = scrypt(passwordBytes, this.salt, {
|
|
33
|
+
N: this.N,
|
|
34
|
+
r: this.r,
|
|
35
|
+
p: this.p,
|
|
36
|
+
dkLen: 64
|
|
37
|
+
});
|
|
38
|
+
this.kekMaterialPromise = globalThis.crypto.subtle.importKey("raw", seed, "HKDF", false, ["deriveBits"]).then(
|
|
39
|
+
(key) => {
|
|
40
|
+
seed.fill(0);
|
|
41
|
+
return key;
|
|
42
|
+
},
|
|
43
|
+
(err) => {
|
|
44
|
+
seed.fill(0);
|
|
45
|
+
throw err;
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Reconstruct an adapter from parameters previously stored via `getKdfParams()`.
|
|
51
|
+
* Use this when loading existing encrypted data — ensures the exact same N/r/p
|
|
52
|
+
* are used regardless of what the current SDK defaults are.
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const adapter = PasswordEncryptionAdapter.fromKdfParams({
|
|
56
|
+
* password,
|
|
57
|
+
* salt: meta.salt,
|
|
58
|
+
* kdf: meta.kdf,
|
|
59
|
+
* N: meta.N,
|
|
60
|
+
* r: meta.r,
|
|
61
|
+
* p: meta.p,
|
|
62
|
+
* });
|
|
63
|
+
*/
|
|
64
|
+
static fromKdfParams(config) {
|
|
65
|
+
if (config.kdf !== "scrypt") {
|
|
66
|
+
throw new DStorageError(
|
|
67
|
+
EncryptionErrorCode.PASSWORD_UNSUPPORTED_KDF,
|
|
68
|
+
`[dStorage] PasswordEncryptionAdapter.fromKdfParams: unsupported kdf "${config.kdf}". Only "scrypt" is supported.`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
return new _PasswordEncryptionAdapter({
|
|
72
|
+
password: config.password,
|
|
73
|
+
salt: config.salt,
|
|
74
|
+
params: { N: config.N, r: config.r, p: config.p },
|
|
75
|
+
...config.minPasswordLength !== void 0 && {
|
|
76
|
+
minPasswordLength: config.minPasswordLength
|
|
77
|
+
},
|
|
78
|
+
...config.minEntropyBits !== void 0 && {
|
|
79
|
+
minEntropyBits: config.minEntropyBits
|
|
80
|
+
},
|
|
81
|
+
...config.additionalValidators !== void 0 && {
|
|
82
|
+
additionalValidators: config.additionalValidators
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Returns the resolved KDF parameters to store alongside encrypted data.
|
|
88
|
+
*
|
|
89
|
+
* These values are language- and SDK-agnostic: any scrypt implementation
|
|
90
|
+
* can re-derive the key from `{ kdf, N, r, p }` + the original salt and
|
|
91
|
+
* password, without any knowledge of this SDK's preset names.
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* const meta = {
|
|
95
|
+
* ...adapter.getKdfParams(), // { kdf: "scrypt", N: 131072, r: 8, p: 1 }
|
|
96
|
+
* salt: "myapp:alice@example.com",
|
|
97
|
+
* };
|
|
98
|
+
* await db.save({ encryptedData, meta });
|
|
99
|
+
*/
|
|
100
|
+
getKdfParams() {
|
|
101
|
+
return { kdf: "scrypt", N: this.N, r: this.r, p: this.p };
|
|
102
|
+
}
|
|
103
|
+
async wrapDek(dek) {
|
|
104
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
105
|
+
const kek = await deriveKek(kekMaterial);
|
|
106
|
+
try {
|
|
107
|
+
return {
|
|
108
|
+
algorithm: "xchacha20poly1305-v1",
|
|
109
|
+
kekDerivation: "hkdf-sha256-v1",
|
|
110
|
+
wrappedDek: await wrapKey(dek, kek)
|
|
111
|
+
};
|
|
112
|
+
} finally {
|
|
113
|
+
kek.fill(0);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
async unwrapDek({ wrappedDek }) {
|
|
117
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
118
|
+
const kek = await deriveKek(kekMaterial);
|
|
119
|
+
try {
|
|
120
|
+
return unwrapKey(wrappedDek, kek);
|
|
121
|
+
} finally {
|
|
122
|
+
kek.fill(0);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Deterministically derive a ML-KEM keypair from this adapter's key material.
|
|
127
|
+
*
|
|
128
|
+
* Uses Web Crypto HKDF to derive a 64-byte ML-KEM seed from the stored
|
|
129
|
+
* CryptoKey, domain-separated from the symmetric KEK derivation path with
|
|
130
|
+
* info="dstorage:mlkem-seed:v1". This is equivalent to noble's
|
|
131
|
+
* hkdf(sha256, scryptOutput, undefined, info, 64) but avoids materialising
|
|
132
|
+
* the raw scrypt output in a JS-accessible variable.
|
|
133
|
+
*
|
|
134
|
+
* Same password + salt + variant always produces the same keypair.
|
|
135
|
+
*
|
|
136
|
+
* @param context Domain-separation string bound into the KEK derivation
|
|
137
|
+
* @param variant ML-KEM variant. Default: "mlkem768".
|
|
138
|
+
*/
|
|
139
|
+
async intoKeypairEncryptionAdapter(context, variant) {
|
|
140
|
+
const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-XUKCLRFU-ERPYK5S4.mjs");
|
|
141
|
+
const { mlkemGenerateKeypairFromSeed } = await import("./dist-MOLWJ4Y7.mjs");
|
|
142
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
143
|
+
const mlkemBits = await globalThis.crypto.subtle.deriveBits(
|
|
144
|
+
{
|
|
145
|
+
name: "HKDF",
|
|
146
|
+
hash: "SHA-256",
|
|
147
|
+
salt: new Uint8Array(32),
|
|
148
|
+
info: new TextEncoder().encode("dstorage:mlkem-seed:v1")
|
|
149
|
+
},
|
|
150
|
+
kekMaterial,
|
|
151
|
+
512
|
|
152
|
+
// 64 bytes
|
|
153
|
+
);
|
|
154
|
+
const mlkemSeed = new Uint8Array(mlkemBits);
|
|
155
|
+
try {
|
|
156
|
+
const { pk, sk } = mlkemGenerateKeypairFromSeed(mlkemSeed, variant);
|
|
157
|
+
return KeypairEncryptionAdapter.fromKeypair(pk, sk, context, variant);
|
|
158
|
+
} finally {
|
|
159
|
+
mlkemSeed.fill(0);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
function resolveSalt(salt) {
|
|
164
|
+
const bytes = typeof salt === "string" ? new TextEncoder().encode(salt) : salt;
|
|
165
|
+
if (bytes.length === 0) {
|
|
166
|
+
throw new DStorageError(
|
|
167
|
+
EncryptionErrorCode.PASSWORD_EMPTY_SALT,
|
|
168
|
+
'[dStorage] PasswordEncryptionAdapter: salt must not be empty. Provide an app-specific domain label, e.g. "myapp:v1:alice@example.com".'
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
return bytes;
|
|
172
|
+
}
|
|
173
|
+
function resolveParams(preset, params) {
|
|
174
|
+
if (preset !== void 0) {
|
|
175
|
+
if (!(preset in KDF_PRESETS)) {
|
|
176
|
+
throw new DStorageError(
|
|
177
|
+
EncryptionErrorCode.PASSWORD_UNKNOWN_PRESET,
|
|
178
|
+
`[dStorage] PasswordEncryptionAdapter: unknown preset "${preset}". Valid presets: ${Object.keys(KDF_PRESETS).join(", ")}.`
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
return KDF_PRESETS[preset];
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
N: params?.N ?? KDF_PRESETS["v1"].N,
|
|
185
|
+
r: params?.r ?? KDF_PRESETS["v1"].r,
|
|
186
|
+
p: params?.p ?? KDF_PRESETS["v1"].p
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
function estimateEntropyBits(password) {
|
|
190
|
+
let pool = 0;
|
|
191
|
+
if (/[a-z]/.test(password)) pool += 26;
|
|
192
|
+
if (/[A-Z]/.test(password)) pool += 26;
|
|
193
|
+
if (/[0-9]/.test(password)) pool += 10;
|
|
194
|
+
if (/[^a-zA-Z0-9]/.test(password)) pool += 32;
|
|
195
|
+
return pool === 0 ? 0 : Math.log2(pool) * password.length;
|
|
196
|
+
}
|
|
197
|
+
function hasSequentialRun(pw, runLength = 5) {
|
|
198
|
+
for (let i = 0; i <= pw.length - runLength; i++) {
|
|
199
|
+
let ascending = true;
|
|
200
|
+
let descending = true;
|
|
201
|
+
for (let j = 1; j < runLength; j++) {
|
|
202
|
+
const diff = pw.charCodeAt(i + j) - pw.charCodeAt(i + j - 1);
|
|
203
|
+
if (diff !== 1) ascending = false;
|
|
204
|
+
if (diff !== -1) descending = false;
|
|
205
|
+
if (!ascending && !descending) break;
|
|
206
|
+
}
|
|
207
|
+
if (ascending || descending) return true;
|
|
208
|
+
}
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
var KEYBOARD_ROWS = ["qwertyuiop", "asdfghjkl", "zxcvbnm", "1234567890"];
|
|
212
|
+
function hasKeyboardWalk(pw, minLen = 5) {
|
|
213
|
+
const lower = pw.toLowerCase();
|
|
214
|
+
for (const row of KEYBOARD_ROWS) {
|
|
215
|
+
for (let i = 0; i <= row.length - minLen; i++) {
|
|
216
|
+
const fwd = row.slice(i, i + minLen);
|
|
217
|
+
const rev = fwd.split("").reverse().join("");
|
|
218
|
+
if (lower.includes(fwd) || lower.includes(rev)) return true;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
function generatePqsPassword() {
|
|
224
|
+
while (true) {
|
|
225
|
+
const bytes = globalThis.crypto.getRandomValues(new Uint8Array(32));
|
|
226
|
+
const b64 = btoa(Array.from(bytes, (b) => String.fromCharCode(b)).join(""));
|
|
227
|
+
const password = b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
228
|
+
if (new Set(password).size >= 3 && !hasSequentialRun(password) && !hasKeyboardWalk(password)) {
|
|
229
|
+
return password;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
function validatePassword(password, minLength, minEntropyBits, additionalValidators) {
|
|
234
|
+
if (password.length < minLength) {
|
|
235
|
+
throw new DStorageError(
|
|
236
|
+
EncryptionErrorCode.PASSWORD_TOO_SHORT,
|
|
237
|
+
`[dStorage] Password too short: ${password.length} character(s) (minimum ${minLength}). Use a longer passphrase.`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
if (new Set(password).size < 3) {
|
|
241
|
+
throw new DStorageError(
|
|
242
|
+
EncryptionErrorCode.PASSWORD_TOO_FEW_DISTINCT,
|
|
243
|
+
"[dStorage] Password is too weak: too few distinct characters."
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
if (hasSequentialRun(password)) {
|
|
247
|
+
throw new DStorageError(
|
|
248
|
+
EncryptionErrorCode.PASSWORD_SEQUENTIAL_RUN,
|
|
249
|
+
"[dStorage] Password is too weak: contains a sequential character run (e.g. abcde, 12345)."
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
if (hasKeyboardWalk(password)) {
|
|
253
|
+
throw new DStorageError(
|
|
254
|
+
EncryptionErrorCode.PASSWORD_KEYBOARD_WALK,
|
|
255
|
+
"[dStorage] Password is too weak: contains a keyboard-row pattern (e.g. qwerty, asdfg)."
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
const entropy = estimateEntropyBits(password);
|
|
259
|
+
if (entropy < minEntropyBits) {
|
|
260
|
+
throw new DStorageError(
|
|
261
|
+
EncryptionErrorCode.PASSWORD_ENTROPY_TOO_LOW,
|
|
262
|
+
`[dStorage] Password entropy too low: ~${Math.round(entropy)} bits (minimum ${minEntropyBits} bits). Use a longer password with a mix of uppercase, lowercase, digits, and symbols.`
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (additionalValidators) {
|
|
266
|
+
for (const validate of additionalValidators) {
|
|
267
|
+
validate(password);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export {
|
|
273
|
+
KDF_PRESETS,
|
|
274
|
+
PasswordEncryptionAdapter,
|
|
275
|
+
generatePqsPassword
|
|
276
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// ../chain/dist/chunk-AGLD4HA4.mjs
|
|
2
|
+
var ChainErrorCode = {
|
|
3
|
+
// mock.ts — 11001-11009
|
|
4
|
+
MOCK_REF_NOT_FOUND: 11001,
|
|
5
|
+
MOCK_REF_NOT_FOUND_REMOVE: 11002,
|
|
6
|
+
MOCK_REF_NOT_FOUND_UPDATE: 11003,
|
|
7
|
+
MOCK_OWNER_SECRET_REQUIRED: 11004,
|
|
8
|
+
MOCK_OWNER_SECRET_MISMATCH: 11005,
|
|
9
|
+
// httpGateway.ts — 11201-11219
|
|
10
|
+
GATEWAY_WRITE_FAILED: 11201,
|
|
11
|
+
GATEWAY_WRITE_HTTP_ERROR: 11202,
|
|
12
|
+
GATEWAY_UPDATE_FAILED: 11203,
|
|
13
|
+
GATEWAY_UPDATE_HTTP_ERROR: 11204,
|
|
14
|
+
GATEWAY_READ_FAILED: 11205,
|
|
15
|
+
GATEWAY_READ_HTTP_ERROR: 11206,
|
|
16
|
+
GATEWAY_READ_INVALID_JSON: 11207,
|
|
17
|
+
GATEWAY_READ_INVALID_SHAPE: 11208,
|
|
18
|
+
GATEWAY_LIST_FAILED: 11209,
|
|
19
|
+
GATEWAY_LIST_HTTP_ERROR: 11210,
|
|
20
|
+
GATEWAY_LIST_INVALID_JSON: 11211,
|
|
21
|
+
GATEWAY_LIST_NOT_ARRAY: 11212,
|
|
22
|
+
GATEWAY_REMOVE_FAILED: 11213,
|
|
23
|
+
GATEWAY_REMOVE_HTTP_ERROR: 11214,
|
|
24
|
+
// contract-api.ts — 11401-11409
|
|
25
|
+
CONTRACT_UNSUPPORTED_VERSION: 11401,
|
|
26
|
+
CONTRACT_CIRCUIT_RESULT_MISSING: 11402,
|
|
27
|
+
// midnight.ts — 11501-11599
|
|
28
|
+
MIDNIGHT_NETWORK_REQUIRED: 11501,
|
|
29
|
+
MIDNIGHT_WALLET_PROVIDER_REQUIRED: 11502,
|
|
30
|
+
MIDNIGHT_PRIVATE_STATE_PASSWORD_REQUIRED: 11503,
|
|
31
|
+
MIDNIGHT_ZK_ARTIFACTS_PATH_EMPTY: 11504,
|
|
32
|
+
MIDNIGHT_ZK_CONFIG_BASE_URL_EMPTY: 11505,
|
|
33
|
+
MIDNIGHT_UNSUPPORTED_NETWORK: 11506,
|
|
34
|
+
MIDNIGHT_AUTH_TOKEN_REQUIRED: 11507,
|
|
35
|
+
MIDNIGHT_ZK_CONFIG_BASE_URL_REQUIRED: 11508,
|
|
36
|
+
MIDNIGHT_ZK_ARTIFACTS_PATH_REQUIRED: 11509,
|
|
37
|
+
MIDNIGHT_ZK_ARTIFACTS_PATH_NOT_FOUND: 11510,
|
|
38
|
+
MIDNIGHT_CONNECTOR_NOT_INITIALIZED: 11511,
|
|
39
|
+
MIDNIGHT_COIN_PUBLIC_KEY_MISSING: 11512,
|
|
40
|
+
MIDNIGHT_ENC_PUBLIC_KEY_MISSING: 11513,
|
|
41
|
+
MIDNIGHT_INDEXER_HTTP_NOT_SET: 11514,
|
|
42
|
+
MIDNIGHT_INDEXER_WS_NOT_SET: 11515,
|
|
43
|
+
MIDNIGHT_PROOF_SERVER_NOT_SET: 11516,
|
|
44
|
+
MIDNIGHT_NOT_INITIALIZED: 11517,
|
|
45
|
+
MIDNIGHT_INVALID_CONTRACT_ADDRESS: 11518,
|
|
46
|
+
MIDNIGHT_REF_NOT_FOUND: 11519,
|
|
47
|
+
MIDNIGHT_KEYENVELOPE_REQUIRED: 11520,
|
|
48
|
+
MIDNIGHT_OWNER_SECRET_REQUIRED_WRITE: 11521,
|
|
49
|
+
MIDNIGHT_OWNER_SECRET_REQUIRED_REMOVE: 11522,
|
|
50
|
+
MIDNIGHT_OWNER_SECRET_REQUIRED_UPDATE: 11523,
|
|
51
|
+
MIDNIGHT_WALLET_NOT_FOUND: 11524,
|
|
52
|
+
MIDNIGHT_NO_WALLETS_FOUND: 11525,
|
|
53
|
+
MIDNIGHT_WALLET_CONNECT_FAILED: 11526,
|
|
54
|
+
MIDNIGHT_NO_ADDRESSES: 11527,
|
|
55
|
+
MIDNIGHT_EXTRACT_ADDRESSES_FAILED: 11528,
|
|
56
|
+
MIDNIGHT_GET_ADDRESSES_NOT_INITIALIZED: 11529,
|
|
57
|
+
MIDNIGHT_GET_BALANCES_NOT_INITIALIZED: 11530
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
ChainErrorCode
|
|
62
|
+
};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EncryptionErrorCode
|
|
3
|
+
} from "./chunk-O37NHERH.mjs";
|
|
4
|
+
import {
|
|
5
|
+
DStorageError,
|
|
6
|
+
KeyDeriver,
|
|
7
|
+
deriveKek,
|
|
8
|
+
unwrapKey,
|
|
9
|
+
wrapKey
|
|
10
|
+
} from "./chunk-TSZ37JDY.mjs";
|
|
11
|
+
|
|
12
|
+
// ../encryption/dist/chunk-QPJOFIIO.mjs
|
|
13
|
+
var MnemonicEncryptionAdapter = class {
|
|
14
|
+
constructor(input) {
|
|
15
|
+
this.name = "mnemonic";
|
|
16
|
+
if (input.mnemonic !== void 0) {
|
|
17
|
+
const wordCount = input.mnemonic.trim().split(/\s+/).length;
|
|
18
|
+
if (wordCount < 24) {
|
|
19
|
+
throw new DStorageError(
|
|
20
|
+
EncryptionErrorCode.MNEMONIC_TOO_SHORT,
|
|
21
|
+
`[dStorage] MnemonicEncryptionAdapter: a 24-word BIP-39 mnemonic is required (${wordCount} word${wordCount === 1 ? "" : "s"} provided). Shorter mnemonics provide insufficient entropy for post-quantum-safe key derivation \u2014 a 12-word mnemonic gives only 64-bit PQ security (below NIST's 128-bit minimum). Use a 24-word mnemonic or pass a 64-byte seedHex instead.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const deriver = new KeyDeriver(input);
|
|
26
|
+
this.kekMaterialPromise = deriver.getSeed().then(
|
|
27
|
+
(seed) => (
|
|
28
|
+
// Uint8Array.from() produces Uint8Array<ArrayBuffer> required by Web Crypto.
|
|
29
|
+
globalThis.crypto.subtle.importKey("raw", Uint8Array.from(seed), "HKDF", false, ["deriveBits"]).then(
|
|
30
|
+
(key) => {
|
|
31
|
+
seed.fill(0);
|
|
32
|
+
return key;
|
|
33
|
+
},
|
|
34
|
+
(err) => {
|
|
35
|
+
seed.fill(0);
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
async wrapDek(dek) {
|
|
43
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
44
|
+
const kek = await deriveKek(kekMaterial);
|
|
45
|
+
try {
|
|
46
|
+
return {
|
|
47
|
+
algorithm: "xchacha20poly1305-v1",
|
|
48
|
+
kekDerivation: "hkdf-sha256-v1",
|
|
49
|
+
wrappedDek: await wrapKey(dek, kek)
|
|
50
|
+
};
|
|
51
|
+
} finally {
|
|
52
|
+
kek.fill(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async unwrapDek({ wrappedDek }) {
|
|
56
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
57
|
+
const kek = await deriveKek(kekMaterial);
|
|
58
|
+
try {
|
|
59
|
+
return unwrapKey(wrappedDek, kek);
|
|
60
|
+
} finally {
|
|
61
|
+
kek.fill(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Deterministically derive a ML-KEM keypair from this adapter's key material.
|
|
66
|
+
*
|
|
67
|
+
* Uses Web Crypto HKDF to derive a 64-byte ML-KEM seed from the stored
|
|
68
|
+
* CryptoKey with info="dstorage:mlkem-seed:v1", domain-separated from the
|
|
69
|
+
* symmetric KEK derivation path. The same derivation applies to all seed
|
|
70
|
+
* types (mnemonic or hex), producing consistent, domain-separated keypairs.
|
|
71
|
+
*
|
|
72
|
+
* @param variant ML-KEM variant. Default: "mlkem768".
|
|
73
|
+
*/
|
|
74
|
+
async intoKeypairEncryptionAdapter(context, variant) {
|
|
75
|
+
const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-AE75YC4W-AOWNSH2M.mjs");
|
|
76
|
+
const { mlkemGenerateKeypairFromSeed } = await import("./dist-UD4WRMYJ.mjs");
|
|
77
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
78
|
+
const mlkemBits = await globalThis.crypto.subtle.deriveBits(
|
|
79
|
+
{
|
|
80
|
+
name: "HKDF",
|
|
81
|
+
hash: "SHA-256",
|
|
82
|
+
salt: new Uint8Array(32),
|
|
83
|
+
info: new TextEncoder().encode("dstorage:mlkem-seed:v1")
|
|
84
|
+
},
|
|
85
|
+
kekMaterial,
|
|
86
|
+
512
|
|
87
|
+
// 64 bytes
|
|
88
|
+
);
|
|
89
|
+
const mlkemSeed = new Uint8Array(mlkemBits);
|
|
90
|
+
try {
|
|
91
|
+
const { pk, sk } = mlkemGenerateKeypairFromSeed(mlkemSeed, variant);
|
|
92
|
+
return KeypairEncryptionAdapter.fromKeypair(pk, sk, context, variant);
|
|
93
|
+
} finally {
|
|
94
|
+
mlkemSeed.fill(0);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
MnemonicEncryptionAdapter
|
|
101
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// ../encryption/dist/chunk-WBLC4ONG.mjs
|
|
2
|
+
var EncryptionErrorCode = {
|
|
3
|
+
KEYPAIR_MISSING_SECRET_KEY: 13001,
|
|
4
|
+
MNEMONIC_TOO_SHORT: 13002,
|
|
5
|
+
PASSWORD_UNSUPPORTED_KDF: 13003,
|
|
6
|
+
PASSWORD_EMPTY_SALT: 13004,
|
|
7
|
+
PASSWORD_UNKNOWN_PRESET: 13005,
|
|
8
|
+
PASSWORD_TOO_SHORT: 13006,
|
|
9
|
+
PASSWORD_TOO_FEW_DISTINCT: 13007,
|
|
10
|
+
PASSWORD_SEQUENTIAL_RUN: 13008,
|
|
11
|
+
PASSWORD_KEYBOARD_WALK: 13009,
|
|
12
|
+
PASSWORD_ENTROPY_TOO_LOW: 13010
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
EncryptionErrorCode
|
|
17
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EncryptionErrorCode
|
|
3
|
+
} from "./chunk-O37NHERH.mjs";
|
|
4
|
+
import {
|
|
5
|
+
DStorageError,
|
|
6
|
+
KeyDeriver,
|
|
7
|
+
deriveKek,
|
|
8
|
+
unwrapKey,
|
|
9
|
+
wrapKey
|
|
10
|
+
} from "./chunk-BWLEAAFW.mjs";
|
|
11
|
+
|
|
12
|
+
// ../encryption/dist/chunk-SW62VRE3.mjs
|
|
13
|
+
var MnemonicEncryptionAdapter = class {
|
|
14
|
+
constructor(input) {
|
|
15
|
+
this.name = "mnemonic";
|
|
16
|
+
if (input.mnemonic !== void 0) {
|
|
17
|
+
const wordCount = input.mnemonic.trim().split(/\s+/).length;
|
|
18
|
+
if (wordCount < 24) {
|
|
19
|
+
throw new DStorageError(
|
|
20
|
+
EncryptionErrorCode.MNEMONIC_TOO_SHORT,
|
|
21
|
+
`[dStorage] MnemonicEncryptionAdapter: a 24-word BIP-39 mnemonic is required (${wordCount} word${wordCount === 1 ? "" : "s"} provided). Shorter mnemonics provide insufficient entropy for post-quantum-safe key derivation \u2014 a 12-word mnemonic gives only 64-bit PQ security (below NIST's 128-bit minimum). Use a 24-word mnemonic or pass a 64-byte seedHex instead.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const deriver = new KeyDeriver(input);
|
|
26
|
+
this.kekMaterialPromise = deriver.getSeed().then(
|
|
27
|
+
(seed) => (
|
|
28
|
+
// Uint8Array.from() produces Uint8Array<ArrayBuffer> required by Web Crypto.
|
|
29
|
+
globalThis.crypto.subtle.importKey("raw", Uint8Array.from(seed), "HKDF", false, ["deriveBits"]).then(
|
|
30
|
+
(key) => {
|
|
31
|
+
seed.fill(0);
|
|
32
|
+
return key;
|
|
33
|
+
},
|
|
34
|
+
(err) => {
|
|
35
|
+
seed.fill(0);
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
async wrapDek(dek) {
|
|
43
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
44
|
+
const kek = await deriveKek(kekMaterial);
|
|
45
|
+
try {
|
|
46
|
+
return {
|
|
47
|
+
algorithm: "xchacha20poly1305-v1",
|
|
48
|
+
kekDerivation: "hkdf-sha256-v1",
|
|
49
|
+
wrappedDek: await wrapKey(dek, kek)
|
|
50
|
+
};
|
|
51
|
+
} finally {
|
|
52
|
+
kek.fill(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async unwrapDek({ wrappedDek }) {
|
|
56
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
57
|
+
const kek = await deriveKek(kekMaterial);
|
|
58
|
+
try {
|
|
59
|
+
return unwrapKey(wrappedDek, kek);
|
|
60
|
+
} finally {
|
|
61
|
+
kek.fill(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Deterministically derive a ML-KEM keypair from this adapter's key material.
|
|
66
|
+
*
|
|
67
|
+
* Uses Web Crypto HKDF to derive a 64-byte ML-KEM seed from the stored
|
|
68
|
+
* CryptoKey with info="dstorage:mlkem-seed:v1", domain-separated from the
|
|
69
|
+
* symmetric KEK derivation path. The same derivation applies to all seed
|
|
70
|
+
* types (mnemonic or hex), producing consistent, domain-separated keypairs.
|
|
71
|
+
*
|
|
72
|
+
* @param context Domain-separation string bound into the KEK derivation
|
|
73
|
+
* @param variant ML-KEM variant. Default: "mlkem768".
|
|
74
|
+
*/
|
|
75
|
+
async intoKeypairEncryptionAdapter(context, variant) {
|
|
76
|
+
const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-XUKCLRFU-ERPYK5S4.mjs");
|
|
77
|
+
const { mlkemGenerateKeypairFromSeed } = await import("./dist-MOLWJ4Y7.mjs");
|
|
78
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
79
|
+
const mlkemBits = await globalThis.crypto.subtle.deriveBits(
|
|
80
|
+
{
|
|
81
|
+
name: "HKDF",
|
|
82
|
+
hash: "SHA-256",
|
|
83
|
+
salt: new Uint8Array(32),
|
|
84
|
+
info: new TextEncoder().encode("dstorage:mlkem-seed:v1")
|
|
85
|
+
},
|
|
86
|
+
kekMaterial,
|
|
87
|
+
512
|
|
88
|
+
// 64 bytes
|
|
89
|
+
);
|
|
90
|
+
const mlkemSeed = new Uint8Array(mlkemBits);
|
|
91
|
+
try {
|
|
92
|
+
const { pk, sk } = mlkemGenerateKeypairFromSeed(mlkemSeed, variant);
|
|
93
|
+
return KeypairEncryptionAdapter.fromKeypair(pk, sk, context, variant);
|
|
94
|
+
} finally {
|
|
95
|
+
mlkemSeed.fill(0);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
export {
|
|
101
|
+
MnemonicEncryptionAdapter
|
|
102
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var S={KEYPAIR_MISSING_SECRET_KEY:13001,MNEMONIC_TOO_SHORT:13002,PASSWORD_UNSUPPORTED_KDF:13003,PASSWORD_EMPTY_SALT:13004,PASSWORD_UNKNOWN_PRESET:13005,PASSWORD_TOO_SHORT:13006,PASSWORD_TOO_FEW_DISTINCT:13007,PASSWORD_SEQUENTIAL_RUN:13008,PASSWORD_KEYBOARD_WALK:13009,PASSWORD_ENTROPY_TOO_LOW:13010};export{S as a};
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EncryptionErrorCode
|
|
3
|
+
} from "./chunk-O37NHERH.mjs";
|
|
4
|
+
import {
|
|
5
|
+
DStorageError,
|
|
6
|
+
KeyDeriver,
|
|
7
|
+
deriveKek,
|
|
8
|
+
unwrapKey,
|
|
9
|
+
wrapKey
|
|
10
|
+
} from "./chunk-BWLEAAFW.mjs";
|
|
11
|
+
|
|
12
|
+
// ../encryption/dist/chunk-QPJOFIIO.mjs
|
|
13
|
+
var MnemonicEncryptionAdapter = class {
|
|
14
|
+
constructor(input) {
|
|
15
|
+
this.name = "mnemonic";
|
|
16
|
+
if (input.mnemonic !== void 0) {
|
|
17
|
+
const wordCount = input.mnemonic.trim().split(/\s+/).length;
|
|
18
|
+
if (wordCount < 24) {
|
|
19
|
+
throw new DStorageError(
|
|
20
|
+
EncryptionErrorCode.MNEMONIC_TOO_SHORT,
|
|
21
|
+
`[dStorage] MnemonicEncryptionAdapter: a 24-word BIP-39 mnemonic is required (${wordCount} word${wordCount === 1 ? "" : "s"} provided). Shorter mnemonics provide insufficient entropy for post-quantum-safe key derivation \u2014 a 12-word mnemonic gives only 64-bit PQ security (below NIST's 128-bit minimum). Use a 24-word mnemonic or pass a 64-byte seedHex instead.`
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
const deriver = new KeyDeriver(input);
|
|
26
|
+
this.kekMaterialPromise = deriver.getSeed().then(
|
|
27
|
+
(seed) => (
|
|
28
|
+
// Uint8Array.from() produces Uint8Array<ArrayBuffer> required by Web Crypto.
|
|
29
|
+
globalThis.crypto.subtle.importKey("raw", Uint8Array.from(seed), "HKDF", false, ["deriveBits"]).then(
|
|
30
|
+
(key) => {
|
|
31
|
+
seed.fill(0);
|
|
32
|
+
return key;
|
|
33
|
+
},
|
|
34
|
+
(err) => {
|
|
35
|
+
seed.fill(0);
|
|
36
|
+
throw err;
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
)
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
async wrapDek(dek) {
|
|
43
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
44
|
+
const kek = await deriveKek(kekMaterial);
|
|
45
|
+
try {
|
|
46
|
+
return {
|
|
47
|
+
algorithm: "xchacha20poly1305-v1",
|
|
48
|
+
kekDerivation: "hkdf-sha256-v1",
|
|
49
|
+
wrappedDek: await wrapKey(dek, kek)
|
|
50
|
+
};
|
|
51
|
+
} finally {
|
|
52
|
+
kek.fill(0);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async unwrapDek({ wrappedDek }) {
|
|
56
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
57
|
+
const kek = await deriveKek(kekMaterial);
|
|
58
|
+
try {
|
|
59
|
+
return unwrapKey(wrappedDek, kek);
|
|
60
|
+
} finally {
|
|
61
|
+
kek.fill(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Deterministically derive a ML-KEM keypair from this adapter's key material.
|
|
66
|
+
*
|
|
67
|
+
* Uses Web Crypto HKDF to derive a 64-byte ML-KEM seed from the stored
|
|
68
|
+
* CryptoKey with info="dstorage:mlkem-seed:v1", domain-separated from the
|
|
69
|
+
* symmetric KEK derivation path. The same derivation applies to all seed
|
|
70
|
+
* types (mnemonic or hex), producing consistent, domain-separated keypairs.
|
|
71
|
+
*
|
|
72
|
+
* @param variant ML-KEM variant. Default: "mlkem768".
|
|
73
|
+
*/
|
|
74
|
+
async intoKeypairEncryptionAdapter(context, variant) {
|
|
75
|
+
const { KeypairEncryptionAdapter } = await import("./KeypairEncryptionAdapter-AE75YC4W-VAOH6AAL.mjs");
|
|
76
|
+
const { mlkemGenerateKeypairFromSeed } = await import("./dist-INTAFXQ7.mjs");
|
|
77
|
+
const kekMaterial = await this.kekMaterialPromise;
|
|
78
|
+
const mlkemBits = await globalThis.crypto.subtle.deriveBits(
|
|
79
|
+
{
|
|
80
|
+
name: "HKDF",
|
|
81
|
+
hash: "SHA-256",
|
|
82
|
+
salt: new Uint8Array(32),
|
|
83
|
+
info: new TextEncoder().encode("dstorage:mlkem-seed:v1")
|
|
84
|
+
},
|
|
85
|
+
kekMaterial,
|
|
86
|
+
512
|
|
87
|
+
// 64 bytes
|
|
88
|
+
);
|
|
89
|
+
const mlkemSeed = new Uint8Array(mlkemBits);
|
|
90
|
+
try {
|
|
91
|
+
const { pk, sk } = mlkemGenerateKeypairFromSeed(mlkemSeed, variant);
|
|
92
|
+
return KeypairEncryptionAdapter.fromKeypair(pk, sk, context, variant);
|
|
93
|
+
} finally {
|
|
94
|
+
mlkemSeed.fill(0);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export {
|
|
100
|
+
MnemonicEncryptionAdapter
|
|
101
|
+
};
|