@parity/product-sdk-keys 0.1.0 → 0.2.1

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/index.js CHANGED
@@ -1,8 +1,10 @@
1
+ import { sr25519CreateDerive, ed25519CreateDerive } from '@polkadot-labs/hdkd';
2
+ import { getPolkadotSigner } from 'polkadot-api/signer';
3
+ import { ss58Encode, deriveH160 } from '@parity/product-sdk-address';
4
+ import { deriveKey, nacl } from '@parity/product-sdk-crypto';
5
+ import { mnemonicToEntropy, entropyToMiniSecret, generateMnemonic } from '@polkadot-labs/hdkd-helpers';
6
+
1
7
  // src/key-manager.ts
2
- import { sr25519CreateDerive } from "@polkadot-labs/hdkd";
3
- import { getPolkadotSigner } from "polkadot-api/signer";
4
- import { deriveH160, ss58Encode } from "@parity/product-sdk-address";
5
- import { deriveKey, nacl } from "@parity/product-sdk-crypto";
6
8
  var DEFAULT_SALT = "product-sdk-keys-v1";
7
9
  function hexToBytes(hex) {
8
10
  const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
@@ -99,210 +101,6 @@ var KeyManager = class _KeyManager {
99
101
  return this.masterKey.slice();
100
102
  }
101
103
  };
