@nokinc-flur/sdk 1.1.2 → 1.1.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/dist/index.js CHANGED
@@ -1563,64 +1563,113 @@ function constantTimeEqual(a, b) {
1563
1563
  return diff === 0;
1564
1564
  }
1565
1565
 
1566
- // src/crypto/ed25519.ts
1567
- import { ed25519 } from "@noble/curves/ed25519";
1568
- function generateKeyPair() {
1569
- const privateKey = ed25519.utils.randomPrivateKey();
1570
- const publicKey = ed25519.getPublicKey(privateKey);
1571
- return { privateKey, publicKey };
1566
+ // src/offline/oac.ts
1567
+ import { z as z5 } from "zod";
1568
+
1569
+ // src/crypto/p256-issuer.ts
1570
+ import { p256 } from "@noble/curves/nist";
1571
+ function bytesToBase64(bytes) {
1572
+ if (typeof Buffer !== "undefined") {
1573
+ return Buffer.from(bytes).toString("base64");
1574
+ }
1575
+ let bin = "";
1576
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
1577
+ return btoa(bin);
1572
1578
  }
1573
- function publicKeyFromPrivate(privateKey) {
1574
- return ed25519.getPublicKey(privateKey);
1579
+ function base64ToBytes(b64) {
1580
+ if (typeof Buffer !== "undefined") {
1581
+ return new Uint8Array(Buffer.from(b64, "base64"));
1582
+ }
1583
+ const bin = atob(b64);
1584
+ const out = new Uint8Array(bin.length);
1585
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
1586
+ return out;
1587
+ }
1588
+ var P256_SPKI_HEADER = new Uint8Array([
1589
+ 48,
1590
+ 89,
1591
+ 48,
1592
+ 19,
1593
+ 6,
1594
+ 7,
1595
+ 42,
1596
+ 134,
1597
+ 72,
1598
+ 206,
1599
+ 61,
1600
+ 2,
1601
+ 1,
1602
+ 6,
1603
+ 8,
1604
+ 42,
1605
+ 134,
1606
+ 72,
1607
+ 206,
1608
+ 61,
1609
+ 3,
1610
+ 1,
1611
+ 7,
1612
+ 3,
1613
+ 66,
1614
+ 0
1615
+ ]);
1616
+ function p256SpkiB64ToRaw(spkiB64) {
1617
+ const spki = base64ToBytes(spkiB64);
1618
+ if (spki.length !== P256_SPKI_HEADER.length + 65) {
1619
+ throw new Error("p256: invalid SPKI length");
1620
+ }
1621
+ for (let i = 0; i < P256_SPKI_HEADER.length; i++) {
1622
+ if (spki[i] !== P256_SPKI_HEADER[i]) {
1623
+ throw new Error("p256: invalid SPKI header");
1624
+ }
1625
+ }
1626
+ return spki.slice(P256_SPKI_HEADER.length);
1575
1627
  }
1576
- function sign(message, privateKey) {
1577
- return ed25519.sign(message, privateKey);
1628
+ function signIssuerP256(bytes, issuerPrivateKey) {
1629
+ const sig = p256.sign(bytes, issuerPrivateKey, { prehash: true });
1630
+ return bytesToBase64(sig.toBytes("der"));
1578
1631
  }
1579
- function verify(message, signature, publicKey) {
1632
+ function verifyIssuerP256(bytes, signatureB64, issuerPublicKeySpkiB64) {
1580
1633
  try {
1581
- return ed25519.verify(signature, message, publicKey);
1634
+ const pubRaw = p256SpkiB64ToRaw(issuerPublicKeySpkiB64);
1635
+ const sigBytes = base64ToBytes(signatureB64);
1636
+ return p256.verify(sigBytes, bytes, pubRaw, {
1637
+ prehash: true,
1638
+ format: "der"
1639
+ });
1582
1640
  } catch {
1583
1641
  return false;
1584
1642
  }
1585
1643
  }
1586
- function signCanonical(value, privateKey) {
1587
- return sign(canonicalJSONBytes(value), privateKey);
1588
- }
1589
- function verifyCanonical(value, signature, publicKey) {
1590
- return verify(canonicalJSONBytes(value), signature, publicKey);
1591
- }
1592
1644
 
1593
1645
  // src/offline/oac.ts
1594
- import { z as z5 } from "zod";
1595
1646
  var OAC_DEFAULT_PER_TX_KOBO = 5e5;
1596
1647
  var OAC_DEFAULT_CUMULATIVE_KOBO = 2e6;
1597
1648
  var OAC_DEFAULT_VALIDITY_MS = 24 * 60 * 60 * 1e3;
1598
- var HexString = (length) => z5.string().regex(
1599
- new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
1600
- `expected ${length}-byte hex string`
1601
- );
1649
+ var Base64Std = z5.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (standard) string");
1602
1650
  var OACSchema = z5.object({
1603
1651
  userId: z5.string().min(1),
1604
1652
  deviceId: z5.string().min(1),
1605
- devicePublicKey: HexString(32),
1653
+ /** SubjectPublicKeyInfo DER, base64 (P-256). */
1654
+ devicePublicKey: Base64Std,
1606
1655
  perTxCapKobo: z5.number().int().nonnegative(),
1607
1656
  cumulativeCapKobo: z5.number().int().nonnegative(),
1608
1657
  validFromMs: z5.number().int().nonnegative(),
1609
1658
  validUntilMs: z5.number().int().positive(),
1610
1659
  counterSeed: z5.number().int().nonnegative(),
1611
1660
  nonce: z5.string().min(1),
1612
- issuerSig: HexString(64)
1661
+ /** ASN.1 DER ECDSA(SHA-256) signature, base64. */
1662
+ issuerSig: Base64Std
1613
1663
  }).refine((v) => v.validUntilMs > v.validFromMs, {
1614
1664
  message: "validUntilMs must be greater than validFromMs"
1615
1665
  }).refine((v) => v.perTxCapKobo <= v.cumulativeCapKobo, {
1616
1666
  message: "perTxCapKobo must not exceed cumulativeCapKobo"
1617
1667
  });
1618
1668
  function buildOAC(input) {
1619
- const devicePublicKey = typeof input.devicePublicKey === "string" ? input.devicePublicKey : bytesToHex(input.devicePublicKey);
1620
1669
  return {
1621
1670
  userId: input.userId,
1622
1671
  deviceId: input.deviceId,
1623
- devicePublicKey,
1672
+ devicePublicKey: input.devicePublicKey,
1624
1673
  perTxCapKobo: input.perTxCapKobo ?? OAC_DEFAULT_PER_TX_KOBO,
1625
1674
  cumulativeCapKobo: input.cumulativeCapKobo ?? OAC_DEFAULT_CUMULATIVE_KOBO,
1626
1675
  validFromMs: input.validFromMs,
@@ -1630,36 +1679,25 @@ function buildOAC(input) {
1630
1679
  };
1631
1680
  }
1632
1681
  function signOAC(unsigned, issuerPrivateKey) {
1633
- const issuerSig = bytesToHex(
1634
- sign(canonicalJSONBytes(unsigned), issuerPrivateKey)
1682
+ const issuerSig = signIssuerP256(
1683
+ canonicalJSONBytes(unsigned),
1684
+ issuerPrivateKey
1635
1685
  );
1636
1686
  return { ...unsigned, issuerSig };
1637
1687
  }
1638
- function verifyOAC(oac, issuerPublicKey) {
1688
+ function verifyOAC(oac, issuerPublicKeySpkiB64) {
1639
1689
  try {
1640
1690
  const parsed = OACSchema.parse(oac);
1641
1691
  const { issuerSig, ...unsigned } = parsed;
1642
- return verify(
1692
+ return verifyIssuerP256(
1643
1693
  canonicalJSONBytes(unsigned),
1644
- hexToBytes(issuerSig),
1645
- issuerPublicKey
1694
+ issuerSig,
1695
+ issuerPublicKeySpkiB64
1646
1696
  );
1647
1697
  } catch {
1648
1698
  return false;
1649
1699
  }
1650
1700
  }
1651
- function bytesToHex(b) {
1652
- let s = "";
1653
- for (let i = 0; i < b.length; i++) s += b[i].toString(16).padStart(2, "0");
1654
- return s;
1655
- }
1656
- function hexToBytes(s) {
1657
- if (s.length % 2 !== 0) throw new Error("hex: odd length");
1658
- const out = new Uint8Array(s.length / 2);
1659
- for (let i = 0; i < out.length; i++)
1660
- out[i] = parseInt(s.slice(i * 2, i * 2 + 2), 16);
1661
- return out;
1662
- }
1663
1701
 
1664
1702
  // src/offline/codec.ts
1665
1703
  var ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:";
@@ -1716,19 +1754,19 @@ function decodeBase45(s) {
1716
1754
 
1717
1755
  // src/offline/messages.ts
1718
1756
  import { z as z6 } from "zod";
1719
- var HexSig = z6.string().regex(/^[0-9a-fA-F]{128}$/, "expected 64-byte hex signature");
1757
+ var Base64Sig = z6.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (standard) signature");
1720
1758
  var OfflinePaymentRequestSchema = z6.object({
1721
1759
  reference: z6.string().min(1),
1722
1760
  amountKobo: z6.number().int().positive(),
1723
1761
  merchantOAC: OACSchema,
1724
1762
  expiresAtMs: z6.number().int().positive(),
1725
- merchantSig: HexSig
1763
+ merchantSig: Base64Sig
1726
1764
  });
1727
1765
  var OfflinePaymentAuthorizationSchema = z6.object({
1728
1766
  request: OfflinePaymentRequestSchema,
1729
1767
  payerOAC: OACSchema,
1730
1768
  payerCounter: z6.number().int().positive(),
1731
- payerSig: HexSig
1769
+ payerSig: Base64Sig
1732
1770
  });
1733
1771
  function buildPaymentRequest(input) {
1734
1772
  if (!Number.isInteger(input.amountKobo) || input.amountKobo <= 0) {
@@ -1745,27 +1783,28 @@ function buildPaymentRequest(input) {
1745
1783
  };
1746
1784
  }
1747
1785
  function signPaymentRequest(unsigned, merchantDevicePrivateKey) {
1748
- const merchantSig = bytesToHex(
1749
- sign(canonicalJSONBytes(unsigned), merchantDevicePrivateKey)
1786
+ const merchantSig = signIssuerP256(
1787
+ canonicalJSONBytes(unsigned),
1788
+ merchantDevicePrivateKey
1750
1789
  );
1751
1790
  return { ...unsigned, merchantSig };
1752
1791
  }
1753
- function verifyPaymentRequest(req, issuerPublicKey) {
1792
+ function verifyPaymentRequest(req, issuerPublicKeySpkiB64) {
1754
1793
  try {
1755
1794
  const parsed = OfflinePaymentRequestSchema.parse(req);
1756
1795
  const { issuerSig: merchantOacSig, ...merchantOacUnsigned } = parsed.merchantOAC;
1757
- if (!verify(
1796
+ if (!verifyIssuerP256(
1758
1797
  canonicalJSONBytes(merchantOacUnsigned),
1759
- hexToBytes(merchantOacSig),
1760
- issuerPublicKey
1798
+ merchantOacSig,
1799
+ issuerPublicKeySpkiB64
1761
1800
  )) {
1762
1801
  return false;
1763
1802
  }
1764
1803
  const { merchantSig, ...unsigned } = parsed;
1765
- return verify(
1804
+ return verifyIssuerP256(
1766
1805
  canonicalJSONBytes(unsigned),
1767
- hexToBytes(merchantSig),
1768
- hexToBytes(parsed.merchantOAC.devicePublicKey)
1806
+ merchantSig,
1807
+ parsed.merchantOAC.devicePublicKey
1769
1808
  );
1770
1809
  } catch {
1771
1810
  return false;
@@ -1785,28 +1824,30 @@ function buildAuthorization(input) {
1785
1824
  };
1786
1825
  }
1787
1826
  function signAuthorization(unsigned, payerDevicePrivateKey) {
1788
- const payerSig = bytesToHex(
1789
- sign(canonicalJSONBytes(unsigned), payerDevicePrivateKey)
1827
+ const payerSig = signIssuerP256(
1828
+ canonicalJSONBytes(unsigned),
1829
+ payerDevicePrivateKey
1790
1830
  );
1791
1831
  return { ...unsigned, payerSig };
1792
1832
  }
1793
- function verifyAuthorization(auth, issuerPublicKey) {
1833
+ function verifyAuthorization(auth, issuerPublicKeySpkiB64) {
1794
1834
  try {
1795
1835
  const parsed = OfflinePaymentAuthorizationSchema.parse(auth);
1796
- if (!verifyPaymentRequest(parsed.request, issuerPublicKey)) return false;
1836
+ if (!verifyPaymentRequest(parsed.request, issuerPublicKeySpkiB64))
1837
+ return false;
1797
1838
  const { issuerSig: payerOacSig, ...payerOacUnsigned } = parsed.payerOAC;
1798
- if (!verify(
1839
+ if (!verifyIssuerP256(
1799
1840
  canonicalJSONBytes(payerOacUnsigned),
1800
- hexToBytes(payerOacSig),
1801
- issuerPublicKey
1841
+ payerOacSig,
1842
+ issuerPublicKeySpkiB64
1802
1843
  )) {
1803
1844
  return false;
1804
1845
  }
1805
1846
  const { payerSig, ...unsigned } = parsed;
1806
- return verify(
1847
+ return verifyIssuerP256(
1807
1848
  canonicalJSONBytes(unsigned),
1808
- hexToBytes(payerSig),
1809
- hexToBytes(parsed.payerOAC.devicePublicKey)
1849
+ payerSig,
1850
+ parsed.payerOAC.devicePublicKey
1810
1851
  );
1811
1852
  } catch {
1812
1853
  return false;
@@ -1838,7 +1879,7 @@ function decodeAuthorizationQR(s) {
1838
1879
  // src/offline/settlements.ts
1839
1880
  import { z as z7 } from "zod";
1840
1881
  import { sha256 } from "@noble/hashes/sha256";
1841
- import { bytesToHex as bytesToHex2 } from "@noble/hashes/utils";
1882
+ import { bytesToHex } from "@noble/hashes/utils";
1842
1883
  var OfflineTokenSchema = z7.object({
1843
1884
  tokenId: z7.string().uuid(),
1844
1885
  tokenSerial: z7.string(),
@@ -1862,10 +1903,14 @@ var PaymentClaimSchema = z7.object({
1862
1903
  occurredAtMs: z7.number().int().nonnegative(),
1863
1904
  completedAtMs: z7.number().int().nonnegative().optional(),
1864
1905
  contextId: z7.string().optional(),
1865
- payerPubkey: z7.string().regex(/^[0-9a-f]{64}$/i),
1866
- payerSignature: z7.string().regex(/^[0-9a-f]+$/i),
1867
- payeePubkey: z7.string().regex(/^[0-9a-f]{64}$/i).optional(),
1868
- payeeSignature: z7.string().regex(/^[0-9a-f]+$/i).optional()
1906
+ // Stage 2c: P-256 device keys are now SubjectPublicKeyInfo DER, base64.
1907
+ // Signatures are ASN.1 DER ECDSA(SHA-256), base64. Backwards-incompatible
1908
+ // wire change; the backend has the matching widening in offline-settlements
1909
+ // service + zod schema.
1910
+ payerPubkey: z7.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
1911
+ payerSignature: z7.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
1912
+ payeePubkey: z7.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional(),
1913
+ payeeSignature: z7.string().min(16).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/).optional()
1869
1914
  });
1870
1915
  var SettlementSchema = z7.object({
1871
1916
  settlementId: z7.string().uuid(),
@@ -1889,7 +1934,7 @@ var SettleResponseSchema = z7.object({
1889
1934
  });
1890
1935
  var ENCOUNTER_DOMAIN = "offline:v1:encounter";
1891
1936
  async function sha256Hex(input) {
1892
- return bytesToHex2(sha256(new TextEncoder().encode(input)));
1937
+ return bytesToHex(sha256(new TextEncoder().encode(input)));
1893
1938
  }
1894
1939
  async function computeEncounterId(input) {
1895
1940
  return sha256Hex(
@@ -2293,10 +2338,6 @@ var PASS_STATES = [
2293
2338
  "expired",
2294
2339
  "revoked"
2295
2340
  ];
2296
- var HexString2 = (length) => z9.string().regex(
2297
- new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
2298
- `expected ${length}-byte hex string`
2299
- );
2300
2341
  var PassMetadataSchema = z9.record(
2301
2342
  z9.union([z9.string(), z9.number(), z9.boolean(), z9.null()])
2302
2343
  );
@@ -2317,9 +2358,9 @@ var PassSchema = z9.object({
2317
2358
  nonce: z9.string().min(1),
2318
2359
  /** Device id this pass is bound to (FK to backend `device_keys`). */
2319
2360
  holderDeviceId: z9.string().min(1),
2320
- /** 32-byte hex Ed25519 public key of the bound device. The redemption signature
2321
- * is verified against this key — it is the security-critical binding. */
2322
- holderDevicePubkey: HexString2(32),
2361
+ /** SubjectPublicKeyInfo DER (P-256) of the bound device, base64. The redemption
2362
+ * signature is verified against this key — it is the security-critical binding. */
2363
+ holderDevicePubkey: z9.string().min(64).max(4096).regex(/^[A-Za-z0-9+/]+={0,2}$/),
2323
2364
  /** Optional fixed amount for monetary passes (vouchers, gift cards) in kobo. */
2324
2365
  amountKobo: z9.number().int().nonnegative().optional(),
2325
2366
  /** ISO-4217-ish currency code; required on the wire. SDK builders default to NGN. */
@@ -2328,7 +2369,8 @@ var PassSchema = z9.object({
2328
2369
  counterSeed: z9.number().int().nonnegative(),
2329
2370
  /** Optional cumulative spend cap in kobo across all redemptions of this pass. */
2330
2371
  cumulativeCapKobo: z9.number().int().nonnegative().optional(),
2331
- issuerSig: HexString2(64)
2372
+ /** ASN.1 DER ECDSA P-256 signature, base64. */
2373
+ issuerSig: z9.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
2332
2374
  }).refine((v) => v.validUntilMs > v.validFromMs, {
2333
2375
  message: "validUntilMs must be greater than validFromMs"
2334
2376
  });
@@ -2361,19 +2403,20 @@ function buildPass(input) {
2361
2403
  return out;
2362
2404
  }
2363
2405
  function signPass(unsigned, issuerPrivateKey) {
2364
- const issuerSig = bytesToHex(
2365
- sign(canonicalJSONBytes(unsigned), issuerPrivateKey)
2406
+ const issuerSig = signIssuerP256(
2407
+ canonicalJSONBytes(unsigned),
2408
+ issuerPrivateKey
2366
2409
  );
2367
2410
  return { ...unsigned, issuerSig };
2368
2411
  }
2369
- function verifyPass(pass, issuerPublicKey) {
2412
+ function verifyPass(pass, issuerPublicKeySpkiB64) {
2370
2413
  try {
2371
2414
  const parsed = PassSchema.parse(pass);
2372
2415
  const { issuerSig, ...unsigned } = parsed;
2373
- return verify(
2416
+ return verifyIssuerP256(
2374
2417
  canonicalJSONBytes(unsigned),
2375
- hexToBytes(issuerSig),
2376
- issuerPublicKey
2418
+ issuerSig,
2419
+ issuerPublicKeySpkiB64
2377
2420
  );
2378
2421
  } catch {
2379
2422
  return false;
@@ -2385,7 +2428,7 @@ function isPassWithinValidity(pass, nowMs) {
2385
2428
 
2386
2429
  // src/passes/redemption.ts
2387
2430
  import { z as z10 } from "zod";
2388
- var HexSig2 = z10.string().regex(/^[0-9a-fA-F]{128}$/, "expected 64-byte hex signature");
2431
+ var Base64Std2 = z10.string().min(16).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/, "expected base64 (std)");
2389
2432
  var RedemptionSchema = z10.object({
2390
2433
  pass: PassSchema,
2391
2434
  redeemerId: z10.string().min(1),
@@ -2396,7 +2439,8 @@ var RedemptionSchema = z10.object({
2396
2439
  /** Amount being redeemed in kobo (0 for non-monetary passes like ride tickets). */
2397
2440
  amountKobo: z10.number().int().nonnegative(),
2398
2441
  nonce: z10.string().min(1),
2399
- holderSig: HexSig2
2442
+ /** ASN.1 DER ECDSA P-256 signature over canonicalJSONBytes(unsigned), base64. */
2443
+ holderSig: Base64Std2
2400
2444
  });
2401
2445
  var REDEEMABLE_STATES = /* @__PURE__ */ new Set(["issued", "active"]);
2402
2446
  function buildRedemption(input) {
@@ -2450,31 +2494,28 @@ function buildRedemption(input) {
2450
2494
  };
2451
2495
  }
2452
2496
  function signRedemption(unsigned, holderDevicePrivateKey) {
2453
- const holderSig = bytesToHex(
2454
- sign(canonicalJSONBytes(unsigned), holderDevicePrivateKey)
2497
+ const holderSig = signIssuerP256(
2498
+ canonicalJSONBytes(unsigned),
2499
+ holderDevicePrivateKey
2455
2500
  );
2456
2501
  return { ...unsigned, holderSig };
2457
2502
  }
2458
- function verifyRedemption(r, issuerPublicKey) {
2503
+ function verifyRedemption(r, issuerPublicKeySpkiB64) {
2459
2504
  try {
2460
2505
  const parsed = RedemptionSchema.parse(r);
2461
2506
  if (parsed.counter <= parsed.pass.counterSeed) return false;
2462
2507
  const { issuerSig, ...passUnsigned } = parsed.pass;
2463
- if (!verify(
2508
+ if (!verifyIssuerP256(
2464
2509
  canonicalJSONBytes(passUnsigned),
2465
- hexToBytes(issuerSig),
2466
- issuerPublicKey
2510
+ issuerSig,
2511
+ issuerPublicKeySpkiB64
2467
2512
  )) {
2468
2513
  return false;
2469
2514
  }
2470
- const holderHex = parsed.pass.holderDevicePubkey;
2471
- if (typeof holderHex !== "string") return false;
2515
+ const holderPub = parsed.pass.holderDevicePubkey;
2516
+ if (typeof holderPub !== "string") return false;
2472
2517
  const { holderSig, ...unsigned } = parsed;
2473
- return verify(
2474
- canonicalJSONBytes(unsigned),
2475
- hexToBytes(holderSig),
2476
- hexToBytes(holderHex)
2477
- );
2518
+ return verifyIssuerP256(canonicalJSONBytes(unsigned), holderSig, holderPub);
2478
2519
  } catch {
2479
2520
  return false;
2480
2521
  }
@@ -2484,10 +2525,6 @@ function verifyRedemption(r, issuerPublicKey) {
2484
2525
  import { z as z11 } from "zod";
2485
2526
  var RECEIPT_CHANNELS = ["cash", "pass"];
2486
2527
  var RECEIPT_KINDS = RECEIPT_CHANNELS;
2487
- var HexString3 = (length) => z11.string().regex(
2488
- new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
2489
- `expected ${length}-byte hex string`
2490
- );
2491
2528
  var ReceiptPayloadSchema = z11.record(
2492
2529
  z11.union([z11.string(), z11.number(), z11.boolean(), z11.null()])
2493
2530
  );
@@ -2505,7 +2542,8 @@ var ReceiptSchema = z11.object({
2505
2542
  issuedAtMs: z11.number().int().nonnegative(),
2506
2543
  issuerId: z11.string().min(1),
2507
2544
  payload: ReceiptPayloadSchema,
2508
- issuerSig: HexString3(64)
2545
+ /** ASN.1 DER ECDSA P-256 signature, base64. */
2546
+ issuerSig: z11.string().min(64).max(2048).regex(/^[A-Za-z0-9+/]+={0,2}$/)
2509
2547
  }).superRefine((v, ctx) => {
2510
2548
  if (v.channel === "cash") {
2511
2549
  if (!v.intentId) {
@@ -2571,19 +2609,20 @@ function buildReceipt(input) {
2571
2609
  return out;
2572
2610
  }
2573
2611
  function signReceipt(unsigned, issuerPrivateKey) {
2574
- const issuerSig = bytesToHex(
2575
- sign(canonicalJSONBytes(unsigned), issuerPrivateKey)
2612
+ const issuerSig = signIssuerP256(
2613
+ canonicalJSONBytes(unsigned),
2614
+ issuerPrivateKey
2576
2615
  );
2577
2616
  return { ...unsigned, issuerSig };
2578
2617
  }
2579
- function verifyReceipt(r, issuerPublicKey) {
2618
+ function verifyReceipt(r, issuerPublicKeySpkiB64) {
2580
2619
  try {
2581
2620
  const parsed = ReceiptSchema.parse(r);
2582
2621
  const { issuerSig, ...unsigned } = parsed;
2583
- return verify(
2622
+ return verifyIssuerP256(
2584
2623
  canonicalJSONBytes(unsigned),
2585
- hexToBytes(issuerSig),
2586
- issuerPublicKey
2624
+ issuerSig,
2625
+ issuerPublicKeySpkiB64
2587
2626
  );
2588
2627
  } catch {
2589
2628
  return false;
@@ -2918,17 +2957,49 @@ function createAccountsClient(opts) {
2918
2957
  // src/me-offline/client.ts
2919
2958
  import { z as z13 } from "zod";
2920
2959
  var Hex64 = z13.string().regex(/^[0-9a-f]{64}$/i);
2921
- var HexAny = z13.string().regex(/^[0-9a-f]+$/i);
2922
2960
  var Sha256Hex = z13.string().regex(/^[0-9a-f]{64}$/i);
2961
+ var Base64Std3 = z13.string().regex(/^[A-Za-z0-9+/]+={0,2}$/);
2923
2962
  var RegisterDeviceKeyInputSchema = z13.object({
2924
2963
  deviceId: z13.string().min(1).max(128),
2925
2964
  publicKeyHex: Hex64
2926
2965
  });
2966
+ var AttestationSecurityLevelSchema = z13.enum([
2967
+ "STRONGBOX",
2968
+ "TEE",
2969
+ "SECURE_ENCLAVE",
2970
+ "SOFTWARE"
2971
+ ]);
2972
+ var DeviceKeyAlgSchema = z13.literal("p256");
2973
+ var RegisterDeviceKeyP256InputSchema = z13.object({
2974
+ deviceId: z13.string().min(1).max(128),
2975
+ /** P-256 SubjectPublicKeyInfo DER, base64. */
2976
+ publicKeySpkiB64: Base64Std3.min(64).max(4096),
2977
+ /** Base64 of the server-issued enrollment challenge string. */
2978
+ challengeB64: Base64Std3.min(8).max(1024),
2979
+ /** iOS App Attest payload or Android X.509 Key Attestation chain. */
2980
+ attestationChainB64: z13.array(Base64Std3.min(16).max(16384)).min(1).max(16),
2981
+ securityLevel: AttestationSecurityLevelSchema
2982
+ });
2983
+ var P256EnrollmentChallengeInputSchema = z13.object({
2984
+ deviceId: z13.string().min(1).max(128)
2985
+ });
2986
+ var P256EnrollmentChallengeResultSchema = z13.object({
2987
+ challenge: z13.string().min(16),
2988
+ expiresAtMs: z13.number().int().positive()
2989
+ });
2927
2990
  var DeviceKeyRecordSchema = z13.object({
2928
2991
  id: z13.string().uuid(),
2929
2992
  userId: z13.string().uuid(),
2930
2993
  deviceId: z13.string(),
2931
- publicKeyHex: Hex64,
2994
+ /** Always 'p256' on the consumer offline rail. Field retained for forward-compat. */
2995
+ alg: DeviceKeyAlgSchema.default("p256"),
2996
+ /** Legacy ed25519 hex key. Always null on new records (kept for back-compat reads). */
2997
+ publicKeyHex: Hex64.nullable().default(null),
2998
+ /** P-256 SubjectPublicKeyInfo DER, base64. Required for new records. */
2999
+ publicKeySpkiB64: Base64Std3.nullable().default(null),
3000
+ securityLevel: AttestationSecurityLevelSchema.nullable().default(null),
3001
+ hardwareBacked: z13.boolean().default(false),
3002
+ attestedAtMs: z13.number().int().nonnegative().nullable().default(null),
2932
3003
  createdAtMs: z13.number().int().nonnegative(),
2933
3004
  revokedAtMs: z13.number().int().nonnegative().nullable()
2934
3005
  });
@@ -2937,7 +3008,10 @@ var ConsumerOACSchema = z13.object({
2937
3008
  issuerId: z13.string().min(1).max(64),
2938
3009
  userId: z13.string().uuid(),
2939
3010
  deviceId: z13.string().min(1).max(128),
2940
- devicePubkeyHex: Hex64,
3011
+ /** Always 'p256'. Field retained for forward-compat. */
3012
+ alg: z13.literal("p256").default("p256"),
3013
+ /** P-256 SubjectPublicKeyInfo DER, base64. */
3014
+ devicePubkeySpkiB64: Base64Std3.min(64).max(4096),
2941
3015
  perTxCapKobo: z13.number().int().positive(),
2942
3016
  cumulativeCapKobo: z13.number().int().positive(),
2943
3017
  currency: z13.string().length(3),
@@ -2948,8 +3022,10 @@ var ConsumerOACSchema = z13.object({
2948
3022
  });
2949
3023
  var SignedConsumerOACSchema = z13.object({
2950
3024
  oac: ConsumerOACSchema,
2951
- issuerSig: HexAny,
2952
- issuerPublicKeyHex: Hex64
3025
+ /** ASN.1 DER ECDSA P-256 issuer signature, base64. */
3026
+ issuerSig: Base64Std3.min(16).max(2048),
3027
+ /** Issuer's P-256 public key as SubjectPublicKeyInfo DER, base64. */
3028
+ issuerPublicKeySpkiB64: Base64Std3.min(64).max(4096)
2953
3029
  });
2954
3030
  var OACRecordSchema = SignedConsumerOACSchema.extend({
2955
3031
  currentOfflineSpentKobo: z13.number().int().nonnegative(),
@@ -2981,6 +3057,7 @@ var EnableOfflineInputSchema = z13.object({
2981
3057
  installId: z13.string().min(1).max(128),
2982
3058
  partnerId: z13.string().min(1).max(64).optional()
2983
3059
  });
3060
+ var ProvisionOfflineAllowanceInputSchema = EnableOfflineInputSchema;
2984
3061
  var DisableOfflineInputSchema = z13.object({
2985
3062
  deviceId: z13.string().min(1).max(128),
2986
3063
  installId: z13.string().min(1).max(128).optional(),
@@ -3018,6 +3095,7 @@ var EnableOfflineResultSchema = z13.object({
3018
3095
  hold: OfflineHoldRecordSchema,
3019
3096
  oac: OACRecordSchema
3020
3097
  });
3098
+ var ProvisionOfflineAllowanceResultSchema = EnableOfflineResultSchema;
3021
3099
  var DisableOfflineResultSchema = z13.object({
3022
3100
  hold: OfflineHoldRecordSchema,
3023
3101
  trusted: z13.boolean(),
@@ -3031,6 +3109,8 @@ var OfflineStateResultSchema = z13.object({
3031
3109
  active: OACRecordSchema.nullable()
3032
3110
  });
3033
3111
  var ConsumerPaymentClaimSchema = z13.object({
3112
+ /** Always 'p256'. Retained for forward-compat and as an explicit domain marker. */
3113
+ alg: z13.literal("p256").default("p256"),
3034
3114
  oacId: z13.string().uuid(),
3035
3115
  encounterId: Sha256Hex.optional(),
3036
3116
  payerUserId: z13.string().uuid(),
@@ -3043,10 +3123,10 @@ var ConsumerPaymentClaimSchema = z13.object({
3043
3123
  occurredAtMs: z13.number().int().nonnegative(),
3044
3124
  completedAtMs: z13.number().int().nonnegative().optional(),
3045
3125
  contextId: z13.string().max(128).optional(),
3046
- payerPubkeyHex: Hex64,
3047
- payerSignature: HexAny,
3048
- payeePubkeyHex: Hex64.optional(),
3049
- payeeSignature: HexAny.optional()
3126
+ payerPubkeySpkiB64: Base64Std3.min(64).max(4096),
3127
+ payerSignatureDerB64: Base64Std3.min(16).max(2048),
3128
+ payeePubkeySpkiB64: Base64Std3.min(64).max(4096).optional(),
3129
+ payeeSignatureDerB64: Base64Std3.min(16).max(2048).optional()
3050
3130
  });
3051
3131
  var ConsumerSettlementSchema = z13.object({
3052
3132
  settlementId: z13.string().uuid(),
@@ -3060,7 +3140,8 @@ var ConsumerSettlementSchema = z13.object({
3060
3140
  status: z13.enum(["SETTLED", "REVIEW"]),
3061
3141
  reviewReason: z13.string().nullable(),
3062
3142
  ledgerRef: z13.string().nullable(),
3063
- issuerSig: HexAny,
3143
+ /** ASN.1 DER ECDSA P-256 issuer signature, base64. */
3144
+ issuerSig: Base64Std3.min(16).max(2048),
3064
3145
  createdAtMs: z13.number().int().nonnegative()
3065
3146
  });
3066
3147
  var ConsumerSettleResultSchema = z13.object({
@@ -3112,6 +3193,18 @@ function createMeOfflineClient(opts) {
3112
3193
  RegisterDeviceKeyInputSchema.parse(input),
3113
3194
  (raw) => DeviceKeyRecordSchema.parse(raw)
3114
3195
  ),
3196
+ issueP256EnrollmentChallenge: (input) => call(
3197
+ "POST",
3198
+ "/v1/me/offline/keys/p256/challenge",
3199
+ P256EnrollmentChallengeInputSchema.parse(input),
3200
+ (raw) => P256EnrollmentChallengeResultSchema.parse(raw)
3201
+ ),
3202
+ registerDeviceKeyP256: (input) => call(
3203
+ "POST",
3204
+ "/v1/me/offline/keys/p256",
3205
+ RegisterDeviceKeyP256InputSchema.parse(input),
3206
+ (raw) => DeviceKeyRecordSchema.parse(raw)
3207
+ ),
3115
3208
  listDeviceKeys: () => call(
3116
3209
  "GET",
3117
3210
  "/v1/me/offline/keys",
@@ -3124,6 +3217,12 @@ function createMeOfflineClient(opts) {
3124
3217
  RevokeDeviceKeyInputSchema.parse(input),
3125
3218
  () => void 0
3126
3219
  ),
3220
+ provisionAllowance: (input) => call(
3221
+ "POST",
3222
+ "/v1/me/offline/allowance",
3223
+ ProvisionOfflineAllowanceInputSchema.parse(input),
3224
+ (raw) => ProvisionOfflineAllowanceResultSchema.parse(raw)
3225
+ ),
3127
3226
  enable: (input) => call(
3128
3227
  "POST",
3129
3228
  "/v1/me/offline/enable",
@@ -3169,6 +3268,185 @@ function createMeOfflineClient(opts) {
3169
3268
  };
3170
3269
  }
3171
3270
 
3271
+ // src/me-offline/signer.ts
3272
+ import { p256 as p2562 } from "@noble/curves/nist";
3273
+ var CLAIM_DOMAIN_V2 = "flur:consumer-offline:v2:claim";
3274
+ function canonicalClaimSigningPayload(claim) {
3275
+ return {
3276
+ domain: CLAIM_DOMAIN_V2,
3277
+ alg: claim.alg,
3278
+ oacId: claim.oacId,
3279
+ payerUserId: claim.payerUserId,
3280
+ payeeUserId: claim.payeeUserId,
3281
+ payerDeviceId: claim.payerDeviceId,
3282
+ payerNonce: claim.payerNonce,
3283
+ payeeNonce: claim.payeeNonce,
3284
+ amountKobo: claim.amountKobo,
3285
+ currency: claim.currency,
3286
+ occurredAtMs: claim.occurredAtMs,
3287
+ completedAtMs: claim.completedAtMs ?? null,
3288
+ contextId: claim.contextId ?? null
3289
+ };
3290
+ }
3291
+ function canonicalClaimSigningBytes(claim) {
3292
+ return canonicalJSONBytes(canonicalClaimSigningPayload(claim));
3293
+ }
3294
+ function bytesToBase642(bytes) {
3295
+ if (typeof Buffer !== "undefined") {
3296
+ return Buffer.from(bytes).toString("base64");
3297
+ }
3298
+ let bin = "";
3299
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
3300
+ return btoa(bin);
3301
+ }
3302
+ function base64ToBytes2(b64) {
3303
+ if (typeof Buffer !== "undefined") {
3304
+ return new Uint8Array(Buffer.from(b64, "base64"));
3305
+ }
3306
+ const bin = atob(b64);
3307
+ const out = new Uint8Array(bin.length);
3308
+ for (let i = 0; i < bin.length; i++) out[i] = bin.charCodeAt(i);
3309
+ return out;
3310
+ }
3311
+ var P256_SPKI_HEADER2 = new Uint8Array([
3312
+ 48,
3313
+ 89,
3314
+ 48,
3315
+ 19,
3316
+ 6,
3317
+ 7,
3318
+ 42,
3319
+ 134,
3320
+ 72,
3321
+ 206,
3322
+ 61,
3323
+ 2,
3324
+ 1,
3325
+ 6,
3326
+ 8,
3327
+ 42,
3328
+ 134,
3329
+ 72,
3330
+ 206,
3331
+ 61,
3332
+ 3,
3333
+ 1,
3334
+ 7,
3335
+ 3,
3336
+ 66,
3337
+ 0
3338
+ ]);
3339
+ function p256PublicKeyToSpkiB64(rawUncompressed) {
3340
+ if (rawUncompressed.length !== 65 || rawUncompressed[0] !== 4) {
3341
+ throw new Error("p256: expected 65-byte uncompressed point");
3342
+ }
3343
+ const out = new Uint8Array(P256_SPKI_HEADER2.length + rawUncompressed.length);
3344
+ out.set(P256_SPKI_HEADER2, 0);
3345
+ out.set(rawUncompressed, P256_SPKI_HEADER2.length);
3346
+ return bytesToBase642(out);
3347
+ }
3348
+ function p256SpkiB64ToPublicKey(spkiB64) {
3349
+ const spki = base64ToBytes2(spkiB64);
3350
+ if (spki.length !== P256_SPKI_HEADER2.length + 65) {
3351
+ throw new Error("p256: invalid SPKI length");
3352
+ }
3353
+ for (let i = 0; i < P256_SPKI_HEADER2.length; i++) {
3354
+ if (spki[i] !== P256_SPKI_HEADER2[i]) {
3355
+ throw new Error("p256: invalid SPKI header");
3356
+ }
3357
+ }
3358
+ return spki.slice(P256_SPKI_HEADER2.length);
3359
+ }
3360
+ function createSoftwareP256Signer(privateKey) {
3361
+ const raw = p2562.getPublicKey(privateKey, false);
3362
+ const spkiB64 = p256PublicKeyToSpkiB64(raw);
3363
+ return {
3364
+ alg: "p256",
3365
+ async getPublicKey() {
3366
+ return { alg: "p256", publicKey: spkiB64 };
3367
+ },
3368
+ async sign(bytes) {
3369
+ const sig = p2562.sign(bytes, privateKey, { prehash: true });
3370
+ const der = sig.toBytes("der");
3371
+ return { alg: "p256", signature: bytesToBase642(der) };
3372
+ }
3373
+ };
3374
+ }
3375
+ function verifyClaimSignature(input) {
3376
+ try {
3377
+ if (input.alg !== "p256") return false;
3378
+ const sigDer = base64ToBytes2(input.signature);
3379
+ const pub = p256SpkiB64ToPublicKey(input.publicKey);
3380
+ return p2562.verify(sigDer, input.bytes, pub, {
3381
+ prehash: true,
3382
+ format: "der"
3383
+ });
3384
+ } catch {
3385
+ return false;
3386
+ }
3387
+ }
3388
+
3389
+ // src/me-offline/sms.ts
3390
+ var OFFLINE_CLAIM_SMS_PREFIX = "FLURC1.";
3391
+ var TOKEN_RE = /(?:^|\s)(FLURC1\.[A-Za-z0-9_-]+={0,2})(?:\s|$)/;
3392
+ function encodeOfflineClaimSmsMessage(claim) {
3393
+ const parsed = ConsumerPaymentClaimSchema.parse(claim);
3394
+ const json = JSON.stringify(parsed);
3395
+ return `${OFFLINE_CLAIM_SMS_PREFIX}${base64UrlEncodeUtf8(json)}`;
3396
+ }
3397
+ function decodeOfflineClaimSmsMessage(message) {
3398
+ const token = extractOfflineClaimSmsToken(message);
3399
+ if (!token) {
3400
+ throw new Error("offline claim SMS token not found");
3401
+ }
3402
+ const encoded = token.slice(OFFLINE_CLAIM_SMS_PREFIX.length);
3403
+ let raw;
3404
+ try {
3405
+ raw = JSON.parse(base64UrlDecodeUtf8(encoded));
3406
+ } catch {
3407
+ throw new Error("offline claim SMS token is malformed");
3408
+ }
3409
+ const parsed = ConsumerPaymentClaimSchema.safeParse(raw);
3410
+ if (!parsed.success) {
3411
+ throw new Error("offline claim SMS token is invalid");
3412
+ }
3413
+ return parsed.data;
3414
+ }
3415
+ function extractOfflineClaimSmsToken(message) {
3416
+ const trimmed = message.trim();
3417
+ if (trimmed.startsWith(OFFLINE_CLAIM_SMS_PREFIX)) {
3418
+ return trimmed.split(/\s+/, 1)[0] ?? null;
3419
+ }
3420
+ return TOKEN_RE.exec(message)?.[1] ?? null;
3421
+ }
3422
+ function base64UrlEncodeUtf8(input) {
3423
+ const bytes = new TextEncoder().encode(input);
3424
+ let binary = "";
3425
+ for (const byte of bytes) binary += String.fromCharCode(byte);
3426
+ const base64 = typeof btoa === "function" ? btoa(binary) : typeof Buffer !== "undefined" ? Buffer.from(bytes).toString("base64") : void 0;
3427
+ if (!base64) throw new Error("base64 encoder unavailable");
3428
+ return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");
3429
+ }
3430
+ function base64UrlDecodeUtf8(input) {
3431
+ const base64 = input.replace(/-/g, "+").replace(/_/g, "/");
3432
+ const padded = base64.padEnd(
3433
+ base64.length + (4 - base64.length % 4) % 4,
3434
+ "="
3435
+ );
3436
+ if (typeof atob === "function") {
3437
+ const binary = atob(padded);
3438
+ const bytes = new Uint8Array(binary.length);
3439
+ for (let index = 0; index < binary.length; index++) {
3440
+ bytes[index] = binary.charCodeAt(index);
3441
+ }
3442
+ return new TextDecoder().decode(bytes);
3443
+ }
3444
+ if (typeof Buffer !== "undefined") {
3445
+ return Buffer.from(padded, "base64").toString("utf8");
3446
+ }
3447
+ throw new Error("base64 decoder unavailable");
3448
+ }
3449
+
3172
3450
  // src/partner-funding/client.ts
3173
3451
  import { z as z14 } from "zod";
3174
3452
  var MinorString = z14.string().regex(/^-?\d+$/);
@@ -3544,14 +3822,15 @@ function buildArtifactBody(input) {
3544
3822
  return { ...header, data: input.data };
3545
3823
  }
3546
3824
  function signArtifact(body, privateKey) {
3547
- const sig = bytesToHex(sign(canonicalJSONBytes(body), privateKey));
3825
+ const sig = signIssuerP256(canonicalJSONBytes(body), privateKey);
3548
3826
  return { body, sig };
3549
3827
  }
3550
3828
  function encodeArtifactUri(signed) {
3551
3829
  const bodyBytes = canonicalJSONBytes(signed.body);
3552
3830
  const bodyB64 = base64UrlEncode(bodyBytes);
3553
- const sigB64 = base64UrlEncode(hexToBytes(signed.sig));
3554
- return `${FLUR_ARTIFACT_URI_PREFIX}${signed.body.t}/${bodyB64}.${sigB64}`;
3831
+ const sigBytes = base64UrlDecode(signed.sig);
3832
+ const sigB64Url = base64UrlEncode(sigBytes);
3833
+ return `${FLUR_ARTIFACT_URI_PREFIX}${signed.body.t}/${bodyB64}.${sigB64Url}`;
3555
3834
  }
3556
3835
  function decodeArtifactUri(uri) {
3557
3836
  if (!uri.startsWith(FLUR_ARTIFACT_URI_PREFIX)) {
@@ -3582,9 +3861,9 @@ function decodeArtifactUri(uri) {
3582
3861
  }
3583
3862
  const bodyBytes = base64UrlDecode(payload.slice(0, dot));
3584
3863
  const sigBytes = base64UrlDecode(payload.slice(dot + 1));
3585
- if (sigBytes.length !== 64) {
3864
+ if (sigBytes.length < 64 || sigBytes.length > 80) {
3586
3865
  throw new FlurArtifactError(
3587
- `Signature must be 64 bytes, got ${sigBytes.length}`,
3866
+ `Signature length out of range: ${sigBytes.length}`,
3588
3867
  "INVALID_SIGNATURE"
3589
3868
  );
3590
3869
  }
@@ -3611,17 +3890,26 @@ function decodeArtifactUri(uri) {
3611
3890
  type,
3612
3891
  bodyBytes,
3613
3892
  body: bodyJson,
3614
- sig: bytesToHex(sigBytes)
3893
+ // Encode as standard base64 (not url-safe) for sig field consistency.
3894
+ sig: encodeStdBase64(sigBytes)
3615
3895
  };
3616
3896
  }
3617
- function verifyArtifactSignature(decoded, publicKey, options = {}) {
3897
+ function encodeStdBase64(bytes) {
3898
+ if (typeof Buffer !== "undefined") {
3899
+ return Buffer.from(bytes).toString("base64");
3900
+ }
3901
+ let bin = "";
3902
+ for (let i = 0; i < bytes.length; i++) bin += String.fromCharCode(bytes[i]);
3903
+ return typeof btoa === "function" ? btoa(bin) : "";
3904
+ }
3905
+ function verifyArtifactSignature(decoded, publicKeySpkiB64, options = {}) {
3618
3906
  if (options.enforceExpiry !== false && decoded.body.exp !== void 0) {
3619
3907
  const now = options.nowSeconds ?? Math.floor(Date.now() / 1e3);
3620
3908
  if (decoded.body.exp < now) {
3621
3909
  throw new FlurArtifactError("Artifact has expired", "EXPIRED");
3622
3910
  }
3623
3911
  }
3624
- return verify(decoded.bodyBytes, hexToBytes(decoded.sig), publicKey);
3912
+ return verifyIssuerP256(decoded.bodyBytes, decoded.sig, publicKeySpkiB64);
3625
3913
  }
3626
3914
 
3627
3915
  // src/artifacts/types.ts
@@ -3640,7 +3928,7 @@ var ARTIFACT_TYPES = {
3640
3928
  PASS: "pass",
3641
3929
  IDENTITY: "identity"
3642
3930
  };
3643
- var HexString4 = (length) => z16.string().regex(
3931
+ var HexString = (length) => z16.string().regex(
3644
3932
  new RegExp(`^[0-9a-fA-F]{${length * 2}}$`),
3645
3933
  `expected ${length}-byte hex string`
3646
3934
  );
@@ -3658,7 +3946,7 @@ var ReceiptArtifactSchema = z16.object({
3658
3946
  settledAtMs: z16.number().int().positive(),
3659
3947
  ledgerTxnId: z16.string().min(1).max(64).optional(),
3660
3948
  memo: z16.string().max(140).optional(),
3661
- hashChainPrev: HexString4(32).optional()
3949
+ hashChainPrev: HexString(32).optional()
3662
3950
  });
3663
3951
  var ShortId = z16.string().min(1).max(64);
3664
3952
  var PositiveInt = z16.number().int().positive();
@@ -3740,7 +4028,7 @@ var StatementArtifactSchema = z16.object({
3740
4028
  closingBalanceKobo: z16.number().int(),
3741
4029
  transactionCount: NonNegativeInt,
3742
4030
  currency: Currency2,
3743
- hashChainPrev: HexString4(32).optional()
4031
+ hashChainPrev: HexString(32).optional()
3744
4032
  }).refine((v) => v.periodEndMs > v.periodStartMs, {
3745
4033
  message: "periodEndMs must be greater than periodStartMs",
3746
4034
  path: ["periodEndMs"]
@@ -3773,7 +4061,7 @@ var IdentityArtifactSchema = z16.object({
3773
4061
  "kyc_tier",
3774
4062
  "age_band"
3775
4063
  ]),
3776
- claimValueHash: HexString4(32),
4064
+ claimValueHash: HexString(32),
3777
4065
  attestedAtMs: PositiveInt
3778
4066
  });
3779
4067
  var ARTIFACT_BODY_SCHEMAS = {
@@ -3837,7 +4125,7 @@ function createArtifactUri(input) {
3837
4125
  const signed = signArtifact(body, input.privateKey);
3838
4126
  return { uri: encodeArtifactUri(signed), signed };
3839
4127
  }
3840
- function verifyArtifactUri(uri, publicKey, options = {}) {
4128
+ function verifyArtifactUri(uri, publicKeySpkiB64, options = {}) {
3841
4129
  const decoded = decodeArtifactUri(uri);
3842
4130
  if (!isKnownArtifactType(decoded.type)) {
3843
4131
  throw new FlurArtifactError(
@@ -3859,7 +4147,7 @@ function verifyArtifactUri(uri, publicKey, options = {}) {
3859
4147
  "INVALID_BODY"
3860
4148
  );
3861
4149
  }
3862
- const ok = verifyArtifactSignature(decoded, publicKey, options);
4150
+ const ok = verifyArtifactSignature(decoded, publicKeySpkiB64, options);
3863
4151
  if (!ok) {
3864
4152
  throw new FlurArtifactError(
3865
4153
  "Artifact signature verification failed",
@@ -3895,6 +4183,8 @@ export {
3895
4183
  AccountSchema,
3896
4184
  ApiCredentialPublicSchema,
3897
4185
  ArtifactHeaderSchema,
4186
+ AttestationSecurityLevelSchema,
4187
+ CLAIM_DOMAIN_V2,
3898
4188
  COLLECTION_INTENT_STATUSES,
3899
4189
  COLLECTION_PAYMENT_STATUSES,
3900
4190
  CUSTODIAL_MODES,
@@ -3913,6 +4203,7 @@ export {
3913
4203
  CreatePayoutInputSchema,
3914
4204
  CreateWithdrawalInputSchema,
3915
4205
  CreateWithdrawalResultSchema,
4206
+ DeviceKeyAlgSchema,
3916
4207
  DeviceKeyRecordSchema,
3917
4208
  DisableOfflineInputSchema,
3918
4209
  DisableOfflineResultSchema,
@@ -3949,6 +4240,7 @@ export {
3949
4240
  OAC_DEFAULT_CUMULATIVE_KOBO,
3950
4241
  OAC_DEFAULT_PER_TX_KOBO,
3951
4242
  OAC_DEFAULT_VALIDITY_MS,
4243
+ OFFLINE_CLAIM_SMS_PREFIX,
3952
4244
  OfflineClaimArtifactSchema,
3953
4245
  OfflineHoldRecordSchema,
3954
4246
  OfflinePaymentAuthorizationArtifactSchema,
@@ -3957,6 +4249,8 @@ export {
3957
4249
  OfflineStateResultSchema,
3958
4250
  OfflineStatusResultSchema,
3959
4251
  OfflineTokenSchema,
4252
+ P256EnrollmentChallengeInputSchema,
4253
+ P256EnrollmentChallengeResultSchema,
3960
4254
  PARTNER_FUNDING_DIRECTIONS,
3961
4255
  PARTNER_FUNDING_STATUSES,
3962
4256
  PARTNER_KINDS,
@@ -3980,6 +4274,8 @@ export {
3980
4274
  PayoutEventInputSchema,
3981
4275
  ProviderEventInputSchema,
3982
4276
  ProviderEventRecordSchema,
4277
+ ProvisionOfflineAllowanceInputSchema,
4278
+ ProvisionOfflineAllowanceResultSchema,
3983
4279
  PublicCollectionIntentSchema,
3984
4280
  RECEIPT_CHANNELS,
3985
4281
  RECEIPT_KINDS,
@@ -3991,6 +4287,7 @@ export {
3991
4287
  RecordPayoutEventResultSchema,
3992
4288
  RedemptionSchema,
3993
4289
  RegisterDeviceKeyInputSchema,
4290
+ RegisterDeviceKeyP256InputSchema,
3994
4291
  ReversalRecordArtifactSchema,
3995
4292
  RevokeDeviceKeyInputSchema,
3996
4293
  SETTLEMENT_SCHEDULES,
@@ -4013,6 +4310,8 @@ export {
4013
4310
  buildPaymentRequest,
4014
4311
  buildReceipt,
4015
4312
  buildRedemption,
4313
+ canonicalClaimSigningBytes,
4314
+ canonicalClaimSigningPayload,
4016
4315
  canonicalJSONBytes,
4017
4316
  canonicalJSONStringify,
4018
4317
  canonicalRequestString,
@@ -4037,18 +4336,21 @@ export {
4037
4336
  createPassesClient,
4038
4337
  createReceiptArtifactUri,
4039
4338
  createReceiptsClient,
4339
+ createSoftwareP256Signer,
4040
4340
  decodeArtifactUri,
4041
4341
  decodeAuthorizationQR,
4042
4342
  decodeBase45,
4343
+ decodeOfflineClaimSmsMessage,
4043
4344
  decodePaymentRequestQR,
4044
4345
  encodeArtifactUri,
4045
4346
  encodeAuthorizationQR,
4046
4347
  encodeBase45,
4047
4348
  encodeNQR,
4349
+ encodeOfflineClaimSmsMessage,
4048
4350
  encodePaymentRequestQR,
4351
+ extractOfflineClaimSmsToken,
4049
4352
  formatAmount,
4050
4353
  generateDynamicQR,
4051
- generateKeyPair,
4052
4354
  generateStaticQR,
4053
4355
  init,
4054
4356
  isHardenedArtifactType,
@@ -4059,13 +4361,10 @@ export {
4059
4361
  parseAmountInput,
4060
4362
  parseNQR,
4061
4363
  parseQR,
4062
- publicKeyFromPrivate,
4063
4364
  readTLV,
4064
4365
  routingHint,
4065
- sign,
4066
4366
  signArtifact,
4067
4367
  signAuthorization,
4068
- signCanonical,
4069
4368
  signOAC,
4070
4369
  signPartnerRequest,
4071
4370
  signPass,
@@ -4073,11 +4372,10 @@ export {
4073
4372
  signReceipt,
4074
4373
  signRedemption,
4075
4374
  signRequestHMAC,
4076
- verify,
4077
4375
  verifyArtifactSignature,
4078
4376
  verifyArtifactUri,
4079
4377
  verifyAuthorization,
4080
- verifyCanonical,
4378
+ verifyClaimSignature,
4081
4379
  verifyOAC,
4082
4380
  verifyPass,
4083
4381
  verifyPaymentRequest,