quantum-resistant-rustykey 0.13.9 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,27 @@
1
+ // src/opfs-sk/browser-gate.ts
2
+ var OPFS_SK_WORKER_NAME = "qrr-opfs-sk";
3
+ var BROWSER_SK_CEREMONY_ERROR = "Browser keygen/sign requires WebAuthn userVerification:required, PRF, TEE, and loadOpfsSkWallet() (ciphertext in OPFS). Node and server REST loaders are unchanged. verify() remains available.";
4
+ function hasWorkerGlobalScope() {
5
+ return typeof globalThis.WorkerGlobalScope !== "undefined";
6
+ }
7
+ function hasWindow() {
8
+ return typeof globalThis.window !== "undefined";
9
+ }
10
+ function workerName() {
11
+ const name = globalThis.name;
12
+ return typeof name === "string" ? name : "";
13
+ }
14
+ function isUnconstrainedSigningRealm() {
15
+ return !hasWindow() && !hasWorkerGlobalScope();
16
+ }
17
+ function isOpfsSkWorkerRealm() {
18
+ return hasWorkerGlobalScope() && !hasWindow() && workerName() === OPFS_SK_WORKER_NAME;
19
+ }
20
+ function assertBrowserSkCeremonyAllowed() {
21
+ if (isUnconstrainedSigningRealm() || isOpfsSkWorkerRealm()) return;
22
+ throw new Error(BROWSER_SK_CEREMONY_ERROR);
23
+ }
24
+
1
25
  // src/signature-common.ts
2
26
  function fromHex(hex) {
3
27
  const cleaned = hex.trim().replace(/^0x/i, "").replace(/\s+/g, "");
@@ -17,12 +41,36 @@ function asBytes(value) {
17
41
  }
18
42
  function withStack(module, run) {
19
43
  const stack = module.stackSave();
44
+ const regions = [];
20
45
  try {
21
- return run((size) => module.stackAlloc(size));
46
+ return run((size) => {
47
+ const ptr = module.stackAlloc(size);
48
+ regions.push({ ptr, length: size });
49
+ return ptr;
50
+ });
22
51
  } finally {
52
+ zeroizeStackAllocations(module, stack, regions);
23
53
  module.stackRestore(stack);
24
54
  }
25
55
  }
56
+ function zeroizeBytes(bytes) {
57
+ bytes.fill(0);
58
+ }
59
+ function zeroizeStackAllocations(module, savedStack, regions) {
60
+ const heap = module.HEAPU8;
61
+ for (const { ptr, length } of regions) {
62
+ zeroizeHeapRange(heap, ptr, ptr + length);
63
+ }
64
+ const now = module.stackSave();
65
+ const lo = now < savedStack ? now : savedStack;
66
+ const hi = now < savedStack ? savedStack : now;
67
+ zeroizeHeapRange(heap, lo, hi);
68
+ }
69
+ function zeroizeHeapRange(heap, start, end) {
70
+ const lo = start < 0 ? 0 : start;
71
+ const hi = end > heap.length ? heap.length : end;
72
+ if (hi > lo) heap.fill(0, lo, hi);
73
+ }
26
74
  function writeBytes(module, alloc, bytes) {
27
75
  const ptr = alloc(bytes.length);
28
76
  module.HEAPU8.set(bytes, ptr);
@@ -1440,43 +1488,47 @@ async function runKeygen(variant) {
1440
1488
  )
1441
1489
  )
1442
1490
  );