102
- if (void 0) {
103
- const { test, expect, describe } = void 0;
104
- const TEST_SIG = new Uint8Array(64).fill(170);
105
- const TEST_ADDR = "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY";
106
- describe("KeyManager.fromSignature", () => {
107
- test("deterministic master key from fixed signature", () => {
108
- const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);
109
- const b = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);
110
- expect(a.exportKey()).toEqual(b.exportKey());
111
- expect(a.exportKey().length).toBe(32);
112
- });
113
- test("accepts hex string with 0x prefix", () => {
114
- const hex = `0x${"aa".repeat(64)}`;
115
- const fromHex = KeyManager.fromSignature(hex, TEST_ADDR);
116
- const fromBytes = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);
117
- expect(fromHex.exportKey()).toEqual(fromBytes.exportKey());
118
- });
119
- test("accepts hex string without 0x prefix", () => {
120
- const hex = "aa".repeat(64);
121
- const fromHex = KeyManager.fromSignature(hex, TEST_ADDR);
122
- const fromBytes = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);
123
- expect(fromHex.exportKey()).toEqual(fromBytes.exportKey());
124
- });
125
- test("different addresses produce different master keys", () => {
126
- const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);
127
- const b = KeyManager.fromSignature(
128
- TEST_SIG,
129
- "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty"
130
- );
131
- expect(a.exportKey()).not.toEqual(b.exportKey());
132
- });
133
- test("custom salt produces different master key", () => {
134
- const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);
135
- const b = KeyManager.fromSignature(TEST_SIG, TEST_ADDR, { salt: "custom-salt" });
136
- expect(a.exportKey()).not.toEqual(b.exportKey());
137
- });
138
- test("rejects empty signature", () => {
139
- expect(() => KeyManager.fromSignature(new Uint8Array(0), TEST_ADDR)).toThrow(
140
- "Signature too short"
141
- );
142
- });
143
- test("rejects short signature", () => {
144
- expect(() => KeyManager.fromSignature(new Uint8Array(16), TEST_ADDR)).toThrow(
145
- "Signature too short"
146
- );
147
- });
148
- test("rejects empty hex string", () => {
149
- expect(() => KeyManager.fromSignature("0x", TEST_ADDR)).toThrow("Signature too short");
150
- });
151
- });
152
- describe("KeyManager.fromRawKey", () => {
153
- test("accepts 32-byte key", () => {
154
- const key = new Uint8Array(32).fill(187);
155
- const km = KeyManager.fromRawKey(key);
156
- expect(km.exportKey()).toEqual(key);
157
- });
158
- test("rejects non-32-byte input", () => {
159
- expect(() => KeyManager.fromRawKey(new Uint8Array(16))).toThrow("Expected 32-byte");
160
- expect(() => KeyManager.fromRawKey(new Uint8Array(64))).toThrow("Expected 32-byte");
161
- });
162
- test("exportKey returns a copy", () => {
163
- const key = new Uint8Array(32).fill(204);
164
- const km = KeyManager.fromRawKey(key);
165
- const exported = km.exportKey();
166
- exported[0] = 255;
167
- expect(km.exportKey()[0]).toBe(204);
168
- });
169
- test("copies input \u2014 mutating original does not affect internal state", () => {
170
- const key = new Uint8Array(32).fill(170);
171
- const km = KeyManager.fromRawKey(key);
172
- key[0] = 255;
173
- expect(km.exportKey()[0]).toBe(170);
174
- });
175
- });
176
- describe("deriveSymmetricKey", () => {
177
- test("deterministic for same context", () => {
178
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(221));
179
- const a = km.deriveSymmetricKey("doc:123");
180
- const b = km.deriveSymmetricKey("doc:123");
181
- expect(a).toEqual(b);
182
- expect(a.length).toBe(32);
183
- });
184
- test("different contexts produce different keys", () => {
185
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(221));
186
- const a = km.deriveSymmetricKey("doc:123");
187
- const b = km.deriveSymmetricKey("doc:456");
188
- expect(a).not.toEqual(b);
189
- });
190
- test("empty context string works", () => {
191
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(221));
192
- const key = km.deriveSymmetricKey("");
193
- expect(key.length).toBe(32);
194
- });
195
- test("deriveSymmetricKey and deriveAccount use different domains", () => {
196
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(221));
197
- const symKey = km.deriveSymmetricKey("foo");
198
- const accountSeed = km.deriveSymmetricKey("account:foo");
199
- expect(symKey).not.toEqual(accountSeed);
200
- });
201
- });
202
- describe("deriveAccount", () => {
203
- test("deterministic for same context", () => {
204
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(238));
205
- const a = km.deriveAccount("doc-account:123");
206
- const b = km.deriveAccount("doc-account:123");
207
- expect(a.ss58Address).toBe(b.ss58Address);
208
- expect(a.h160Address).toBe(b.h160Address);
209
- expect(a.publicKey).toEqual(b.publicKey);
210
- });
211
- test("produces valid addresses", () => {
212
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(238));
213
- const account = km.deriveAccount("test");
214
- expect(account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);
215
- expect(account.h160Address).toMatch(/^0x[a-f0-9]{40}$/);
216
- expect(account.publicKey.length).toBe(32);
217
- });
218
- test("different contexts produce different accounts", () => {
219
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(238));
220
- const a = km.deriveAccount("ctx-a");
221
- const b = km.deriveAccount("ctx-b");
222
- expect(a.ss58Address).not.toBe(b.ss58Address);
223
- });
224
- test("custom ss58Prefix changes address encoding", () => {
225
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(238));
226
- const generic = km.deriveAccount("test", 42);
227
- const polkadot = km.deriveAccount("test", 0);
228
- expect(generic.ss58Address).not.toBe(polkadot.ss58Address);
229
- expect(generic.publicKey).toEqual(polkadot.publicKey);
230
- });
231
- test("signer has correct publicKey", () => {
232
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(238));
233
- const account = km.deriveAccount("test");
234
- expect(account.signer.publicKey).toEqual(account.publicKey);
235
- });
236
- });
237
- describe("deriveKeypairs", () => {
238
- test("deterministic from same master key", () => {
239
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(255));
240
- const a = km.deriveKeypairs();
241
- const b = km.deriveKeypairs();
242
- expect(a.encryption.publicKey).toEqual(b.encryption.publicKey);
243
- expect(a.signing.publicKey).toEqual(b.signing.publicKey);
244
- });
245
- test("NaCl Box encrypt/decrypt round-trip", () => {
246
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(255));
247
- const kp = km.deriveKeypairs();
248
- const message = new TextEncoder().encode("hello keys");
249
- const nonce = nacl.randomBytes(24);
250
- const encrypted = nacl.box(
251
- message,
252
- nonce,
253
- kp.encryption.publicKey,
254
- kp.encryption.secretKey
255
- );
256
- expect(encrypted).not.toBeNull();
257
- const decrypted = nacl.box.open(
258
- encrypted,
259
- nonce,
260
- kp.encryption.publicKey,
261
- kp.encryption.secretKey
262
- );
263
- expect(new TextDecoder().decode(decrypted)).toBe("hello keys");
264
- });
265
- test("NaCl Box two-party encrypt/decrypt", () => {
266
- const kmA = KeyManager.fromRawKey(new Uint8Array(32).fill(170));
267
- const kmB = KeyManager.fromRawKey(new Uint8Array(32).fill(187));
268
- const kpA = kmA.deriveKeypairs();
269
- const kpB = kmB.deriveKeypairs();
270
- const message = new TextEncoder().encode("secret for B");
271
- const nonce = nacl.randomBytes(24);
272
- const encrypted = nacl.box(
273
- message,
274
- nonce,
275
- kpB.encryption.publicKey,
276
- kpA.encryption.secretKey
277
- );
278
- expect(encrypted).not.toBeNull();
279
- const decrypted = nacl.box.open(
280
- encrypted,
281
- nonce,
282
- kpA.encryption.publicKey,
283
- kpB.encryption.secretKey
284
- );
285
- expect(new TextDecoder().decode(decrypted)).toBe("secret for B");
286
- });
287
- test("NaCl Sign sign/verify round-trip", () => {
288
- const km = KeyManager.fromRawKey(new Uint8Array(32).fill(255));
289
- const kp = km.deriveKeypairs();
290
- const message = new TextEncoder().encode("sign this");
291
- const signed = nacl.sign(message, kp.signing.secretKey);
292
- const opened = nacl.sign.open(signed, kp.signing.publicKey);
293
- expect(new TextDecoder().decode(opened)).toBe("sign this");
294
- });
295
- });
296
- }
297
-
298
- // src/session-key-manager.ts
299
- import { generateMnemonic } from "@polkadot-labs/hdkd-helpers";
300
-
301
- // src/seed-to-account.ts
302
- import { ed25519CreateDerive, sr25519CreateDerive as sr25519CreateDerive2 } from "@polkadot-labs/hdkd";
303
- import { entropyToMiniSecret, mnemonicToEntropy } from "@polkadot-labs/hdkd-helpers";
304
- import { getPolkadotSigner as getPolkadotSigner2 } from "polkadot-api/signer";
305
- import { deriveH160 as deriveH1602, ss58Encode as ss58Encode2 } from "@parity/product-sdk-address";
306
104
  function seedToAccount(mnemonic, derivationPath = "//0", ss58Prefix = 42, keyType = "sr25519") {
307
105
  let entropy;
308
106
  try {
@@ -311,12 +109,12 @@ function seedToAccount(mnemonic, derivationPath = "//0", ss58Prefix = 42, keyTyp
311
109
  throw new Error("Invalid mnemonic phrase", { cause });
312
110
  }
313
111
  const miniSecret = entropyToMiniSecret(entropy);
314
- const derive = keyType === "ed25519" ? ed25519CreateDerive(miniSecret) : sr25519CreateDerive2(miniSecret);
112
+ const derive = keyType === "ed25519" ? ed25519CreateDerive(miniSecret) : sr25519CreateDerive(miniSecret);
315
113
  const keyPair = derive(derivationPath);
316
114
  const signerKeyType = keyType === "ed25519" ? "Ed25519" : "Sr25519";
317
- const ss58Address = ss58Encode2(keyPair.publicKey, ss58Prefix);
318
- const h160Address = deriveH1602(keyPair.publicKey);
319
- const signer = getPolkadotSigner2(keyPair.publicKey, signerKeyType, keyPair.sign);
115
+ const ss58Address = ss58Encode(keyPair.publicKey, ss58Prefix);
116
+ const h160Address = deriveH160(keyPair.publicKey);
117
+ const signer = getPolkadotSigner(keyPair.publicKey, signerKeyType, keyPair.sign);
320
118
  return {
321
119
  publicKey: keyPair.publicKey,
322
120
  ss58Address,
@@ -324,81 +122,6 @@ function seedToAccount(mnemonic, derivationPath = "//0", ss58Prefix = 42, keyTyp
324
122
  signer
325
123
  };
326
124
  }
327
- if (void 0) {
328
- const { test, expect, describe } = void 0;
329
- const { generateMnemonic: generateMnemonic2 } = await null;
330
- const TEST_MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
331
- describe("seedToAccount", () => {
332
- test("deterministic derivation from fixed mnemonic", () => {
333
- const a = seedToAccount(TEST_MNEMONIC);
334
- const b = seedToAccount(TEST_MNEMONIC);
335
- expect(a.ss58Address).toBe(b.ss58Address);
336
- expect(a.h160Address).toBe(b.h160Address);
337
- expect(a.publicKey).toEqual(b.publicKey);
338
- expect(a.publicKey.length).toBe(32);
339
- });
340
- test("returns valid SS58 and H160 addresses", () => {
341
- const account = seedToAccount(TEST_MNEMONIC);
342
- expect(account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);
343
- expect(account.h160Address).toMatch(/^0x[a-f0-9]{40}$/);
344
- });
345
- test("custom derivation path produces different addresses", () => {
346
- const a = seedToAccount(TEST_MNEMONIC, "//0");
347
- const b = seedToAccount(TEST_MNEMONIC, "//1");
348
- expect(a.ss58Address).not.toBe(b.ss58Address);
349
- expect(a.h160Address).not.toBe(b.h160Address);
350
- });
351
- test("custom SS58 prefix changes address encoding", () => {
352
- const generic = seedToAccount(TEST_MNEMONIC, "//0", 42);
353
- const polkadot = seedToAccount(TEST_MNEMONIC, "//0", 0);
354
- expect(generic.ss58Address).not.toBe(polkadot.ss58Address);
355
- expect(generic.publicKey).toEqual(polkadot.publicKey);
356
- expect(generic.h160Address).toBe(polkadot.h160Address);
357
- });
358
- test("provides a signer", () => {
359
- const account = seedToAccount(TEST_MNEMONIC);
360
- expect(account.signer).toBeDefined();
361
- expect(account.signer.publicKey).toEqual(account.publicKey);
362
- });
363
- test("works with a freshly generated mnemonic", () => {
364
- const mnemonic = generateMnemonic2();
365
- const account = seedToAccount(mnemonic);
366
- expect(account.ss58Address).toBeTruthy();
367
- expect(account.publicKey.length).toBe(32);
368
- });
369
- test("throws descriptive error for invalid mnemonic", () => {
370
- expect(() => seedToAccount("not a valid mnemonic")).toThrow("Invalid mnemonic phrase");
371
- });
372
- test("throws descriptive error for empty string", () => {
373
- expect(() => seedToAccount("")).toThrow("Invalid mnemonic phrase");
374
- });
375
- test("ed25519 derivation produces different address than sr25519", () => {
376
- const sr = seedToAccount(TEST_MNEMONIC, "//0", 42, "sr25519");
377
- const ed = seedToAccount(TEST_MNEMONIC, "//0", 42, "ed25519");
378
- expect(ed.ss58Address).not.toBe(sr.ss58Address);
379
- expect(ed.publicKey).not.toEqual(sr.publicKey);
380
- });
381
- test("ed25519 derivation is deterministic", () => {
382
- const a = seedToAccount(TEST_MNEMONIC, "//0", 42, "ed25519");
383
- const b = seedToAccount(TEST_MNEMONIC, "//0", 42, "ed25519");
384
- expect(a.ss58Address).toBe(b.ss58Address);
385
- expect(a.h160Address).toBe(b.h160Address);
386
- expect(a.publicKey).toEqual(b.publicKey);
387
- expect(a.publicKey.length).toBe(32);
388
- });
389
- test("ed25519 provides a signer with matching publicKey", () => {
390
- const account = seedToAccount(TEST_MNEMONIC, "//0", 42, "ed25519");
391
- expect(account.signer).toBeDefined();
392
- expect(account.signer.publicKey).toEqual(account.publicKey);
393
- });
394
- test("default keyType is sr25519 (backward compatible)", () => {
395
- const withDefault = seedToAccount(TEST_MNEMONIC);
396
- const withExplicit = seedToAccount(TEST_MNEMONIC, "//0", 42, "sr25519");
397
- expect(withDefault.ss58Address).toBe(withExplicit.ss58Address);
398
- expect(withDefault.publicKey).toEqual(withExplicit.publicKey);
399
- });
400
- });
401
- }
402
125
 
403
126
  // src/session-key-manager.ts
404
127
  var SessionKeyManager = class {
@@ -447,97 +170,7 @@ var SessionKeyManager = class {
447
170
  await this.store.remove(this.name);
448
171
  }
449
172
  };
450
- if (void 0) {
451
- let mockKvStore = function() {
452
- const data = /* @__PURE__ */ new Map();
453
- return {
454
- data,
455
- async get(key) {
456
- return data.get(key) ?? null;
457
- },
458
- async set(key, value) {
459
- data.set(key, value);
460
- },
461
- async remove(key) {
462
- data.delete(key);
463
- },
464
- async getJSON() {
465
- return null;
466
- },
467
- async setJSON() {
468
- }
469
- };
470
- };
471
- mockKvStore2 = mockKvStore;
472
- const { test, expect, describe } = void 0;
473
- const TEST_MNEMONIC = "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about";
474
- describe("SessionKeyManager", () => {
475
- test("fromMnemonic produces deterministic results", () => {
476
- const store = mockKvStore();
477
- const skm = new SessionKeyManager({ store });
478
- const a = skm.fromMnemonic(TEST_MNEMONIC);
479
- const b = skm.fromMnemonic(TEST_MNEMONIC);
480
- expect(a.mnemonic).toBe(TEST_MNEMONIC);
481
- expect(a.account.ss58Address).toBe(b.account.ss58Address);
482
- expect(a.account.h160Address).toBe(b.account.h160Address);
483
- });
484
- test("fromMnemonic throws on invalid mnemonic", () => {
485
- const store = mockKvStore();
486
- const skm = new SessionKeyManager({ store });
487
- expect(() => skm.fromMnemonic("invalid words here")).toThrow("Invalid mnemonic phrase");
488
- });
489
- test("create and get round-trip", async () => {
490
- const store = mockKvStore();
491
- const skm = new SessionKeyManager({ store });
492
- const info = await skm.create();
493
- expect(info.mnemonic).toBeTruthy();
494
- expect(info.account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);
495
- expect(store.data.get("default")).toBe(info.mnemonic);
496
- const loaded = await skm.get();
497
- expect(loaded?.mnemonic).toBe(info.mnemonic);
498
- });
499
- test("get returns null when no key stored", async () => {
500
- const store = mockKvStore();
501
- const skm = new SessionKeyManager({ store });
502
- expect(await skm.get()).toBeNull();
503
- });
504
- test("getOrCreate creates then returns cached", async () => {
505
- const store = mockKvStore();
506
- const skm = new SessionKeyManager({ store });
507
- const created = await skm.getOrCreate();
508
- expect(store.data.size).toBe(1);
509
- const loaded = await skm.getOrCreate();
510
- expect(loaded.mnemonic).toBe(created.mnemonic);
511
- expect(loaded.account.ss58Address).toBe(created.account.ss58Address);
512
- });
513
- test("clear removes mnemonic from store", async () => {
514
- const store = mockKvStore();
515
- const skm = new SessionKeyManager({ store });
516
- await skm.create();
517
- expect(store.data.size).toBe(1);
518
- await skm.clear();
519
- expect(store.data.size).toBe(0);
520
- expect(await skm.get()).toBeNull();
521
- });
522
- test("name separates storage keys", async () => {
523
- const store = mockKvStore();
524
- const main = new SessionKeyManager({ name: "main", store });
525
- const burner = new SessionKeyManager({ name: "burner", store });
526
- const mainInfo = await main.create();
527
- const burnerInfo = await burner.create();
528
- expect(store.data.get("main")).toBe(mainInfo.mnemonic);
529
- expect(store.data.get("burner")).toBe(burnerInfo.mnemonic);
530
- expect(mainInfo.account.ss58Address).not.toBe(burnerInfo.account.ss58Address);
531
- await main.clear();
532
- expect(store.data.has("main")).toBe(false);
533
- expect(store.data.get("burner")).toBe(burnerInfo.mnemonic);
534
- });
535
- });
536
- }
537
- var mockKvStore2;
538
- export {
539
- KeyManager,
540
- SessionKeyManager,
541
- seedToAccount
542
- };
173
+
174
+ export { KeyManager, SessionKeyManager, seedToAccount };
175
+ //# sourceMappingURL=index.js.map
543
176
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/key-manager.ts","../src/session-key-manager.ts","../src/seed-to-account.ts"],"sourcesContent":["import { sr25519CreateDerive } from \"@polkadot-labs/hdkd\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nimport { deriveH160, ss58Encode } from \"@parity/product-sdk-address\";\nimport { deriveKey, nacl } from \"@parity/product-sdk-crypto\";\n\nimport type { DerivedAccount, DerivedKeypairs } from \"./types.js\";\n\nconst DEFAULT_SALT = \"product-sdk-keys-v1\";\n\nfunction hexToBytes(hex: string): Uint8Array {\n const clean = hex.startsWith(\"0x\") ? hex.slice(2) : hex;\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Hierarchical key manager.\n *\n * Holds a 32-byte master key in memory and derives child keys via HKDF-SHA256.\n * Does not persist anything — persistence is the consumer's responsibility.\n */\nexport class KeyManager {\n private readonly masterKey: Uint8Array;\n\n private constructor(masterKey: Uint8Array) {\n this.masterKey = masterKey;\n }\n\n /**\n * Create a KeyManager from a cryptographic signature.\n *\n * Derives master key via HKDF-SHA256:\n * IKM = signatureBytes, salt = options.salt (default \"product-sdk-keys-v1\"), info = signerAddress\n *\n * @param signature - Hex string (with/without 0x prefix) or raw bytes\n * @param signerAddress - SS58 address of the signer\n * @param options.salt - HKDF salt, defaults to \"product-sdk-keys-v1\"\n */\n static fromSignature(\n signature: Uint8Array | string,\n signerAddress: string,\n options?: { salt?: string },\n ): KeyManager {\n const sigBytes =\n signature instanceof Uint8Array\n ? signature\n : hexToBytes(signature.startsWith(\"0x\") ? signature.slice(2) : signature);\n if (sigBytes.length < 32) {\n throw new Error(\n `Signature too short: expected at least 32 bytes, got ${sigBytes.length}`,\n );\n }\n const salt = options?.salt ?? DEFAULT_SALT;\n const masterKey = deriveKey(sigBytes, salt, signerAddress);\n return new KeyManager(masterKey);\n }\n\n /**\n * Create a KeyManager from raw 32-byte key material.\n * For restoring from storage, testing, etc.\n */\n static fromRawKey(masterKey: Uint8Array): KeyManager {\n if (masterKey.length !== 32) {\n throw new Error(`Expected 32-byte master key, got ${masterKey.length} bytes`);\n }\n return new KeyManager(masterKey.slice());\n }\n\n /**\n * Derive a 32-byte symmetric key for a given context string.\n *\n * Uses HKDF-SHA256: IKM=masterKey, salt=\"\", info=context\n */\n deriveSymmetricKey(context: string): Uint8Array {\n return deriveKey(this.masterKey, \"\", context);\n }\n\n /**\n * Derive a Substrate sr25519 account for a given context string.\n *\n * HKDF(masterKey, \"\", \"account:\" + context) → 32-byte seed → sr25519 keypair\n */\n deriveAccount(context: string, ss58Prefix = 42): DerivedAccount {\n const seed = deriveKey(this.masterKey, \"\", `account:${context}`);\n const derive = sr25519CreateDerive(seed);\n const keyPair = derive(\"//0\");\n\n const ss58Address = ss58Encode(keyPair.publicKey, ss58Prefix);\n const h160Address = deriveH160(keyPair.publicKey);\n const signer = getPolkadotSigner(keyPair.publicKey, \"Sr25519\", keyPair.sign);\n\n return { publicKey: keyPair.publicKey, ss58Address, h160Address, signer };\n }\n\n /**\n * Derive NaCl encryption and signing keypairs from the master key.\n *\n * - Encryption: HKDF(masterKey, \"\", \"encryption-keypair\") → nacl.box.keyPair.fromSecretKey\n * - Signing: HKDF(masterKey, \"\", \"signing-keypair\") → nacl.sign.keyPair.fromSeed\n */\n deriveKeypairs(): DerivedKeypairs {\n const encSeed = deriveKey(this.masterKey, \"\", \"encryption-keypair\");\n const encKp = nacl.box.keyPair.fromSecretKey(encSeed);\n\n const sigSeed = deriveKey(this.masterKey, \"\", \"signing-keypair\");\n const sigKp = nacl.sign.keyPair.fromSeed(sigSeed);\n\n return {\n encryption: {\n publicKey: encKp.publicKey,\n secretKey: encKp.secretKey,\n },\n signing: {\n publicKey: sigKp.publicKey,\n secretKey: sigKp.secretKey,\n },\n };\n }\n\n /**\n * Export the raw master key bytes for consumer-managed persistence.\n */\n exportKey(): Uint8Array {\n return this.masterKey.slice();\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n const TEST_SIG = new Uint8Array(64).fill(0xaa);\n const TEST_ADDR = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\n\n describe(\"KeyManager.fromSignature\", () => {\n test(\"deterministic master key from fixed signature\", () => {\n const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n const b = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n expect(a.exportKey()).toEqual(b.exportKey());\n expect(a.exportKey().length).toBe(32);\n });\n\n test(\"accepts hex string with 0x prefix\", () => {\n const hex = `0x${\"aa\".repeat(64)}`;\n const fromHex = KeyManager.fromSignature(hex, TEST_ADDR);\n const fromBytes = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n expect(fromHex.exportKey()).toEqual(fromBytes.exportKey());\n });\n\n test(\"accepts hex string without 0x prefix\", () => {\n const hex = \"aa\".repeat(64);\n const fromHex = KeyManager.fromSignature(hex, TEST_ADDR);\n const fromBytes = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n expect(fromHex.exportKey()).toEqual(fromBytes.exportKey());\n });\n\n test(\"different addresses produce different master keys\", () => {\n const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n const b = KeyManager.fromSignature(\n TEST_SIG,\n \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\n );\n expect(a.exportKey()).not.toEqual(b.exportKey());\n });\n\n test(\"custom salt produces different master key\", () => {\n const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n const b = KeyManager.fromSignature(TEST_SIG, TEST_ADDR, { salt: \"custom-salt\" });\n expect(a.exportKey()).not.toEqual(b.exportKey());\n });\n\n test(\"rejects empty signature\", () => {\n expect(() => KeyManager.fromSignature(new Uint8Array(0), TEST_ADDR)).toThrow(\n \"Signature too short\",\n );\n });\n\n test(\"rejects short signature\", () => {\n expect(() => KeyManager.fromSignature(new Uint8Array(16), TEST_ADDR)).toThrow(\n \"Signature too short\",\n );\n });\n\n test(\"rejects empty hex string\", () => {\n expect(() => KeyManager.fromSignature(\"0x\", TEST_ADDR)).toThrow(\"Signature too short\");\n });\n });\n\n describe(\"KeyManager.fromRawKey\", () => {\n test(\"accepts 32-byte key\", () => {\n const key = new Uint8Array(32).fill(0xbb);\n const km = KeyManager.fromRawKey(key);\n expect(km.exportKey()).toEqual(key);\n });\n\n test(\"rejects non-32-byte input\", () => {\n expect(() => KeyManager.fromRawKey(new Uint8Array(16))).toThrow(\"Expected 32-byte\");\n expect(() => KeyManager.fromRawKey(new Uint8Array(64))).toThrow(\"Expected 32-byte\");\n });\n\n test(\"exportKey returns a copy\", () => {\n const key = new Uint8Array(32).fill(0xcc);\n const km = KeyManager.fromRawKey(key);\n const exported = km.exportKey();\n exported[0] = 0xff;\n expect(km.exportKey()[0]).toBe(0xcc);\n });\n\n test(\"copies input — mutating original does not affect internal state\", () => {\n const key = new Uint8Array(32).fill(0xaa);\n const km = KeyManager.fromRawKey(key);\n key[0] = 0xff;\n expect(km.exportKey()[0]).toBe(0xaa);\n });\n });\n\n describe(\"deriveSymmetricKey\", () => {\n test(\"deterministic for same context\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const a = km.deriveSymmetricKey(\"doc:123\");\n const b = km.deriveSymmetricKey(\"doc:123\");\n expect(a).toEqual(b);\n expect(a.length).toBe(32);\n });\n\n test(\"different contexts produce different keys\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const a = km.deriveSymmetricKey(\"doc:123\");\n const b = km.deriveSymmetricKey(\"doc:456\");\n expect(a).not.toEqual(b);\n });\n\n test(\"empty context string works\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const key = km.deriveSymmetricKey(\"\");\n expect(key.length).toBe(32);\n });\n\n test(\"deriveSymmetricKey and deriveAccount use different domains\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const symKey = km.deriveSymmetricKey(\"foo\");\n const accountSeed = km.deriveSymmetricKey(\"account:foo\");\n // deriveAccount(\"foo\") uses info=\"account:foo\" internally,\n // so its HKDF output matches deriveSymmetricKey(\"account:foo\")\n // but the final account is further derived through sr25519\n expect(symKey).not.toEqual(accountSeed);\n });\n });\n\n describe(\"deriveAccount\", () => {\n test(\"deterministic for same context\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const a = km.deriveAccount(\"doc-account:123\");\n const b = km.deriveAccount(\"doc-account:123\");\n expect(a.ss58Address).toBe(b.ss58Address);\n expect(a.h160Address).toBe(b.h160Address);\n expect(a.publicKey).toEqual(b.publicKey);\n });\n\n test(\"produces valid addresses\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const account = km.deriveAccount(\"test\");\n expect(account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);\n expect(account.h160Address).toMatch(/^0x[a-f0-9]{40}$/);\n expect(account.publicKey.length).toBe(32);\n });\n\n test(\"different contexts produce different accounts\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const a = km.deriveAccount(\"ctx-a\");\n const b = km.deriveAccount(\"ctx-b\");\n expect(a.ss58Address).not.toBe(b.ss58Address);\n });\n\n test(\"custom ss58Prefix changes address encoding\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const generic = km.deriveAccount(\"test\", 42);\n const polkadot = km.deriveAccount(\"test\", 0);\n expect(generic.ss58Address).not.toBe(polkadot.ss58Address);\n expect(generic.publicKey).toEqual(polkadot.publicKey);\n });\n\n test(\"signer has correct publicKey\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const account = km.deriveAccount(\"test\");\n expect(account.signer.publicKey).toEqual(account.publicKey);\n });\n });\n\n describe(\"deriveKeypairs\", () => {\n test(\"deterministic from same master key\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xff));\n const a = km.deriveKeypairs();\n const b = km.deriveKeypairs();\n expect(a.encryption.publicKey).toEqual(b.encryption.publicKey);\n expect(a.signing.publicKey).toEqual(b.signing.publicKey);\n });\n\n test(\"NaCl Box encrypt/decrypt round-trip\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xff));\n const kp = km.deriveKeypairs();\n const message = new TextEncoder().encode(\"hello keys\");\n const nonce = nacl.randomBytes(24);\n const encrypted = nacl.box(\n message,\n nonce,\n kp.encryption.publicKey,\n kp.encryption.secretKey,\n );\n expect(encrypted).not.toBeNull();\n const decrypted = nacl.box.open(\n encrypted!,\n nonce,\n kp.encryption.publicKey,\n kp.encryption.secretKey,\n );\n expect(new TextDecoder().decode(decrypted!)).toBe(\"hello keys\");\n });\n\n test(\"NaCl Box two-party encrypt/decrypt\", () => {\n const kmA = KeyManager.fromRawKey(new Uint8Array(32).fill(0xaa));\n const kmB = KeyManager.fromRawKey(new Uint8Array(32).fill(0xbb));\n const kpA = kmA.deriveKeypairs();\n const kpB = kmB.deriveKeypairs();\n const message = new TextEncoder().encode(\"secret for B\");\n const nonce = nacl.randomBytes(24);\n // A encrypts for B\n const encrypted = nacl.box(\n message,\n nonce,\n kpB.encryption.publicKey,\n kpA.encryption.secretKey,\n );\n expect(encrypted).not.toBeNull();\n // B decrypts from A\n const decrypted = nacl.box.open(\n encrypted!,\n nonce,\n kpA.encryption.publicKey,\n kpB.encryption.secretKey,\n );\n expect(new TextDecoder().decode(decrypted!)).toBe(\"secret for B\");\n });\n\n test(\"NaCl Sign sign/verify round-trip\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xff));\n const kp = km.deriveKeypairs();\n const message = new TextEncoder().encode(\"sign this\");\n const signed = nacl.sign(message, kp.signing.secretKey);\n const opened = nacl.sign.open(signed, kp.signing.publicKey);\n expect(new TextDecoder().decode(opened!)).toBe(\"sign this\");\n });\n });\n}\n","import { generateMnemonic } from \"@polkadot-labs/hdkd-helpers\";\nimport type { KvStore } from \"@parity/product-sdk-storage\";\n\nimport { seedToAccount } from \"./seed-to-account.js\";\nimport type { SessionKeyInfo } from \"./types.js\";\n\n/**\n * Manages an sr25519 account derived from a BIP39 mnemonic.\n *\n * @param options.store - KvStore instance (from `@parity/product-sdk-storage`).\n * Create with `createKvStore({ prefix: \"session-key\" })` for namespaced persistence.\n * @param options.name - Identifies this session key. Defaults to `\"default\"`.\n * Use different names to manage multiple independent session keys.\n *\n * @example\n * ```ts\n * const store = await createKvStore({ prefix: \"session-key\" });\n * const skm = new SessionKeyManager({ store });\n * const key = await skm.getOrCreate();\n * ```\n */\nexport class SessionKeyManager {\n private readonly name: string;\n private readonly store: KvStore;\n\n constructor(options: { store: KvStore; name?: string }) {\n this.name = options.name ?? \"default\";\n this.store = options.store;\n }\n\n /**\n * Create a new session key from a fresh mnemonic.\n * Persists the mnemonic to the store.\n */\n async create(): Promise<SessionKeyInfo> {\n const mnemonic = generateMnemonic();\n await this.store.set(this.name, mnemonic);\n return { mnemonic, account: seedToAccount(mnemonic) };\n }\n\n /**\n * Load an existing session key from the store.\n * Returns null if no mnemonic is stored.\n */\n async get(): Promise<SessionKeyInfo | null> {\n const mnemonic = await this.store.get(this.name);\n if (!mnemonic) return null;\n return { mnemonic, account: seedToAccount(mnemonic) };\n }\n\n /**\n * Load existing or create a new session key.\n */\n async getOrCreate(): Promise<SessionKeyInfo> {\n const existing = await this.get();\n if (existing) return existing;\n return this.create();\n }\n\n /**\n * Derive a session key from an explicit mnemonic (no storage interaction).\n */\n fromMnemonic(mnemonic: string): SessionKeyInfo {\n return { mnemonic, account: seedToAccount(mnemonic) };\n }\n\n /**\n * Clear the stored mnemonic from the store.\n */\n async clear(): Promise<void> {\n await this.store.remove(this.name);\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n const TEST_MNEMONIC =\n \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\";\n\n function mockKvStore(): KvStore & { data: Map<string, string> } {\n const data = new Map<string, string>();\n return {\n data,\n async get(key) {\n return data.get(key) ?? null;\n },\n async set(key, value) {\n data.set(key, value);\n },\n async remove(key) {\n data.delete(key);\n },\n async getJSON() {\n return null;\n },\n async setJSON() {},\n };\n }\n\n describe(\"SessionKeyManager\", () => {\n test(\"fromMnemonic produces deterministic results\", () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n const a = skm.fromMnemonic(TEST_MNEMONIC);\n const b = skm.fromMnemonic(TEST_MNEMONIC);\n expect(a.mnemonic).toBe(TEST_MNEMONIC);\n expect(a.account.ss58Address).toBe(b.account.ss58Address);\n expect(a.account.h160Address).toBe(b.account.h160Address);\n });\n\n test(\"fromMnemonic throws on invalid mnemonic\", () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n expect(() => skm.fromMnemonic(\"invalid words here\")).toThrow(\"Invalid mnemonic phrase\");\n });\n\n test(\"create and get round-trip\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n const info = await skm.create();\n expect(info.mnemonic).toBeTruthy();\n expect(info.account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);\n expect(store.data.get(\"default\")).toBe(info.mnemonic);\n\n const loaded = await skm.get();\n expect(loaded?.mnemonic).toBe(info.mnemonic);\n });\n\n test(\"get returns null when no key stored\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n expect(await skm.get()).toBeNull();\n });\n\n test(\"getOrCreate creates then returns cached\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n const created = await skm.getOrCreate();\n expect(store.data.size).toBe(1);\n\n const loaded = await skm.getOrCreate();\n expect(loaded.mnemonic).toBe(created.mnemonic);\n expect(loaded.account.ss58Address).toBe(created.account.ss58Address);\n });\n\n test(\"clear removes mnemonic from store\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n await skm.create();\n expect(store.data.size).toBe(1);\n\n await skm.clear();\n expect(store.data.size).toBe(0);\n expect(await skm.get()).toBeNull();\n });\n\n test(\"name separates storage keys\", async () => {\n const store = mockKvStore();\n const main = new SessionKeyManager({ name: \"main\", store });\n const burner = new SessionKeyManager({ name: \"burner\", store });\n\n const mainInfo = await main.create();\n const burnerInfo = await burner.create();\n\n expect(store.data.get(\"main\")).toBe(mainInfo.mnemonic);\n expect(store.data.get(\"burner\")).toBe(burnerInfo.mnemonic);\n expect(mainInfo.account.ss58Address).not.toBe(burnerInfo.account.ss58Address);\n\n await main.clear();\n expect(store.data.has(\"main\")).toBe(false);\n expect(store.data.get(\"burner\")).toBe(burnerInfo.mnemonic);\n });\n });\n}\n","import { ed25519CreateDerive, sr25519CreateDerive } from \"@polkadot-labs/hdkd\";\nimport { entropyToMiniSecret, mnemonicToEntropy } from \"@polkadot-labs/hdkd-helpers\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nimport { deriveH160, ss58Encode } from \"@parity/product-sdk-address\";\n\nimport type { DerivedAccount } from \"./types.js\";\n\n/**\n * Derive a DerivedAccount from a BIP39 mnemonic phrase.\n *\n * Uses the specified key type for derivation with a hard derivation path\n * (default `\"//0\"`).\n *\n * @param mnemonic - BIP39 mnemonic phrase\n * @param derivationPath - Hard derivation path, defaults to `\"//0\"`\n * @param ss58Prefix - SS58 network prefix, defaults to 42 (generic)\n * @param keyType - Key type for derivation, either `\"sr25519\"` or `\"ed25519\"`, defaults to `\"sr25519\"`\n */\nexport function seedToAccount(\n mnemonic: string,\n derivationPath = \"//0\",\n ss58Prefix = 42,\n keyType: \"sr25519\" | \"ed25519\" = \"sr25519\",\n): DerivedAccount {\n let entropy: Uint8Array;\n try {\n entropy = mnemonicToEntropy(mnemonic);\n } catch (cause) {\n throw new Error(\"Invalid mnemonic phrase\", { cause });\n }\n const miniSecret = entropyToMiniSecret(entropy);\n const derive =\n keyType === \"ed25519\" ? ed25519CreateDerive(miniSecret) : sr25519CreateDerive(miniSecret);\n const keyPair = derive(derivationPath);\n\n const signerKeyType = keyType === \"ed25519\" ? \"Ed25519\" : \"Sr25519\";\n const ss58Address = ss58Encode(keyPair.publicKey, ss58Prefix);\n const h160Address = deriveH160(keyPair.publicKey);\n const signer = getPolkadotSigner(keyPair.publicKey, signerKeyType, keyPair.sign);\n\n return {\n publicKey: keyPair.publicKey,\n ss58Address,\n h160Address,\n signer,\n };\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n const { generateMnemonic } = await import(\"@polkadot-labs/hdkd-helpers\");\n\n // Fixed test mnemonic (DO NOT use in production)\n const TEST_MNEMONIC =\n \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\";\n\n describe(\"seedToAccount\", () => {\n test(\"deterministic derivation from fixed mnemonic\", () => {\n const a = seedToAccount(TEST_MNEMONIC);\n const b = seedToAccount(TEST_MNEMONIC);\n expect(a.ss58Address).toBe(b.ss58Address);\n expect(a.h160Address).toBe(b.h160Address);\n expect(a.publicKey).toEqual(b.publicKey);\n expect(a.publicKey.length).toBe(32);\n });\n\n test(\"returns valid SS58 and H160 addresses\", () => {\n const account = seedToAccount(TEST_MNEMONIC);\n expect(account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);\n expect(account.h160Address).toMatch(/^0x[a-f0-9]{40}$/);\n });\n\n test(\"custom derivation path produces different addresses\", () => {\n const a = seedToAccount(TEST_MNEMONIC, \"//0\");\n const b = seedToAccount(TEST_MNEMONIC, \"//1\");\n expect(a.ss58Address).not.toBe(b.ss58Address);\n expect(a.h160Address).not.toBe(b.h160Address);\n });\n\n test(\"custom SS58 prefix changes address encoding\", () => {\n const generic = seedToAccount(TEST_MNEMONIC, \"//0\", 42);\n const polkadot = seedToAccount(TEST_MNEMONIC, \"//0\", 0);\n expect(generic.ss58Address).not.toBe(polkadot.ss58Address);\n // Same underlying public key\n expect(generic.publicKey).toEqual(polkadot.publicKey);\n expect(generic.h160Address).toBe(polkadot.h160Address);\n });\n\n test(\"provides a signer\", () => {\n const account = seedToAccount(TEST_MNEMONIC);\n expect(account.signer).toBeDefined();\n expect(account.signer.publicKey).toEqual(account.publicKey);\n });\n\n test(\"works with a freshly generated mnemonic\", () => {\n const mnemonic = generateMnemonic();\n const account = seedToAccount(mnemonic);\n expect(account.ss58Address).toBeTruthy();\n expect(account.publicKey.length).toBe(32);\n });\n\n test(\"throws descriptive error for invalid mnemonic\", () => {\n expect(() => seedToAccount(\"not a valid mnemonic\")).toThrow(\"Invalid mnemonic phrase\");\n });\n\n test(\"throws descriptive error for empty string\", () => {\n expect(() => seedToAccount(\"\")).toThrow(\"Invalid mnemonic phrase\");\n });\n\n test(\"ed25519 derivation produces different address than sr25519\", () => {\n const sr = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"sr25519\");\n const ed = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n expect(ed.ss58Address).not.toBe(sr.ss58Address);\n expect(ed.publicKey).not.toEqual(sr.publicKey);\n });\n\n test(\"ed25519 derivation is deterministic\", () => {\n const a = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n const b = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n expect(a.ss58Address).toBe(b.ss58Address);\n expect(a.h160Address).toBe(b.h160Address);\n expect(a.publicKey).toEqual(b.publicKey);\n expect(a.publicKey.length).toBe(32);\n });\n\n test(\"ed25519 provides a signer with matching publicKey\", () => {\n const account = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n expect(account.signer).toBeDefined();\n expect(account.signer.publicKey).toEqual(account.publicKey);\n });\n\n test(\"default keyType is sr25519 (backward compatible)\", () => {\n const withDefault = seedToAccount(TEST_MNEMONIC);\n const withExplicit = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"sr25519\");\n expect(withDefault.ss58Address).toBe(withExplicit.ss58Address);\n expect(withDefault.publicKey).toEqual(withExplicit.publicKey);\n });\n });\n}\n"],"mappings":"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","names":["sr25519CreateDerive","getPolkadotSigner","deriveH160","ss58Encode","generateMnemonic","mockKvStore"]}
1
+ {"version":3,"sources":["../src/key-manager.ts","../src/seed-to-account.ts","../src/session-key-manager.ts"],"names":["sr25519CreateDerive","ss58Encode","deriveH160","getPolkadotSigner"],"mappings":"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","file":"index.js","sourcesContent":["import { sr25519CreateDerive } from \"@polkadot-labs/hdkd\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nimport { deriveH160, ss58Encode } from \"@parity/product-sdk-address\";\nimport { deriveKey, nacl } from \"@parity/product-sdk-crypto\";\n\nimport type { DerivedAccount, DerivedKeypairs } from \"./types.js\";\n\nconst DEFAULT_SALT = \"product-sdk-keys-v1\";\n\nfunction hexToBytes(hex: string): Uint8Array {\n const clean = hex.startsWith(\"0x\") ? hex.slice(2) : hex;\n const bytes = new Uint8Array(clean.length / 2);\n for (let i = 0; i < bytes.length; i++) {\n bytes[i] = Number.parseInt(clean.slice(i * 2, i * 2 + 2), 16);\n }\n return bytes;\n}\n\n/**\n * Hierarchical key manager.\n *\n * Holds a 32-byte master key in memory and derives child keys via HKDF-SHA256.\n * Does not persist anything — persistence is the consumer's responsibility.\n */\nexport class KeyManager {\n private readonly masterKey: Uint8Array;\n\n private constructor(masterKey: Uint8Array) {\n this.masterKey = masterKey;\n }\n\n /**\n * Create a KeyManager from a cryptographic signature.\n *\n * Derives master key via HKDF-SHA256:\n * IKM = signatureBytes, salt = options.salt (default \"product-sdk-keys-v1\"), info = signerAddress\n *\n * @param signature - Hex string (with/without 0x prefix) or raw bytes\n * @param signerAddress - SS58 address of the signer\n * @param options.salt - HKDF salt, defaults to \"product-sdk-keys-v1\"\n */\n static fromSignature(\n signature: Uint8Array | string,\n signerAddress: string,\n options?: { salt?: string },\n ): KeyManager {\n const sigBytes =\n signature instanceof Uint8Array\n ? signature\n : hexToBytes(signature.startsWith(\"0x\") ? signature.slice(2) : signature);\n if (sigBytes.length < 32) {\n throw new Error(\n `Signature too short: expected at least 32 bytes, got ${sigBytes.length}`,\n );\n }\n const salt = options?.salt ?? DEFAULT_SALT;\n const masterKey = deriveKey(sigBytes, salt, signerAddress);\n return new KeyManager(masterKey);\n }\n\n /**\n * Create a KeyManager from raw 32-byte key material.\n * For restoring from storage, testing, etc.\n */\n static fromRawKey(masterKey: Uint8Array): KeyManager {\n if (masterKey.length !== 32) {\n throw new Error(`Expected 32-byte master key, got ${masterKey.length} bytes`);\n }\n return new KeyManager(masterKey.slice());\n }\n\n /**\n * Derive a 32-byte symmetric key for a given context string.\n *\n * Uses HKDF-SHA256: IKM=masterKey, salt=\"\", info=context\n */\n deriveSymmetricKey(context: string): Uint8Array {\n return deriveKey(this.masterKey, \"\", context);\n }\n\n /**\n * Derive a Substrate sr25519 account for a given context string.\n *\n * HKDF(masterKey, \"\", \"account:\" + context) → 32-byte seed → sr25519 keypair\n */\n deriveAccount(context: string, ss58Prefix = 42): DerivedAccount {\n const seed = deriveKey(this.masterKey, \"\", `account:${context}`);\n const derive = sr25519CreateDerive(seed);\n const keyPair = derive(\"//0\");\n\n const ss58Address = ss58Encode(keyPair.publicKey, ss58Prefix);\n const h160Address = deriveH160(keyPair.publicKey);\n const signer = getPolkadotSigner(keyPair.publicKey, \"Sr25519\", keyPair.sign);\n\n return { publicKey: keyPair.publicKey, ss58Address, h160Address, signer };\n }\n\n /**\n * Derive NaCl encryption and signing keypairs from the master key.\n *\n * - Encryption: HKDF(masterKey, \"\", \"encryption-keypair\") → nacl.box.keyPair.fromSecretKey\n * - Signing: HKDF(masterKey, \"\", \"signing-keypair\") → nacl.sign.keyPair.fromSeed\n */\n deriveKeypairs(): DerivedKeypairs {\n const encSeed = deriveKey(this.masterKey, \"\", \"encryption-keypair\");\n const encKp = nacl.box.keyPair.fromSecretKey(encSeed);\n\n const sigSeed = deriveKey(this.masterKey, \"\", \"signing-keypair\");\n const sigKp = nacl.sign.keyPair.fromSeed(sigSeed);\n\n return {\n encryption: {\n publicKey: encKp.publicKey,\n secretKey: encKp.secretKey,\n },\n signing: {\n publicKey: sigKp.publicKey,\n secretKey: sigKp.secretKey,\n },\n };\n }\n\n /**\n * Export the raw master key bytes for consumer-managed persistence.\n */\n exportKey(): Uint8Array {\n return this.masterKey.slice();\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n const TEST_SIG = new Uint8Array(64).fill(0xaa);\n const TEST_ADDR = \"5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY\";\n\n describe(\"KeyManager.fromSignature\", () => {\n test(\"deterministic master key from fixed signature\", () => {\n const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n const b = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n expect(a.exportKey()).toEqual(b.exportKey());\n expect(a.exportKey().length).toBe(32);\n });\n\n test(\"accepts hex string with 0x prefix\", () => {\n const hex = `0x${\"aa\".repeat(64)}`;\n const fromHex = KeyManager.fromSignature(hex, TEST_ADDR);\n const fromBytes = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n expect(fromHex.exportKey()).toEqual(fromBytes.exportKey());\n });\n\n test(\"accepts hex string without 0x prefix\", () => {\n const hex = \"aa\".repeat(64);\n const fromHex = KeyManager.fromSignature(hex, TEST_ADDR);\n const fromBytes = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n expect(fromHex.exportKey()).toEqual(fromBytes.exportKey());\n });\n\n test(\"different addresses produce different master keys\", () => {\n const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n const b = KeyManager.fromSignature(\n TEST_SIG,\n \"5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty\",\n );\n expect(a.exportKey()).not.toEqual(b.exportKey());\n });\n\n test(\"custom salt produces different master key\", () => {\n const a = KeyManager.fromSignature(TEST_SIG, TEST_ADDR);\n const b = KeyManager.fromSignature(TEST_SIG, TEST_ADDR, { salt: \"custom-salt\" });\n expect(a.exportKey()).not.toEqual(b.exportKey());\n });\n\n test(\"rejects empty signature\", () => {\n expect(() => KeyManager.fromSignature(new Uint8Array(0), TEST_ADDR)).toThrow(\n \"Signature too short\",\n );\n });\n\n test(\"rejects short signature\", () => {\n expect(() => KeyManager.fromSignature(new Uint8Array(16), TEST_ADDR)).toThrow(\n \"Signature too short\",\n );\n });\n\n test(\"rejects empty hex string\", () => {\n expect(() => KeyManager.fromSignature(\"0x\", TEST_ADDR)).toThrow(\"Signature too short\");\n });\n });\n\n describe(\"KeyManager.fromRawKey\", () => {\n test(\"accepts 32-byte key\", () => {\n const key = new Uint8Array(32).fill(0xbb);\n const km = KeyManager.fromRawKey(key);\n expect(km.exportKey()).toEqual(key);\n });\n\n test(\"rejects non-32-byte input\", () => {\n expect(() => KeyManager.fromRawKey(new Uint8Array(16))).toThrow(\"Expected 32-byte\");\n expect(() => KeyManager.fromRawKey(new Uint8Array(64))).toThrow(\"Expected 32-byte\");\n });\n\n test(\"exportKey returns a copy\", () => {\n const key = new Uint8Array(32).fill(0xcc);\n const km = KeyManager.fromRawKey(key);\n const exported = km.exportKey();\n exported[0] = 0xff;\n expect(km.exportKey()[0]).toBe(0xcc);\n });\n\n test(\"copies input — mutating original does not affect internal state\", () => {\n const key = new Uint8Array(32).fill(0xaa);\n const km = KeyManager.fromRawKey(key);\n key[0] = 0xff;\n expect(km.exportKey()[0]).toBe(0xaa);\n });\n });\n\n describe(\"deriveSymmetricKey\", () => {\n test(\"deterministic for same context\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const a = km.deriveSymmetricKey(\"doc:123\");\n const b = km.deriveSymmetricKey(\"doc:123\");\n expect(a).toEqual(b);\n expect(a.length).toBe(32);\n });\n\n test(\"different contexts produce different keys\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const a = km.deriveSymmetricKey(\"doc:123\");\n const b = km.deriveSymmetricKey(\"doc:456\");\n expect(a).not.toEqual(b);\n });\n\n test(\"empty context string works\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const key = km.deriveSymmetricKey(\"\");\n expect(key.length).toBe(32);\n });\n\n test(\"deriveSymmetricKey and deriveAccount use different domains\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xdd));\n const symKey = km.deriveSymmetricKey(\"foo\");\n const accountSeed = km.deriveSymmetricKey(\"account:foo\");\n // deriveAccount(\"foo\") uses info=\"account:foo\" internally,\n // so its HKDF output matches deriveSymmetricKey(\"account:foo\")\n // but the final account is further derived through sr25519\n expect(symKey).not.toEqual(accountSeed);\n });\n });\n\n describe(\"deriveAccount\", () => {\n test(\"deterministic for same context\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const a = km.deriveAccount(\"doc-account:123\");\n const b = km.deriveAccount(\"doc-account:123\");\n expect(a.ss58Address).toBe(b.ss58Address);\n expect(a.h160Address).toBe(b.h160Address);\n expect(a.publicKey).toEqual(b.publicKey);\n });\n\n test(\"produces valid addresses\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const account = km.deriveAccount(\"test\");\n expect(account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);\n expect(account.h160Address).toMatch(/^0x[a-f0-9]{40}$/);\n expect(account.publicKey.length).toBe(32);\n });\n\n test(\"different contexts produce different accounts\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const a = km.deriveAccount(\"ctx-a\");\n const b = km.deriveAccount(\"ctx-b\");\n expect(a.ss58Address).not.toBe(b.ss58Address);\n });\n\n test(\"custom ss58Prefix changes address encoding\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const generic = km.deriveAccount(\"test\", 42);\n const polkadot = km.deriveAccount(\"test\", 0);\n expect(generic.ss58Address).not.toBe(polkadot.ss58Address);\n expect(generic.publicKey).toEqual(polkadot.publicKey);\n });\n\n test(\"signer has correct publicKey\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xee));\n const account = km.deriveAccount(\"test\");\n expect(account.signer.publicKey).toEqual(account.publicKey);\n });\n });\n\n describe(\"deriveKeypairs\", () => {\n test(\"deterministic from same master key\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xff));\n const a = km.deriveKeypairs();\n const b = km.deriveKeypairs();\n expect(a.encryption.publicKey).toEqual(b.encryption.publicKey);\n expect(a.signing.publicKey).toEqual(b.signing.publicKey);\n });\n\n test(\"NaCl Box encrypt/decrypt round-trip\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xff));\n const kp = km.deriveKeypairs();\n const message = new TextEncoder().encode(\"hello keys\");\n const nonce = nacl.randomBytes(24);\n const encrypted = nacl.box(\n message,\n nonce,\n kp.encryption.publicKey,\n kp.encryption.secretKey,\n );\n expect(encrypted).not.toBeNull();\n const decrypted = nacl.box.open(\n encrypted!,\n nonce,\n kp.encryption.publicKey,\n kp.encryption.secretKey,\n );\n expect(new TextDecoder().decode(decrypted!)).toBe(\"hello keys\");\n });\n\n test(\"NaCl Box two-party encrypt/decrypt\", () => {\n const kmA = KeyManager.fromRawKey(new Uint8Array(32).fill(0xaa));\n const kmB = KeyManager.fromRawKey(new Uint8Array(32).fill(0xbb));\n const kpA = kmA.deriveKeypairs();\n const kpB = kmB.deriveKeypairs();\n const message = new TextEncoder().encode(\"secret for B\");\n const nonce = nacl.randomBytes(24);\n // A encrypts for B\n const encrypted = nacl.box(\n message,\n nonce,\n kpB.encryption.publicKey,\n kpA.encryption.secretKey,\n );\n expect(encrypted).not.toBeNull();\n // B decrypts from A\n const decrypted = nacl.box.open(\n encrypted!,\n nonce,\n kpA.encryption.publicKey,\n kpB.encryption.secretKey,\n );\n expect(new TextDecoder().decode(decrypted!)).toBe(\"secret for B\");\n });\n\n test(\"NaCl Sign sign/verify round-trip\", () => {\n const km = KeyManager.fromRawKey(new Uint8Array(32).fill(0xff));\n const kp = km.deriveKeypairs();\n const message = new TextEncoder().encode(\"sign this\");\n const signed = nacl.sign(message, kp.signing.secretKey);\n const opened = nacl.sign.open(signed, kp.signing.publicKey);\n expect(new TextDecoder().decode(opened!)).toBe(\"sign this\");\n });\n });\n}\n","import { ed25519CreateDerive, sr25519CreateDerive } from \"@polkadot-labs/hdkd\";\nimport { entropyToMiniSecret, mnemonicToEntropy } from \"@polkadot-labs/hdkd-helpers\";\nimport { getPolkadotSigner } from \"polkadot-api/signer\";\n\nimport { deriveH160, ss58Encode } from \"@parity/product-sdk-address\";\n\nimport type { DerivedAccount } from \"./types.js\";\n\n/**\n * Derive a DerivedAccount from a BIP39 mnemonic phrase.\n *\n * Uses the specified key type for derivation with a hard derivation path\n * (default `\"//0\"`).\n *\n * @param mnemonic - BIP39 mnemonic phrase\n * @param derivationPath - Hard derivation path, defaults to `\"//0\"`\n * @param ss58Prefix - SS58 network prefix, defaults to 42 (generic)\n * @param keyType - Key type for derivation, either `\"sr25519\"` or `\"ed25519\"`, defaults to `\"sr25519\"`\n */\nexport function seedToAccount(\n mnemonic: string,\n derivationPath = \"//0\",\n ss58Prefix = 42,\n keyType: \"sr25519\" | \"ed25519\" = \"sr25519\",\n): DerivedAccount {\n let entropy: Uint8Array;\n try {\n entropy = mnemonicToEntropy(mnemonic);\n } catch (cause) {\n throw new Error(\"Invalid mnemonic phrase\", { cause });\n }\n const miniSecret = entropyToMiniSecret(entropy);\n const derive =\n keyType === \"ed25519\" ? ed25519CreateDerive(miniSecret) : sr25519CreateDerive(miniSecret);\n const keyPair = derive(derivationPath);\n\n const signerKeyType = keyType === \"ed25519\" ? \"Ed25519\" : \"Sr25519\";\n const ss58Address = ss58Encode(keyPair.publicKey, ss58Prefix);\n const h160Address = deriveH160(keyPair.publicKey);\n const signer = getPolkadotSigner(keyPair.publicKey, signerKeyType, keyPair.sign);\n\n return {\n publicKey: keyPair.publicKey,\n ss58Address,\n h160Address,\n signer,\n };\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n const { generateMnemonic } = await import(\"@polkadot-labs/hdkd-helpers\");\n\n // Fixed test mnemonic (DO NOT use in production)\n const TEST_MNEMONIC =\n \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\";\n\n describe(\"seedToAccount\", () => {\n test(\"deterministic derivation from fixed mnemonic\", () => {\n const a = seedToAccount(TEST_MNEMONIC);\n const b = seedToAccount(TEST_MNEMONIC);\n expect(a.ss58Address).toBe(b.ss58Address);\n expect(a.h160Address).toBe(b.h160Address);\n expect(a.publicKey).toEqual(b.publicKey);\n expect(a.publicKey.length).toBe(32);\n });\n\n test(\"returns valid SS58 and H160 addresses\", () => {\n const account = seedToAccount(TEST_MNEMONIC);\n expect(account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);\n expect(account.h160Address).toMatch(/^0x[a-f0-9]{40}$/);\n });\n\n test(\"custom derivation path produces different addresses\", () => {\n const a = seedToAccount(TEST_MNEMONIC, \"//0\");\n const b = seedToAccount(TEST_MNEMONIC, \"//1\");\n expect(a.ss58Address).not.toBe(b.ss58Address);\n expect(a.h160Address).not.toBe(b.h160Address);\n });\n\n test(\"custom SS58 prefix changes address encoding\", () => {\n const generic = seedToAccount(TEST_MNEMONIC, \"//0\", 42);\n const polkadot = seedToAccount(TEST_MNEMONIC, \"//0\", 0);\n expect(generic.ss58Address).not.toBe(polkadot.ss58Address);\n // Same underlying public key\n expect(generic.publicKey).toEqual(polkadot.publicKey);\n expect(generic.h160Address).toBe(polkadot.h160Address);\n });\n\n test(\"provides a signer\", () => {\n const account = seedToAccount(TEST_MNEMONIC);\n expect(account.signer).toBeDefined();\n expect(account.signer.publicKey).toEqual(account.publicKey);\n });\n\n test(\"works with a freshly generated mnemonic\", () => {\n const mnemonic = generateMnemonic();\n const account = seedToAccount(mnemonic);\n expect(account.ss58Address).toBeTruthy();\n expect(account.publicKey.length).toBe(32);\n });\n\n test(\"throws descriptive error for invalid mnemonic\", () => {\n expect(() => seedToAccount(\"not a valid mnemonic\")).toThrow(\"Invalid mnemonic phrase\");\n });\n\n test(\"throws descriptive error for empty string\", () => {\n expect(() => seedToAccount(\"\")).toThrow(\"Invalid mnemonic phrase\");\n });\n\n test(\"ed25519 derivation produces different address than sr25519\", () => {\n const sr = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"sr25519\");\n const ed = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n expect(ed.ss58Address).not.toBe(sr.ss58Address);\n expect(ed.publicKey).not.toEqual(sr.publicKey);\n });\n\n test(\"ed25519 derivation is deterministic\", () => {\n const a = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n const b = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n expect(a.ss58Address).toBe(b.ss58Address);\n expect(a.h160Address).toBe(b.h160Address);\n expect(a.publicKey).toEqual(b.publicKey);\n expect(a.publicKey.length).toBe(32);\n });\n\n test(\"ed25519 provides a signer with matching publicKey\", () => {\n const account = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"ed25519\");\n expect(account.signer).toBeDefined();\n expect(account.signer.publicKey).toEqual(account.publicKey);\n });\n\n test(\"default keyType is sr25519 (backward compatible)\", () => {\n const withDefault = seedToAccount(TEST_MNEMONIC);\n const withExplicit = seedToAccount(TEST_MNEMONIC, \"//0\", 42, \"sr25519\");\n expect(withDefault.ss58Address).toBe(withExplicit.ss58Address);\n expect(withDefault.publicKey).toEqual(withExplicit.publicKey);\n });\n });\n}\n","import { generateMnemonic } from \"@polkadot-labs/hdkd-helpers\";\nimport type { KvStore } from \"@parity/product-sdk-storage\";\n\nimport { seedToAccount } from \"./seed-to-account.js\";\nimport type { SessionKeyInfo } from \"./types.js\";\n\n/**\n * Manages an sr25519 account derived from a BIP39 mnemonic.\n *\n * @param options.store - KvStore instance (from `@parity/product-sdk-storage`).\n * Create with `createKvStore({ prefix: \"session-key\" })` for namespaced persistence.\n * @param options.name - Identifies this session key. Defaults to `\"default\"`.\n * Use different names to manage multiple independent session keys.\n *\n * @example\n * ```ts\n * const store = await createKvStore({ prefix: \"session-key\" });\n * const skm = new SessionKeyManager({ store });\n * const key = await skm.getOrCreate();\n * ```\n */\nexport class SessionKeyManager {\n private readonly name: string;\n private readonly store: KvStore;\n\n constructor(options: { store: KvStore; name?: string }) {\n this.name = options.name ?? \"default\";\n this.store = options.store;\n }\n\n /**\n * Create a new session key from a fresh mnemonic.\n * Persists the mnemonic to the store.\n */\n async create(): Promise<SessionKeyInfo> {\n const mnemonic = generateMnemonic();\n await this.store.set(this.name, mnemonic);\n return { mnemonic, account: seedToAccount(mnemonic) };\n }\n\n /**\n * Load an existing session key from the store.\n * Returns null if no mnemonic is stored.\n */\n async get(): Promise<SessionKeyInfo | null> {\n const mnemonic = await this.store.get(this.name);\n if (!mnemonic) return null;\n return { mnemonic, account: seedToAccount(mnemonic) };\n }\n\n /**\n * Load existing or create a new session key.\n */\n async getOrCreate(): Promise<SessionKeyInfo> {\n const existing = await this.get();\n if (existing) return existing;\n return this.create();\n }\n\n /**\n * Derive a session key from an explicit mnemonic (no storage interaction).\n */\n fromMnemonic(mnemonic: string): SessionKeyInfo {\n return { mnemonic, account: seedToAccount(mnemonic) };\n }\n\n /**\n * Clear the stored mnemonic from the store.\n */\n async clear(): Promise<void> {\n await this.store.remove(this.name);\n }\n}\n\nif (import.meta.vitest) {\n const { test, expect, describe } = import.meta.vitest;\n\n const TEST_MNEMONIC =\n \"abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about\";\n\n function mockKvStore(): KvStore & { data: Map<string, string> } {\n const data = new Map<string, string>();\n return {\n data,\n async get(key) {\n return data.get(key) ?? null;\n },\n async set(key, value) {\n data.set(key, value);\n },\n async remove(key) {\n data.delete(key);\n },\n async getJSON() {\n return null;\n },\n async setJSON() {},\n };\n }\n\n describe(\"SessionKeyManager\", () => {\n test(\"fromMnemonic produces deterministic results\", () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n const a = skm.fromMnemonic(TEST_MNEMONIC);\n const b = skm.fromMnemonic(TEST_MNEMONIC);\n expect(a.mnemonic).toBe(TEST_MNEMONIC);\n expect(a.account.ss58Address).toBe(b.account.ss58Address);\n expect(a.account.h160Address).toBe(b.account.h160Address);\n });\n\n test(\"fromMnemonic throws on invalid mnemonic\", () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n expect(() => skm.fromMnemonic(\"invalid words here\")).toThrow(\"Invalid mnemonic phrase\");\n });\n\n test(\"create and get round-trip\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n const info = await skm.create();\n expect(info.mnemonic).toBeTruthy();\n expect(info.account.ss58Address).toMatch(/^[1-9A-HJ-NP-Za-km-z]+$/);\n expect(store.data.get(\"default\")).toBe(info.mnemonic);\n\n const loaded = await skm.get();\n expect(loaded?.mnemonic).toBe(info.mnemonic);\n });\n\n test(\"get returns null when no key stored\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n expect(await skm.get()).toBeNull();\n });\n\n test(\"getOrCreate creates then returns cached\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n const created = await skm.getOrCreate();\n expect(store.data.size).toBe(1);\n\n const loaded = await skm.getOrCreate();\n expect(loaded.mnemonic).toBe(created.mnemonic);\n expect(loaded.account.ss58Address).toBe(created.account.ss58Address);\n });\n\n test(\"clear removes mnemonic from store\", async () => {\n const store = mockKvStore();\n const skm = new SessionKeyManager({ store });\n await skm.create();\n expect(store.data.size).toBe(1);\n\n await skm.clear();\n expect(store.data.size).toBe(0);\n expect(await skm.get()).toBeNull();\n });\n\n test(\"name separates storage keys\", async () => {\n const store = mockKvStore();\n const main = new SessionKeyManager({ name: \"main\", store });\n const burner = new SessionKeyManager({ name: \"burner\", store });\n\n const mainInfo = await main.create();\n const burnerInfo = await burner.create();\n\n expect(store.data.get(\"main\")).toBe(mainInfo.mnemonic);\n expect(store.data.get(\"burner\")).toBe(burnerInfo.mnemonic);\n expect(mainInfo.account.ss58Address).not.toBe(burnerInfo.account.ss58Address);\n\n await main.clear();\n expect(store.data.has(\"main\")).toBe(false);\n expect(store.data.get(\"burner\")).toBe(burnerInfo.mnemonic);\n });\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parity/product-sdk-keys",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Hierarchical key derivation and session key management for Polkadot accounts",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -20,10 +20,10 @@
20
20
  "dependencies": {
21
21
  "@polkadot-labs/hdkd": "^0.0.28",
22
22
  "@polkadot-labs/hdkd-helpers": "^0.0.10",
23
- "polkadot-api": "^1.9.0",
24
- "@parity/product-sdk-storage": "0.1.0",
25
- "@parity/product-sdk-address": "0.1.0",
26
- "@parity/product-sdk-crypto": "0.1.0"
23
+ "polkadot-api": "^2.1.2",
24
+ "@parity/product-sdk-address": "0.1.1",
25
+ "@parity/product-sdk-crypto": "0.1.1",
26
+ "@parity/product-sdk-storage": "0.1.2"
27
27
  },
28
28
  "devDependencies": {
29
29
  "typescript": "^5.7.0",
package/src/index.ts CHANGED
@@ -1,3 +1,16 @@
1
+ /**
2
+ * @parity/product-sdk-keys — Derive application keys from a user's account without touching their seed phrase.
3
+ *
4
+ * `KeyManager` holds a master key (typically derived from a one-time signature)
5
+ * and produces deterministic child keys via HKDF-SHA256, so an app can scope its
6
+ * own keys without ever asking for the user's mnemonic. `SessionKeyManager` is a
7
+ * separate, storage-backed mechanism: it generates a fresh BIP39 mnemonic, keeps
8
+ * it in a {@link KvStore}, and derives an sr25519 account from it — useful for
9
+ * persistent session signers. `seedToAccount` is the dev/test escape hatch that
10
+ * turns a mnemonic and derivation path into a ready-to-use signer.
11
+ *
12
+ * @packageDocumentation
13
+ */
1
14
  export { KeyManager } from "./key-manager.js";
2
15
  export { SessionKeyManager } from "./session-key-manager.js";
3
16
  export { seedToAccount } from "./seed-to-account.js";