1443
- return withStack(module, (alloc) => {
1444
- const pkPtr = alloc(
1445
- wasmExport(
1446
- module[`_sqisign_${p}_public_key_bytes`]
1447
- )
1448
- );
1449
- const skPtr = alloc(
1450
- wasmExport(
1451
- module[`_sqisign_${p}_private_key_bytes`]
1452
- )
1453
- );
1454
- const seedPtr = writeBytes(module, alloc, seed);
1455
- const rc = wasmExportWithArgs(
1456
- module[`_sqisign_${p}_keypair_seeded`],
1457
- pkPtr,
1458
- skPtr,
1459
- seedPtr
1460
- );
1461
- if (rc !== 0)
1462
- throw new Error(`SQISign ${variant} keypair failed with code ${rc}`);
1463
- return {
1464
- pk: readBytes(
1465
- module,
1466
- pkPtr,
1491
+ try {
1492
+ return withStack(module, (alloc) => {
1493
+ const pkPtr = alloc(
1467
1494
  wasmExport(
1468
1495
  module[`_sqisign_${p}_public_key_bytes`]
1469
1496
  )
1470
- ),
1471
- sk: readBytes(
1472
- module,
1473
- skPtr,
1497
+ );
1498
+ const skPtr = alloc(
1474
1499
  wasmExport(
1475
1500
  module[`_sqisign_${p}_private_key_bytes`]
1476
1501
  )
1477
- )
1478
- };
1479
- });
1502
+ );
1503
+ const seedPtr = writeBytes(module, alloc, seed);
1504
+ const rc = wasmExportWithArgs(
1505
+ module[`_sqisign_${p}_keypair_seeded`],
1506
+ pkPtr,
1507
+ skPtr,
1508
+ seedPtr
1509
+ );
1510
+ if (rc !== 0)
1511
+ throw new Error(`SQISign ${variant} keypair failed with code ${rc}`);
1512
+ return {
1513
+ pk: readBytes(
1514
+ module,
1515
+ pkPtr,
1516
+ wasmExport(
1517
+ module[`_sqisign_${p}_public_key_bytes`]
1518
+ )
1519
+ ),
1520
+ sk: readBytes(
1521
+ module,
1522
+ skPtr,
1523
+ wasmExport(
1524
+ module[`_sqisign_${p}_private_key_bytes`]
1525
+ )
1526
+ )
1527
+ };
1528
+ });
1529
+ } finally {
1530
+ zeroizeBytes(seed);
1531
+ }
1480
1532
  }
1481
1533
  async function runSign(variant, msg, sk) {
1482
1534
  const module = await getModule(variant);
@@ -1488,33 +1540,37 @@ async function runSign(variant, msg, sk) {
1488
1540
  )
1489
1541
  )
1490
1542
  );
1491
- return withStack(module, (alloc) => {
1492
- const sigPtr = alloc(
1493
- wasmExport(
1494
- module[`_sqisign_${p}_signature_bytes`]
1495
- )
1496
- );
1497
- const msgPtr = writeBytes(module, alloc, msg);
1498
- const skPtr = writeBytes(module, alloc, sk);
1499
- const seedPtr = writeBytes(module, alloc, seed);
1500
- const rc = wasmExportWithArgs(
1501
- module[`_sqisign_${p}_sign_seeded`],
1502
- sigPtr,
1503
- msgPtr,
1504
- msg.length,
1505
- skPtr,
1506
- seedPtr
1507
- );
1508
- if (rc !== 0)
1509
- throw new Error(`SQISign ${variant} sign failed with code ${rc}`);
1510
- return readBytes(
1511
- module,
1512
- sigPtr,
1513
- wasmExport(
1514
- module[`_sqisign_${p}_signature_bytes`]
1515
- )
1516
- );
1517
- });
1543
+ try {
1544
+ return withStack(module, (alloc) => {
1545
+ const sigPtr = alloc(
1546
+ wasmExport(
1547
+ module[`_sqisign_${p}_signature_bytes`]
1548
+ )
1549
+ );
1550
+ const msgPtr = writeBytes(module, alloc, msg);
1551
+ const skPtr = writeBytes(module, alloc, sk);
1552
+ const seedPtr = writeBytes(module, alloc, seed);
1553
+ const rc = wasmExportWithArgs(
1554
+ module[`_sqisign_${p}_sign_seeded`],
1555
+ sigPtr,
1556
+ msgPtr,
1557
+ msg.length,
1558
+ skPtr,
1559
+ seedPtr
1560
+ );
1561
+ if (rc !== 0)
1562
+ throw new Error(`SQISign ${variant} sign failed with code ${rc}`);
1563
+ return readBytes(
1564
+ module,
1565
+ sigPtr,
1566
+ wasmExport(
1567
+ module[`_sqisign_${p}_signature_bytes`]
1568
+ )
1569
+ );
1570
+ });
1571
+ } finally {
1572
+ zeroizeBytes(seed);
1573
+ }
1518
1574
  }
1519
1575
  async function runVerify(variant, sig, msg, pk) {
1520
1576
  const module = await getModule(variant);
@@ -1535,6 +1591,9 @@ async function runVerify(variant, sig, msg, pk) {
1535
1591
  }
1536
1592
  async function handleOp(request) {
1537
1593
  try {
1594
+ if (request.op === "keygen" || request.op === "sign") {
1595
+ assertBrowserSkCeremonyAllowed();
1596
+ }
1538
1597
  if (request.op === "keygen") {
1539
1598
  const { pk, sk } = await runKeygen(request.variant);
1540
1599
  return { id: request.id, ok: true, pk, sk };