@fgv/ts-extras 5.1.0-36 → 5.1.0-39

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.
Files changed (128) hide show
  1. package/dist/packlets/ai-assist/apiClient.js +66 -111
  2. package/dist/packlets/ai-assist/apiClient.js.map +1 -1
  3. package/dist/packlets/ai-assist/converters.js +16 -3
  4. package/dist/packlets/ai-assist/converters.js.map +1 -1
  5. package/dist/packlets/ai-assist/embeddingClient.js +5 -6
  6. package/dist/packlets/ai-assist/embeddingClient.js.map +1 -1
  7. package/dist/packlets/ai-assist/imageOptionsResolver.js +0 -19
  8. package/dist/packlets/ai-assist/imageOptionsResolver.js.map +1 -1
  9. package/dist/packlets/ai-assist/index.js +2 -2
  10. package/dist/packlets/ai-assist/index.js.map +1 -1
  11. package/dist/packlets/ai-assist/model.js +139 -7
  12. package/dist/packlets/ai-assist/model.js.map +1 -1
  13. package/dist/packlets/ai-assist/registry.js +58 -51
  14. package/dist/packlets/ai-assist/registry.js.map +1 -1
  15. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js +38 -11
  16. package/dist/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  17. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +187 -36
  18. package/dist/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
  19. package/dist/packlets/ai-assist/streamingAdapters/common.js +13 -0
  20. package/dist/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  21. package/dist/packlets/ai-assist/streamingAdapters/gemini.js +12 -4
  22. package/dist/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  23. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js +6 -1
  24. package/dist/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  25. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -3
  26. package/dist/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  27. package/dist/packlets/ai-assist/streamingAdapters/proxy.js +5 -2
  28. package/dist/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
  29. package/dist/packlets/ai-assist/streamingClient.js +16 -16
  30. package/dist/packlets/ai-assist/streamingClient.js.map +1 -1
  31. package/dist/packlets/crypto-utils/converters.js +18 -0
  32. package/dist/packlets/crypto-utils/converters.js.map +1 -1
  33. package/dist/packlets/crypto-utils/index.browser.js +2 -2
  34. package/dist/packlets/crypto-utils/index.browser.js.map +1 -1
  35. package/dist/packlets/crypto-utils/index.js +2 -2
  36. package/dist/packlets/crypto-utils/index.js.map +1 -1
  37. package/dist/packlets/crypto-utils/keystore/keyStore.js +19 -4
  38. package/dist/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
  39. package/dist/packlets/crypto-utils/keystore/model.js.map +1 -1
  40. package/dist/packlets/crypto-utils/model.js.map +1 -1
  41. package/dist/packlets/crypto-utils/spkiHelpers.js +104 -25
  42. package/dist/packlets/crypto-utils/spkiHelpers.js.map +1 -1
  43. package/dist/ts-extras.d.ts +377 -132
  44. package/dist/tsdoc-metadata.json +1 -1
  45. package/lib/packlets/ai-assist/apiClient.d.ts +12 -2
  46. package/lib/packlets/ai-assist/apiClient.d.ts.map +1 -1
  47. package/lib/packlets/ai-assist/apiClient.js +65 -110
  48. package/lib/packlets/ai-assist/apiClient.js.map +1 -1
  49. package/lib/packlets/ai-assist/converters.d.ts +8 -2
  50. package/lib/packlets/ai-assist/converters.d.ts.map +1 -1
  51. package/lib/packlets/ai-assist/converters.js +17 -4
  52. package/lib/packlets/ai-assist/converters.js.map +1 -1
  53. package/lib/packlets/ai-assist/embeddingClient.d.ts.map +1 -1
  54. package/lib/packlets/ai-assist/embeddingClient.js +4 -5
  55. package/lib/packlets/ai-assist/embeddingClient.js.map +1 -1
  56. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts +0 -8
  57. package/lib/packlets/ai-assist/imageOptionsResolver.d.ts.map +1 -1
  58. package/lib/packlets/ai-assist/imageOptionsResolver.js +0 -19
  59. package/lib/packlets/ai-assist/imageOptionsResolver.js.map +1 -1
  60. package/lib/packlets/ai-assist/index.d.ts +3 -3
  61. package/lib/packlets/ai-assist/index.d.ts.map +1 -1
  62. package/lib/packlets/ai-assist/index.js +6 -1
  63. package/lib/packlets/ai-assist/index.js.map +1 -1
  64. package/lib/packlets/ai-assist/model.d.ts +181 -98
  65. package/lib/packlets/ai-assist/model.d.ts.map +1 -1
  66. package/lib/packlets/ai-assist/model.js +143 -8
  67. package/lib/packlets/ai-assist/model.js.map +1 -1
  68. package/lib/packlets/ai-assist/registry.d.ts.map +1 -1
  69. package/lib/packlets/ai-assist/registry.js +58 -51
  70. package/lib/packlets/ai-assist/registry.js.map +1 -1
  71. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts +1 -1
  72. package/lib/packlets/ai-assist/streamingAdapters/anthropic.d.ts.map +1 -1
  73. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js +37 -10
  74. package/lib/packlets/ai-assist/streamingAdapters/anthropic.js.map +1 -1
  75. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts +82 -3
  76. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.d.ts.map +1 -1
  77. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js +187 -35
  78. package/lib/packlets/ai-assist/streamingAdapters/clientToolContinuationBuilder.js.map +1 -1
  79. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts +24 -1
  80. package/lib/packlets/ai-assist/streamingAdapters/common.d.ts.map +1 -1
  81. package/lib/packlets/ai-assist/streamingAdapters/common.js +14 -1
  82. package/lib/packlets/ai-assist/streamingAdapters/common.js.map +1 -1
  83. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts +10 -1
  84. package/lib/packlets/ai-assist/streamingAdapters/gemini.d.ts.map +1 -1
  85. package/lib/packlets/ai-assist/streamingAdapters/gemini.js +12 -4
  86. package/lib/packlets/ai-assist/streamingAdapters/gemini.js.map +1 -1
  87. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts +1 -1
  88. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.d.ts.map +1 -1
  89. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js +6 -1
  90. package/lib/packlets/ai-assist/streamingAdapters/openaiChat.js.map +1 -1
  91. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts +1 -1
  92. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.d.ts.map +1 -1
  93. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js +10 -3
  94. package/lib/packlets/ai-assist/streamingAdapters/openaiResponses.js.map +1 -1
  95. package/lib/packlets/ai-assist/streamingAdapters/proxy.d.ts.map +1 -1
  96. package/lib/packlets/ai-assist/streamingAdapters/proxy.js +5 -2
  97. package/lib/packlets/ai-assist/streamingAdapters/proxy.js.map +1 -1
  98. package/lib/packlets/ai-assist/streamingClient.d.ts +1 -1
  99. package/lib/packlets/ai-assist/streamingClient.d.ts.map +1 -1
  100. package/lib/packlets/ai-assist/streamingClient.js +15 -15
  101. package/lib/packlets/ai-assist/streamingClient.js.map +1 -1
  102. package/lib/packlets/crypto-utils/converters.d.ts +9 -1
  103. package/lib/packlets/crypto-utils/converters.d.ts.map +1 -1
  104. package/lib/packlets/crypto-utils/converters.js +19 -1
  105. package/lib/packlets/crypto-utils/converters.js.map +1 -1
  106. package/lib/packlets/crypto-utils/index.browser.d.ts +1 -1
  107. package/lib/packlets/crypto-utils/index.browser.d.ts.map +1 -1
  108. package/lib/packlets/crypto-utils/index.browser.js +6 -2
  109. package/lib/packlets/crypto-utils/index.browser.js.map +1 -1
  110. package/lib/packlets/crypto-utils/index.d.ts +1 -1
  111. package/lib/packlets/crypto-utils/index.d.ts.map +1 -1
  112. package/lib/packlets/crypto-utils/index.js +6 -2
  113. package/lib/packlets/crypto-utils/index.js.map +1 -1
  114. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts +9 -2
  115. package/lib/packlets/crypto-utils/keystore/keyStore.d.ts.map +1 -1
  116. package/lib/packlets/crypto-utils/keystore/keyStore.js +19 -4
  117. package/lib/packlets/crypto-utils/keystore/keyStore.js.map +1 -1
  118. package/lib/packlets/crypto-utils/keystore/model.d.ts +7 -0
  119. package/lib/packlets/crypto-utils/keystore/model.d.ts.map +1 -1
  120. package/lib/packlets/crypto-utils/keystore/model.js.map +1 -1
  121. package/lib/packlets/crypto-utils/model.d.ts +11 -1
  122. package/lib/packlets/crypto-utils/model.d.ts.map +1 -1
  123. package/lib/packlets/crypto-utils/model.js.map +1 -1
  124. package/lib/packlets/crypto-utils/spkiHelpers.d.ts +59 -2
  125. package/lib/packlets/crypto-utils/spkiHelpers.d.ts.map +1 -1
  126. package/lib/packlets/crypto-utils/spkiHelpers.js +108 -25
  127. package/lib/packlets/crypto-utils/spkiHelpers.js.map +1 -1
  128. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/model.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/model.ts"],"names":[],"mappings":";AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAgsBZ,0CAMC;AAjsBD,uDAAyC;AAChC,8BAAS;AAoIlB;;;GAGG;AACU,QAAA,oBAAoB,GAAoC;IACnE,YAAY;IACZ,eAAe;IACf,WAAW;IACX,SAAS;IACT,QAAQ;CACT,CAAC;AAqFF;;;;GAIG;AACU,QAAA,kBAAkB,GAAoB;IACjD,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,EAAE;CACP,CAAC;AAEX;;;GAGG;AACU,QAAA,mBAAmB,GAAoB;IAClD,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,EAAE;CACP,CAAC;AAubX,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,IAAa;IAC3C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,OAAO,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,qBAAqB,CAAC;AACxD,CAAC","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { JsonValue } from '@fgv/ts-json-base';\nimport { Result, Uuid } from '@fgv/ts-utils';\n\nimport * as Constants from './constants';\nexport { Constants };\n\n// ============================================================================\n// Encryption Types\n// ============================================================================\n\n/**\n * Supported encryption algorithms.\n * @public\n */\nexport type EncryptionAlgorithm = typeof Constants.DEFAULT_ALGORITHM;\n\n/**\n * Format version for encrypted files.\n * @public\n */\nexport type EncryptedFileFormat = typeof Constants.ENCRYPTED_FILE_FORMAT;\n\n/**\n * Named secret for encryption/decryption.\n * @public\n */\nexport interface INamedSecret {\n /**\n * Unique name for this secret (referenced in encrypted files).\n */\n readonly name: string;\n\n /**\n * The actual secret key (32 bytes for AES-256).\n */\n readonly key: Uint8Array;\n}\n\n/**\n * Result of an encryption operation.\n * @public\n */\nexport interface IEncryptionResult {\n /**\n * Initialization vector used for encryption (12 bytes for GCM).\n */\n readonly iv: Uint8Array;\n\n /**\n * Authentication tag from GCM mode (16 bytes).\n */\n readonly authTag: Uint8Array;\n\n /**\n * The encrypted data.\n */\n readonly encryptedData: Uint8Array;\n}\n\n/**\n * Asymmetric keypair algorithms supported by the crypto provider.\n * - `'ecdsa-p256'`: ECDSA over the P-256 curve, for signing.\n * - `'rsa-oaep-2048'`: RSA-OAEP, 2048-bit modulus with SHA-256, for encryption.\n * - `'ecdh-p256'`: ECDH over the P-256 curve, for key agreement\n * (e.g. as the recipient keypair in\n * {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes} /\n * {@link CryptoUtils.ICryptoProvider.unwrapBytes | unwrapBytes}).\n * - `'ed25519'`: EdDSA over the Edwards25519 curve, for signing.\n * Deterministic — the per-signature nonce is derived from the private key\n * and message rather than sampled randomly, eliminating the random-nonce\n * reuse risk that ECDSA carries. Distinct from X25519 (key agreement over\n * the Montgomery form, Curve25519).\n * - `'x25519'`: Diffie-Hellman key agreement over the Montgomery form of\n * Curve25519. Key-agreement only — use `deriveBits`/`deriveKey` to produce\n * a shared secret from one party's private key and the peer's public key.\n * Distinct from Ed25519 (which uses the twisted-Edwards form for signing).\n * @public\n */\nexport type KeyPairAlgorithm = 'ecdsa-p256' | 'rsa-oaep-2048' | 'ecdh-p256' | 'ed25519' | 'x25519';\n\n/**\n * Caller-supplied HKDF parameters that domain-separate one\n * {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes} call from another.\n * Two wraps that share recipient but differ on `salt` or `info` derive distinct\n * wrap keys, so callers should pick values that bind the wrap to its\n * application context (e.g. a content hash for `salt` and a secret name for\n * `info`).\n *\n * Both fields are required; pass an empty `Uint8Array` if the caller has no\n * value to bind on a given axis. Silent defaulting would hide protocol\n * mistakes, so the API does not pick defaults.\n * @public\n */\nexport interface IWrapBytesOptions {\n /**\n * HKDF salt. Domain-separates this wrap from others in different contexts.\n * Caller picks; common choices include a content hash, document id, channel\n * id, etc.\n */\n readonly salt: Uint8Array;\n\n /**\n * HKDF info. Further binds the derived key to a specific use within the\n * calling application. Caller picks; common choices include a secret name,\n * message type, or version tag.\n */\n readonly info: Uint8Array;\n}\n\n/**\n * Output of {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes}. The\n * shape is JSON-serializable so it can travel directly over the wire or be\n * persisted as-is.\n * @public\n */\nexport interface IWrappedBytes {\n /**\n * Sender's ephemeral ECDH P-256 public key as a JSON Web Key. The matching\n * ephemeral private key is dropped after the shared-secret derive.\n */\n readonly ephemeralPublicKey: JsonWebKey;\n\n /**\n * AES-GCM nonce, base64-encoded. 12 bytes (96 bits) — the standard AES-GCM\n * nonce length.\n */\n readonly nonce: string;\n\n /**\n * AES-GCM ciphertext concatenated with the 16-byte authentication tag,\n * base64-encoded. Tampering with either the nonce or the ciphertext causes\n * unwrap to fail GCM authentication.\n */\n readonly ciphertext: string;\n}\n\n/**\n * All valid key pair algorithms.\n * @public\n */\nexport const allKeyPairAlgorithms: ReadonlyArray<KeyPairAlgorithm> = [\n 'ecdsa-p256',\n 'rsa-oaep-2048',\n 'ecdh-p256',\n 'ed25519',\n 'x25519'\n];\n\n/**\n * Supported key derivation functions.\n * @public\n */\nexport type KeyDerivationFunction = 'pbkdf2' | 'argon2id';\n\n/**\n * PBKDF2 key derivation parameters.\n * @public\n */\nexport interface IPbkdf2KeyDerivationParams {\n /** Key derivation function discriminator. */\n readonly kdf: 'pbkdf2';\n /** Base64-encoded salt used for key derivation. */\n readonly salt: string;\n /** Number of iterations used for key derivation. */\n readonly iterations: number;\n}\n\n/**\n * Argon2id key derivation parameters (RFC 9106).\n * @public\n */\nexport interface IArgon2idKeyDerivationParams {\n /** Key derivation function discriminator. */\n readonly kdf: 'argon2id';\n /** Base64-encoded salt used for key derivation. */\n readonly salt: string;\n /** Memory cost in kibibytes. */\n readonly memoryKiB: number;\n /** Number of passes (time cost). */\n readonly iterations: number;\n /** Degree of parallelism. */\n readonly parallelism: number;\n}\n\n/**\n * Key derivation parameters stored in encrypted files.\n * Discriminated union on `kdf` field: `'pbkdf2'` or `'argon2id'`.\n * @public\n */\nexport type IKeyDerivationParams = IPbkdf2KeyDerivationParams | IArgon2idKeyDerivationParams;\n\n// ============================================================================\n// Argon2id Types\n// ============================================================================\n\n/**\n * Parameters for Argon2id key derivation (RFC 9106).\n * All fields are required; fgv does not pick defaults silently.\n * @public\n */\nexport interface IArgon2idParams {\n /**\n * Memory cost in kibibytes (KiB).\n * OWASP 2023 minimum: 19456 (19 MiB). Stronger: 65536 (64 MiB).\n * Constraint: \\>= 8.\n */\n readonly memoryKiB: number;\n\n /**\n * Number of passes (iterations / time cost).\n * OWASP 2023 minimum: 2. Range: \\>= 1.\n */\n readonly iterations: number;\n\n /**\n * Degree of parallelism (threads).\n * Note: WASM-based implementations compute sequentially regardless of this value,\n * but the value is wired into the algorithm and AFFECTS the output hash bytes.\n * Callers must use the same parallelism value consistently for a given secret.\n * Range: 1–255.\n */\n readonly parallelism: number;\n\n /**\n * Number of output bytes (hash length).\n * Typical values: 16 (128-bit), 32 (256-bit, AES-256 key), 64 (512-bit).\n * Constraint: \\>= 4.\n */\n readonly outputBytes: number;\n}\n\n/**\n * Recommended OWASP 2023 minimum Argon2id parameters.\n * Suitable for recovery-row key derivation (high-entropy inputs).\n * @public\n */\nexport const ARGON2ID_OWASP_MIN: IArgon2idParams = {\n memoryKiB: 19456,\n iterations: 2,\n parallelism: 1,\n outputBytes: 32\n} as const;\n\n/**\n * Stronger Argon2id parameters suitable for user-typed passphrases.\n * @public\n */\nexport const ARGON2ID_PASSPHRASE: IArgon2idParams = {\n memoryKiB: 65536,\n iterations: 3,\n parallelism: 1,\n outputBytes: 32\n} as const;\n\n/**\n * Argon2id key derivation provider (RFC 9106).\n *\n * Implementations are in separate packages to avoid WASM bundle costs for\n * consumers who don't need Argon2id:\n * - Node: `@fgv/ts-extras-argon2` (`NodeArgon2Provider`)\n * - Browser: `@fgv/ts-web-extras-argon2` (`BrowserArgon2Provider`)\n *\n * @public\n */\nexport interface IArgon2idProvider {\n /**\n * Derives key material from a password using Argon2id (RFC 9106 §3.1).\n *\n * Returns the raw derived bytes as a `Uint8Array`. Both Node and browser\n * implementations produce bit-identical output for identical inputs.\n *\n * @param password - Password or passphrase. Accepts string (UTF-8) or raw bytes.\n * @param salt - Salt bytes. Must be random and unique per credential (\\>= 16 bytes recommended).\n * @param params - Argon2id parameters. Use `ARGON2ID_OWASP_MIN` as a starting point.\n * @returns Success with derived bytes, Failure with error context.\n */\n argon2id(\n password: Uint8Array | string,\n salt: Uint8Array,\n params: IArgon2idParams\n ): Promise<Result<Uint8Array>>;\n}\n\n/**\n * Generic encrypted file format.\n * This is the JSON structure stored in encrypted files.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @public\n */\nexport interface IEncryptedFile<TMetadata = JsonValue> {\n /**\n * Format identifier for versioning.\n */\n readonly format: EncryptedFileFormat;\n\n /**\n * Name of the secret required to decrypt (references INamedSecret.name).\n */\n readonly secretName: string;\n\n /**\n * Algorithm used for encryption.\n */\n readonly algorithm: EncryptionAlgorithm;\n\n /**\n * Base64-encoded initialization vector.\n */\n readonly iv: string;\n\n /**\n * Base64-encoded authentication tag (for GCM mode).\n */\n readonly authTag: string;\n\n /**\n * Base64-encoded encrypted data (JSON string when decrypted).\n */\n readonly encryptedData: string;\n\n /**\n * Optional unencrypted metadata for display/filtering.\n */\n readonly metadata?: TMetadata;\n\n /**\n * Optional key derivation parameters.\n * If present, allows decryption using a password with these parameters.\n * If absent, a pre-derived key must be provided.\n */\n readonly keyDerivation?: IKeyDerivationParams;\n}\n\n// ============================================================================\n// Crypto Provider Interface\n// ============================================================================\n\n/**\n * Crypto provider interface for cross-platform encryption.\n * Implementations provided for Node.js (crypto module) and browser (Web Crypto API).\n * @public\n */\nexport interface ICryptoProvider {\n /**\n * Encrypts plaintext using AES-256-GCM.\n * @param plaintext - UTF-8 string to encrypt\n * @param key - 32-byte encryption key\n * @returns Success with encryption result, or Failure with error\n */\n encrypt(plaintext: string, key: Uint8Array): Promise<Result<IEncryptionResult>>;\n\n /**\n * Decrypts ciphertext using AES-256-GCM.\n * @param encryptedData - Encrypted bytes\n * @param key - 32-byte decryption key\n * @param iv - Initialization vector (12 bytes)\n * @param authTag - GCM authentication tag (16 bytes)\n * @returns Success with decrypted UTF-8 string, or Failure with error\n */\n decrypt(\n encryptedData: Uint8Array,\n key: Uint8Array,\n iv: Uint8Array,\n authTag: Uint8Array\n ): Promise<Result<string>>;\n\n /**\n * Generates a random 32-byte key suitable for AES-256.\n * @returns Success with generated key, or Failure with error\n */\n generateKey(): Promise<Result<Uint8Array>>;\n\n /**\n * Derives a key from a password using PBKDF2.\n * @param password - Password string\n * @param salt - Salt bytes (should be at least 16 bytes)\n * @param iterations - Number of iterations (recommend 100000+)\n * @returns Success with derived 32-byte key, or Failure with error\n */\n deriveKey(password: string, salt: Uint8Array, iterations: number): Promise<Result<Uint8Array>>;\n\n /**\n * Computes a SHA-256 hash of the given data.\n * @param data - UTF-8 string to hash\n * @returns Success with hex-encoded hash string, or Failure with error\n */\n sha256(data: string): Promise<Result<string>>;\n\n // ============================================================================\n // Platform Utility Methods\n // ============================================================================\n\n /**\n * Generates cryptographically secure random bytes.\n * @param length - Number of bytes to generate\n * @returns Success with random bytes, or Failure with error\n */\n generateRandomBytes(length: number): Result<Uint8Array>;\n\n /**\n * Generates a cryptographically random UUIDv4 using the provider's\n * underlying source of randomness. The default Node and browser\n * implementations delegate to `globalThis.crypto.randomUUID`;\n * deterministic providers (e.g. test stubs) may override to produce\n * reproducible values.\n * @returns Success with a canonical UUID, or Failure with error.\n */\n generateUuid(): Result<Uuid>;\n\n /**\n * Encodes binary data to base64 string.\n * @param data - Binary data to encode\n * @returns Base64-encoded string\n */\n toBase64(data: Uint8Array): string;\n\n /**\n * Decodes base64 string to binary data.\n * @param base64 - Base64-encoded string\n * @returns Success with decoded bytes, or Failure if invalid base64\n */\n fromBase64(base64: string): Result<Uint8Array>;\n\n // ============================================================================\n // Asymmetric Key Operations\n // ============================================================================\n\n /**\n * Generates a new asymmetric keypair for the requested algorithm.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} to use.\n * @param extractable - Whether the resulting `CryptoKey` objects may be exported.\n * Set `false` on backends that store `CryptoKey` references directly (e.g.\n * IndexedDB). Set `true` when the private key must round-trip through JWK or\n * PKCS#8 (e.g. encrypted-file backends).\n * @returns Success with the generated `CryptoKeyPair`, or Failure with error context.\n */\n generateKeyPair(algorithm: KeyPairAlgorithm, extractable: boolean): Promise<Result<CryptoKeyPair>>;\n\n /**\n * Exports the public half of a keypair as a JSON Web Key.\n * @param publicKey - The public `CryptoKey` to export. Must be an `extractable`\n * key generated for an asymmetric algorithm.\n * @returns Success with the JWK, or Failure with error context.\n */\n exportPublicKeyJwk(publicKey: CryptoKey): Promise<Result<JsonWebKey>>;\n\n /**\n * Re-imports a public-key JWK as a `CryptoKey` usable for verification or\n * encryption (depending on algorithm).\n * @param jwk - The JSON Web Key produced by {@link CryptoUtils.ICryptoProvider.exportPublicKeyJwk | exportPublicKeyJwk}.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} the\n * key was generated for. Determines the import parameters and key usages.\n * @returns Success with the imported public `CryptoKey`, or Failure with error context.\n */\n importPublicKeyJwk(jwk: JsonWebKey, algorithm: KeyPairAlgorithm): Promise<Result<CryptoKey>>;\n\n /**\n * Exports a public `CryptoKey` as a DER-encoded SPKI (SubjectPublicKeyInfo) blob.\n * SPKI is the standard algorithm-agnostic format for public key storage and transport.\n * @param publicKey - The `CryptoKey` to export. Must have `key.type === 'public'`.\n * @returns `Success` with the raw SPKI bytes, or `Failure` with error context.\n */\n exportPublicKeySpki(publicKey: CryptoKey): Promise<Result<Uint8Array>>;\n\n /**\n * Imports a public key from a DER-encoded SPKI blob.\n * @param spkiBytes - The raw SPKI bytes produced by {@link CryptoUtils.ICryptoProvider.exportPublicKeySpki | exportPublicKeySpki}.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} the key was generated for.\n * @returns `Success` with the imported public `CryptoKey`, or `Failure` with error context.\n */\n importPublicKeySpki(spkiBytes: Uint8Array, algorithm: KeyPairAlgorithm): Promise<Result<CryptoKey>>;\n\n /**\n * Wraps `plaintext` for delivery to the holder of the private key paired\n * with `recipientPublicKey`. Uses ECIES with ECDH P-256, HKDF-SHA256, and\n * AES-GCM-256.\n *\n * Generates a fresh ephemeral keypair per call; the ephemeral private key\n * is discarded after the shared-secret derive. Only the recipient (with the\n * matching private key) and the same HKDF parameters can recover\n * `plaintext`.\n *\n * Empty `plaintext` is permitted; the resulting wrap contains only the\n * 16-byte GCM authentication tag and round-trips back to an empty\n * `Uint8Array`.\n * @param plaintext - The bytes to wrap. Any length supported by AES-GCM\n * (in practice, well below 2^39 - 256 bits).\n * @param recipientPublicKey - The recipient's ECDH P-256 public `CryptoKey`.\n * Must have algorithm name `'ECDH'` and named curve `'P-256'`; mismatched\n * algorithm or curve yields a `Failure` with error context.\n * @param options - HKDF parameters; see {@link CryptoUtils.IWrapBytesOptions | IWrapBytesOptions}.\n * @returns `Success` with the wrapped payload, or `Failure` with error context.\n */\n wrapBytes(\n plaintext: Uint8Array,\n recipientPublicKey: CryptoKey,\n options: IWrapBytesOptions\n ): Promise<Result<IWrappedBytes>>;\n\n /**\n * Inverse of {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes}.\n * Recovers the original `plaintext` from a wrapped payload using the\n * recipient's private key.\n *\n * Returns a `Failure` (never throws) on any of:\n * - Tampered nonce or ciphertext (AES-GCM authentication fails)\n * - Wrong private key (different shared secret derives a different wrap key)\n * - Wrong HKDF parameters (different wrap key)\n * - Malformed `ephemeralPublicKey` JWK\n * - Malformed base64 in `nonce` or `ciphertext`\n * @param wrapped - The wrapped payload produced by `wrapBytes`.\n * @param recipientPrivateKey - The recipient's ECDH P-256 private\n * `CryptoKey`. Must have algorithm name `'ECDH'` and named curve `'P-256'`,\n * and key usages including `'deriveKey'` or `'deriveBits'`.\n * @param options - The same HKDF parameters used at wrap time.\n * @returns `Success` with the original `plaintext`, or `Failure` with error context.\n */\n unwrapBytes(\n wrapped: IWrappedBytes,\n recipientPrivateKey: CryptoKey,\n options: IWrapBytesOptions\n ): Promise<Result<Uint8Array>>;\n\n // ============================================================================\n // Signing Operations\n // ============================================================================\n\n /**\n * Signs `data` with `privateKey` using the algorithm inferred from the key.\n * Delegates to `crypto.subtle.sign`; the algorithm is derived from\n * `privateKey.algorithm.name` — ECDSA keys are augmented with\n * `hash: 'SHA-256'` at sign time (the hash is not stored in the key);\n * all other algorithm names are passed through as-is.\n * Intended for Ed25519 and ECDSA-P256 asymmetric private keys; for\n * HMAC-SHA256 authentication codes use {@link ICryptoProvider.hmacSha256} instead.\n * @param privateKey - A `CryptoKey` with `'sign'` usage (e.g. generated by\n * {@link CryptoUtils.ICryptoProvider.generateKeyPair | generateKeyPair} with\n * `'ecdsa-p256'` or `'ed25519'`).\n * @param data - The bytes to sign.\n * @returns `Success` with the raw signature bytes, or `Failure` with error context.\n */\n sign(privateKey: CryptoKey, data: Uint8Array): Promise<Result<Uint8Array>>;\n\n /**\n * Verifies a signature produced by {@link ICryptoProvider.sign}.\n * Delegates to `crypto.subtle.verify`; the algorithm is derived from\n * `publicKey.algorithm.name` — ECDSA keys are augmented with\n * `hash: 'SHA-256'`; all other algorithm names are passed through as-is.\n * Intended for Ed25519 and ECDSA-P256 asymmetric public keys; for\n * HMAC-SHA256 verification use {@link ICryptoProvider.verifyHmacSha256} instead.\n * @param publicKey - A `CryptoKey` with `'verify'` usage (e.g. the public\n * half of a keypair generated by\n * {@link CryptoUtils.ICryptoProvider.generateKeyPair | generateKeyPair} with\n * `'ecdsa-p256'` or `'ed25519'`).\n * @param signature - The raw signature bytes produced by `sign`.\n * @param data - The original data that was signed.\n * @returns `Success` with `true` if the signature is valid, `false` if it is\n * not, or `Failure` with error context if the operation itself failed.\n */\n verify(publicKey: CryptoKey, signature: Uint8Array, data: Uint8Array): Promise<Result<boolean>>;\n\n /**\n * Compares two byte arrays in constant time.\n *\n * The comparison visits all bytes of `a` and `b` regardless of where they\n * diverge, accumulating XOR differences with bitwise-OR. No early-return is\n * possible once the length check passes, making timing independent of the\n * byte values. This prevents timing side-channels when comparing MAC outputs,\n * signed-token bytes, or any secret-derived byte sequences.\n *\n * Returns `false` immediately (before the loop) when `a.length !== b.length`;\n * the length mismatch itself is not secret in normal use.\n * @param a - First byte array.\n * @param b - Second byte array.\n * @returns `true` if the arrays have the same length and identical contents,\n * `false` otherwise.\n */\n timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;\n\n /**\n * Computes an HMAC-SHA256 authentication code for `data` using `key`.\n *\n * The key must be a `CryptoKey` with `'sign'` usage and algorithm name\n * `'HMAC'` (e.g. derived via PBKDF2 or imported with\n * `crypto.subtle.importKey`). Use {@link ICryptoProvider.verifyHmacSha256}\n * for constant-time verification of the output.\n * @param key - An HMAC `CryptoKey` with `'sign'` usage.\n * @param data - The bytes to authenticate.\n * @returns `Success` with the 32-byte MAC, or `Failure` with error context.\n */\n hmacSha256(key: CryptoKey, data: Uint8Array): Promise<Result<Uint8Array>>;\n\n /**\n * Verifies an HMAC-SHA256 authentication code in constant time.\n *\n * Computes the expected MAC over `data` with `key`, then compares it to\n * `signature` using {@link ICryptoProvider.timingSafeEqual} so that\n * mismatches do not leak information through timing.\n * @param key - An HMAC `CryptoKey` with `'sign'` usage.\n * @param signature - The MAC bytes to verify (typically 32 bytes).\n * @param data - The original data that was authenticated.\n * @returns `Success` with `true` if the MAC is valid, `false` if it is not,\n * or `Failure` with error context if the MAC computation itself failed.\n */\n verifyHmacSha256(key: CryptoKey, signature: Uint8Array, data: Uint8Array): Promise<Result<boolean>>;\n}\n\n// ============================================================================\n// Encryption Provider Interface\n// ============================================================================\n\n/**\n * High-level interface for encrypting JSON content by secret name.\n *\n * This abstraction unifies two common encryption workflows:\n * - **KeyStore**: looks up the named secret and crypto provider from the vault\n * - **DirectEncryptionProvider**: uses a pre-supplied key and crypto provider,\n * optionally bound to a specific secret name for safety\n *\n * Callers that need to encrypt (e.g. `EditableCollection.save()`) depend on\n * this interface rather than on `KeyStore` directly, allowing mix-and-match.\n *\n * @public\n */\nexport interface IEncryptionProvider {\n /**\n * Encrypts JSON content under a named secret.\n *\n * @param secretName - Name of the secret to encrypt with\n * @param content - JSON-safe content to encrypt\n * @param metadata - Optional unencrypted metadata to include in the encrypted file\n * @returns Success with encrypted file structure, or Failure with error context\n */\n encryptByName<TMetadata = JsonValue>(\n secretName: string,\n content: JsonValue,\n metadata?: TMetadata\n ): Promise<Result<IEncryptedFile<TMetadata>>>;\n}\n\n// ============================================================================\n// Encryption Configuration\n// ============================================================================\n\n/**\n * Behavior when an encrypted file cannot be decrypted.\n * @public\n */\nexport type EncryptedFileErrorMode =\n | 'fail' // Return failure, abort loading\n | 'skip' // Skip file silently, continue loading others\n | 'warn'; // Log warning, skip file, continue loading\n\n/**\n * Function type for dynamic secret retrieval.\n * @public\n */\nexport type SecretProvider = (secretName: string) => Promise<Result<Uint8Array>>;\n\n/**\n * Configuration for encrypted file handling during loading.\n * @public\n */\nexport interface IEncryptionConfig {\n /**\n * Named secrets available for decryption.\n */\n readonly secrets?: ReadonlyArray<INamedSecret>;\n\n /**\n * Alternative: dynamic secret provider function.\n * Called when a secret is not found in the secrets array.\n */\n readonly secretProvider?: SecretProvider;\n\n /**\n * Crypto provider implementation (Node.js or browser).\n */\n readonly cryptoProvider: ICryptoProvider;\n\n /**\n * Behavior when decryption key is missing (default: 'fail').\n */\n readonly onMissingKey?: EncryptedFileErrorMode;\n\n /**\n * Behavior when decryption fails (default: 'fail').\n */\n readonly onDecryptionError?: EncryptedFileErrorMode;\n}\n\n// ============================================================================\n// Detection Helper\n// ============================================================================\n\n/**\n * Checks if a JSON object appears to be an encrypted file.\n * Uses the format field as a discriminator.\n * @param json - JSON object to check\n * @returns true if the object has the encrypted file format field\n * @public\n */\nexport function isEncryptedFile(json: unknown): boolean {\n if (typeof json !== 'object' || json === null) {\n return false;\n }\n const obj = json as Record<string, unknown>;\n return obj.format === Constants.ENCRYPTED_FILE_FORMAT;\n}\n"]}
1
+ {"version":3,"file":"model.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/model.ts"],"names":[],"mappings":";AAAA,kCAAkC;AAClC,EAAE;AACF,+EAA+E;AAC/E,gFAAgF;AAChF,+EAA+E;AAC/E,4EAA4E;AAC5E,wEAAwE;AACxE,2DAA2D;AAC3D,EAAE;AACF,iFAAiF;AACjF,kDAAkD;AAClD,EAAE;AACF,6EAA6E;AAC7E,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,gFAAgF;AAChF,gFAAgF;AAChF,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+sBZ,0CAMC;AAhtBD,uDAAyC;AAChC,8BAAS;AAmJlB;;;GAGG;AACU,QAAA,oBAAoB,GAAoC;IACnE,YAAY;IACZ,eAAe;IACf,WAAW;IACX,SAAS;IACT,QAAQ;CACT,CAAC;AAqFF;;;;GAIG;AACU,QAAA,kBAAkB,GAAoB;IACjD,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,EAAE;CACP,CAAC;AAEX;;;GAGG;AACU,QAAA,mBAAmB,GAAoB;IAClD,SAAS,EAAE,KAAK;IAChB,UAAU,EAAE,CAAC;IACb,WAAW,EAAE,CAAC;IACd,WAAW,EAAE,EAAE;CACP,CAAC;AAubX,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;GAMG;AACH,SAAgB,eAAe,CAAC,IAAa;IAC3C,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,GAAG,GAAG,IAA+B,CAAC;IAC5C,OAAO,GAAG,CAAC,MAAM,KAAK,SAAS,CAAC,qBAAqB,CAAC;AACxD,CAAC","sourcesContent":["// Copyright (c) 2024 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { JsonValue } from '@fgv/ts-json-base';\nimport { Brand, Result, Uuid } from '@fgv/ts-utils';\n\nimport * as Constants from './constants';\nexport { Constants };\n\n// ============================================================================\n// Public Key Types\n// ============================================================================\n\n/**\n * A multibase base64url-encoded SPKI (SubjectPublicKeyInfo) public key string —\n * a `'m'` multibase prefix followed by a base64url-no-pad body. Produced by\n * {@link CryptoUtils.exportPublicKeyAsMultibaseSpki} and consumed by\n * {@link CryptoUtils.importPublicKeyFromMultibaseSpki}. Obtain the brand at a\n * boundary via {@link CryptoUtils.isValidMultibaseSpkiPublicKey} or the\n * {@link CryptoUtils.Converters.multibaseSpkiPublicKey} converter.\n * @public\n */\nexport type MultibaseSpkiPublicKey = Brand<string, 'MultibaseSpkiPublicKey'>;\n\n// ============================================================================\n// Encryption Types\n// ============================================================================\n\n/**\n * Supported encryption algorithms.\n * @public\n */\nexport type EncryptionAlgorithm = typeof Constants.DEFAULT_ALGORITHM;\n\n/**\n * Format version for encrypted files.\n * @public\n */\nexport type EncryptedFileFormat = typeof Constants.ENCRYPTED_FILE_FORMAT;\n\n/**\n * Named secret for encryption/decryption.\n * @public\n */\nexport interface INamedSecret {\n /**\n * Unique name for this secret (referenced in encrypted files).\n */\n readonly name: string;\n\n /**\n * The actual secret key (32 bytes for AES-256).\n */\n readonly key: Uint8Array;\n}\n\n/**\n * Result of an encryption operation.\n * @public\n */\nexport interface IEncryptionResult {\n /**\n * Initialization vector used for encryption (12 bytes for GCM).\n */\n readonly iv: Uint8Array;\n\n /**\n * Authentication tag from GCM mode (16 bytes).\n */\n readonly authTag: Uint8Array;\n\n /**\n * The encrypted data.\n */\n readonly encryptedData: Uint8Array;\n}\n\n/**\n * Asymmetric keypair algorithms supported by the crypto provider.\n * - `'ecdsa-p256'`: ECDSA over the P-256 curve, for signing.\n * - `'rsa-oaep-2048'`: RSA-OAEP, 2048-bit modulus with SHA-256, for encryption.\n * - `'ecdh-p256'`: ECDH over the P-256 curve, for key agreement\n * (e.g. as the recipient keypair in\n * {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes} /\n * {@link CryptoUtils.ICryptoProvider.unwrapBytes | unwrapBytes}).\n * - `'ed25519'`: EdDSA over the Edwards25519 curve, for signing.\n * Deterministic — the per-signature nonce is derived from the private key\n * and message rather than sampled randomly, eliminating the random-nonce\n * reuse risk that ECDSA carries. Distinct from X25519 (key agreement over\n * the Montgomery form, Curve25519).\n * - `'x25519'`: Diffie-Hellman key agreement over the Montgomery form of\n * Curve25519. Key-agreement only — use `deriveBits`/`deriveKey` to produce\n * a shared secret from one party's private key and the peer's public key.\n * Distinct from Ed25519 (which uses the twisted-Edwards form for signing).\n * @public\n */\nexport type KeyPairAlgorithm = 'ecdsa-p256' | 'rsa-oaep-2048' | 'ecdh-p256' | 'ed25519' | 'x25519';\n\n/**\n * Caller-supplied HKDF parameters that domain-separate one\n * {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes} call from another.\n * Two wraps that share recipient but differ on `salt` or `info` derive distinct\n * wrap keys, so callers should pick values that bind the wrap to its\n * application context (e.g. a content hash for `salt` and a secret name for\n * `info`).\n *\n * Both fields are required; pass an empty `Uint8Array` if the caller has no\n * value to bind on a given axis. Silent defaulting would hide protocol\n * mistakes, so the API does not pick defaults.\n * @public\n */\nexport interface IWrapBytesOptions {\n /**\n * HKDF salt. Domain-separates this wrap from others in different contexts.\n * Caller picks; common choices include a content hash, document id, channel\n * id, etc.\n */\n readonly salt: Uint8Array;\n\n /**\n * HKDF info. Further binds the derived key to a specific use within the\n * calling application. Caller picks; common choices include a secret name,\n * message type, or version tag.\n */\n readonly info: Uint8Array;\n}\n\n/**\n * Output of {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes}. The\n * shape is JSON-serializable so it can travel directly over the wire or be\n * persisted as-is.\n * @public\n */\nexport interface IWrappedBytes {\n /**\n * Sender's ephemeral ECDH P-256 public key as a JSON Web Key. The matching\n * ephemeral private key is dropped after the shared-secret derive.\n */\n readonly ephemeralPublicKey: JsonWebKey;\n\n /**\n * AES-GCM nonce, base64-encoded. 12 bytes (96 bits) — the standard AES-GCM\n * nonce length.\n */\n readonly nonce: string;\n\n /**\n * AES-GCM ciphertext concatenated with the 16-byte authentication tag,\n * base64-encoded. Tampering with either the nonce or the ciphertext causes\n * unwrap to fail GCM authentication.\n */\n readonly ciphertext: string;\n}\n\n/**\n * All valid key pair algorithms.\n * @public\n */\nexport const allKeyPairAlgorithms: ReadonlyArray<KeyPairAlgorithm> = [\n 'ecdsa-p256',\n 'rsa-oaep-2048',\n 'ecdh-p256',\n 'ed25519',\n 'x25519'\n];\n\n/**\n * Supported key derivation functions.\n * @public\n */\nexport type KeyDerivationFunction = 'pbkdf2' | 'argon2id';\n\n/**\n * PBKDF2 key derivation parameters.\n * @public\n */\nexport interface IPbkdf2KeyDerivationParams {\n /** Key derivation function discriminator. */\n readonly kdf: 'pbkdf2';\n /** Base64-encoded salt used for key derivation. */\n readonly salt: string;\n /** Number of iterations used for key derivation. */\n readonly iterations: number;\n}\n\n/**\n * Argon2id key derivation parameters (RFC 9106).\n * @public\n */\nexport interface IArgon2idKeyDerivationParams {\n /** Key derivation function discriminator. */\n readonly kdf: 'argon2id';\n /** Base64-encoded salt used for key derivation. */\n readonly salt: string;\n /** Memory cost in kibibytes. */\n readonly memoryKiB: number;\n /** Number of passes (time cost). */\n readonly iterations: number;\n /** Degree of parallelism. */\n readonly parallelism: number;\n}\n\n/**\n * Key derivation parameters stored in encrypted files.\n * Discriminated union on `kdf` field: `'pbkdf2'` or `'argon2id'`.\n * @public\n */\nexport type IKeyDerivationParams = IPbkdf2KeyDerivationParams | IArgon2idKeyDerivationParams;\n\n// ============================================================================\n// Argon2id Types\n// ============================================================================\n\n/**\n * Parameters for Argon2id key derivation (RFC 9106).\n * All fields are required; fgv does not pick defaults silently.\n * @public\n */\nexport interface IArgon2idParams {\n /**\n * Memory cost in kibibytes (KiB).\n * OWASP 2023 minimum: 19456 (19 MiB). Stronger: 65536 (64 MiB).\n * Constraint: \\>= 8.\n */\n readonly memoryKiB: number;\n\n /**\n * Number of passes (iterations / time cost).\n * OWASP 2023 minimum: 2. Range: \\>= 1.\n */\n readonly iterations: number;\n\n /**\n * Degree of parallelism (threads).\n * Note: WASM-based implementations compute sequentially regardless of this value,\n * but the value is wired into the algorithm and AFFECTS the output hash bytes.\n * Callers must use the same parallelism value consistently for a given secret.\n * Range: 1–255.\n */\n readonly parallelism: number;\n\n /**\n * Number of output bytes (hash length).\n * Typical values: 16 (128-bit), 32 (256-bit, AES-256 key), 64 (512-bit).\n * Constraint: \\>= 4.\n */\n readonly outputBytes: number;\n}\n\n/**\n * Recommended OWASP 2023 minimum Argon2id parameters.\n * Suitable for recovery-row key derivation (high-entropy inputs).\n * @public\n */\nexport const ARGON2ID_OWASP_MIN: IArgon2idParams = {\n memoryKiB: 19456,\n iterations: 2,\n parallelism: 1,\n outputBytes: 32\n} as const;\n\n/**\n * Stronger Argon2id parameters suitable for user-typed passphrases.\n * @public\n */\nexport const ARGON2ID_PASSPHRASE: IArgon2idParams = {\n memoryKiB: 65536,\n iterations: 3,\n parallelism: 1,\n outputBytes: 32\n} as const;\n\n/**\n * Argon2id key derivation provider (RFC 9106).\n *\n * Implementations are in separate packages to avoid WASM bundle costs for\n * consumers who don't need Argon2id:\n * - Node: `@fgv/ts-extras-argon2` (`NodeArgon2Provider`)\n * - Browser: `@fgv/ts-web-extras-argon2` (`BrowserArgon2Provider`)\n *\n * @public\n */\nexport interface IArgon2idProvider {\n /**\n * Derives key material from a password using Argon2id (RFC 9106 §3.1).\n *\n * Returns the raw derived bytes as a `Uint8Array`. Both Node and browser\n * implementations produce bit-identical output for identical inputs.\n *\n * @param password - Password or passphrase. Accepts string (UTF-8) or raw bytes.\n * @param salt - Salt bytes. Must be random and unique per credential (\\>= 16 bytes recommended).\n * @param params - Argon2id parameters. Use `ARGON2ID_OWASP_MIN` as a starting point.\n * @returns Success with derived bytes, Failure with error context.\n */\n argon2id(\n password: Uint8Array | string,\n salt: Uint8Array,\n params: IArgon2idParams\n ): Promise<Result<Uint8Array>>;\n}\n\n/**\n * Generic encrypted file format.\n * This is the JSON structure stored in encrypted files.\n * @typeParam TMetadata - Type of optional unencrypted metadata\n * @public\n */\nexport interface IEncryptedFile<TMetadata = JsonValue> {\n /**\n * Format identifier for versioning.\n */\n readonly format: EncryptedFileFormat;\n\n /**\n * Name of the secret required to decrypt (references INamedSecret.name).\n */\n readonly secretName: string;\n\n /**\n * Algorithm used for encryption.\n */\n readonly algorithm: EncryptionAlgorithm;\n\n /**\n * Base64-encoded initialization vector.\n */\n readonly iv: string;\n\n /**\n * Base64-encoded authentication tag (for GCM mode).\n */\n readonly authTag: string;\n\n /**\n * Base64-encoded encrypted data (JSON string when decrypted).\n */\n readonly encryptedData: string;\n\n /**\n * Optional unencrypted metadata for display/filtering.\n */\n readonly metadata?: TMetadata;\n\n /**\n * Optional key derivation parameters.\n * If present, allows decryption using a password with these parameters.\n * If absent, a pre-derived key must be provided.\n */\n readonly keyDerivation?: IKeyDerivationParams;\n}\n\n// ============================================================================\n// Crypto Provider Interface\n// ============================================================================\n\n/**\n * Crypto provider interface for cross-platform encryption.\n * Implementations provided for Node.js (crypto module) and browser (Web Crypto API).\n * @public\n */\nexport interface ICryptoProvider {\n /**\n * Encrypts plaintext using AES-256-GCM.\n * @param plaintext - UTF-8 string to encrypt\n * @param key - 32-byte encryption key\n * @returns Success with encryption result, or Failure with error\n */\n encrypt(plaintext: string, key: Uint8Array): Promise<Result<IEncryptionResult>>;\n\n /**\n * Decrypts ciphertext using AES-256-GCM.\n * @param encryptedData - Encrypted bytes\n * @param key - 32-byte decryption key\n * @param iv - Initialization vector (12 bytes)\n * @param authTag - GCM authentication tag (16 bytes)\n * @returns Success with decrypted UTF-8 string, or Failure with error\n */\n decrypt(\n encryptedData: Uint8Array,\n key: Uint8Array,\n iv: Uint8Array,\n authTag: Uint8Array\n ): Promise<Result<string>>;\n\n /**\n * Generates a random 32-byte key suitable for AES-256.\n * @returns Success with generated key, or Failure with error\n */\n generateKey(): Promise<Result<Uint8Array>>;\n\n /**\n * Derives a key from a password using PBKDF2.\n * @param password - Password string\n * @param salt - Salt bytes (should be at least 16 bytes)\n * @param iterations - Number of iterations (recommend 100000+)\n * @returns Success with derived 32-byte key, or Failure with error\n */\n deriveKey(password: string, salt: Uint8Array, iterations: number): Promise<Result<Uint8Array>>;\n\n /**\n * Computes a SHA-256 hash of the given data.\n * @param data - UTF-8 string to hash\n * @returns Success with hex-encoded hash string, or Failure with error\n */\n sha256(data: string): Promise<Result<string>>;\n\n // ============================================================================\n // Platform Utility Methods\n // ============================================================================\n\n /**\n * Generates cryptographically secure random bytes.\n * @param length - Number of bytes to generate\n * @returns Success with random bytes, or Failure with error\n */\n generateRandomBytes(length: number): Result<Uint8Array>;\n\n /**\n * Generates a cryptographically random UUIDv4 using the provider's\n * underlying source of randomness. The default Node and browser\n * implementations delegate to `globalThis.crypto.randomUUID`;\n * deterministic providers (e.g. test stubs) may override to produce\n * reproducible values.\n * @returns Success with a canonical UUID, or Failure with error.\n */\n generateUuid(): Result<Uuid>;\n\n /**\n * Encodes binary data to base64 string.\n * @param data - Binary data to encode\n * @returns Base64-encoded string\n */\n toBase64(data: Uint8Array): string;\n\n /**\n * Decodes base64 string to binary data.\n * @param base64 - Base64-encoded string\n * @returns Success with decoded bytes, or Failure if invalid base64\n */\n fromBase64(base64: string): Result<Uint8Array>;\n\n // ============================================================================\n // Asymmetric Key Operations\n // ============================================================================\n\n /**\n * Generates a new asymmetric keypair for the requested algorithm.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} to use.\n * @param extractable - Whether the resulting `CryptoKey` objects may be exported.\n * Set `false` on backends that store `CryptoKey` references directly (e.g.\n * IndexedDB). Set `true` when the private key must round-trip through JWK or\n * PKCS#8 (e.g. encrypted-file backends).\n * @returns Success with the generated `CryptoKeyPair`, or Failure with error context.\n */\n generateKeyPair(algorithm: KeyPairAlgorithm, extractable: boolean): Promise<Result<CryptoKeyPair>>;\n\n /**\n * Exports the public half of a keypair as a JSON Web Key.\n * @param publicKey - The public `CryptoKey` to export. Must be an `extractable`\n * key generated for an asymmetric algorithm.\n * @returns Success with the JWK, or Failure with error context.\n */\n exportPublicKeyJwk(publicKey: CryptoKey): Promise<Result<JsonWebKey>>;\n\n /**\n * Re-imports a public-key JWK as a `CryptoKey` usable for verification or\n * encryption (depending on algorithm).\n * @param jwk - The JSON Web Key produced by {@link CryptoUtils.ICryptoProvider.exportPublicKeyJwk | exportPublicKeyJwk}.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} the\n * key was generated for. Determines the import parameters and key usages.\n * @returns Success with the imported public `CryptoKey`, or Failure with error context.\n */\n importPublicKeyJwk(jwk: JsonWebKey, algorithm: KeyPairAlgorithm): Promise<Result<CryptoKey>>;\n\n /**\n * Exports a public `CryptoKey` as a DER-encoded SPKI (SubjectPublicKeyInfo) blob.\n * SPKI is the standard algorithm-agnostic format for public key storage and transport.\n * @param publicKey - The `CryptoKey` to export. Must have `key.type === 'public'`.\n * @returns `Success` with the raw SPKI bytes, or `Failure` with error context.\n */\n exportPublicKeySpki(publicKey: CryptoKey): Promise<Result<Uint8Array>>;\n\n /**\n * Imports a public key from a DER-encoded SPKI blob.\n * @param spkiBytes - The raw SPKI bytes produced by {@link CryptoUtils.ICryptoProvider.exportPublicKeySpki | exportPublicKeySpki}.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm | algorithm} the key was generated for.\n * @returns `Success` with the imported public `CryptoKey`, or `Failure` with error context.\n */\n importPublicKeySpki(spkiBytes: Uint8Array, algorithm: KeyPairAlgorithm): Promise<Result<CryptoKey>>;\n\n /**\n * Wraps `plaintext` for delivery to the holder of the private key paired\n * with `recipientPublicKey`. Uses ECIES with ECDH P-256, HKDF-SHA256, and\n * AES-GCM-256.\n *\n * Generates a fresh ephemeral keypair per call; the ephemeral private key\n * is discarded after the shared-secret derive. Only the recipient (with the\n * matching private key) and the same HKDF parameters can recover\n * `plaintext`.\n *\n * Empty `plaintext` is permitted; the resulting wrap contains only the\n * 16-byte GCM authentication tag and round-trips back to an empty\n * `Uint8Array`.\n * @param plaintext - The bytes to wrap. Any length supported by AES-GCM\n * (in practice, well below 2^39 - 256 bits).\n * @param recipientPublicKey - The recipient's ECDH P-256 public `CryptoKey`.\n * Must have algorithm name `'ECDH'` and named curve `'P-256'`; mismatched\n * algorithm or curve yields a `Failure` with error context.\n * @param options - HKDF parameters; see {@link CryptoUtils.IWrapBytesOptions | IWrapBytesOptions}.\n * @returns `Success` with the wrapped payload, or `Failure` with error context.\n */\n wrapBytes(\n plaintext: Uint8Array,\n recipientPublicKey: CryptoKey,\n options: IWrapBytesOptions\n ): Promise<Result<IWrappedBytes>>;\n\n /**\n * Inverse of {@link CryptoUtils.ICryptoProvider.wrapBytes | wrapBytes}.\n * Recovers the original `plaintext` from a wrapped payload using the\n * recipient's private key.\n *\n * Returns a `Failure` (never throws) on any of:\n * - Tampered nonce or ciphertext (AES-GCM authentication fails)\n * - Wrong private key (different shared secret derives a different wrap key)\n * - Wrong HKDF parameters (different wrap key)\n * - Malformed `ephemeralPublicKey` JWK\n * - Malformed base64 in `nonce` or `ciphertext`\n * @param wrapped - The wrapped payload produced by `wrapBytes`.\n * @param recipientPrivateKey - The recipient's ECDH P-256 private\n * `CryptoKey`. Must have algorithm name `'ECDH'` and named curve `'P-256'`,\n * and key usages including `'deriveKey'` or `'deriveBits'`.\n * @param options - The same HKDF parameters used at wrap time.\n * @returns `Success` with the original `plaintext`, or `Failure` with error context.\n */\n unwrapBytes(\n wrapped: IWrappedBytes,\n recipientPrivateKey: CryptoKey,\n options: IWrapBytesOptions\n ): Promise<Result<Uint8Array>>;\n\n // ============================================================================\n // Signing Operations\n // ============================================================================\n\n /**\n * Signs `data` with `privateKey` using the algorithm inferred from the key.\n * Delegates to `crypto.subtle.sign`; the algorithm is derived from\n * `privateKey.algorithm.name` — ECDSA keys are augmented with\n * `hash: 'SHA-256'` at sign time (the hash is not stored in the key);\n * all other algorithm names are passed through as-is.\n * Intended for Ed25519 and ECDSA-P256 asymmetric private keys; for\n * HMAC-SHA256 authentication codes use {@link ICryptoProvider.hmacSha256} instead.\n * @param privateKey - A `CryptoKey` with `'sign'` usage (e.g. generated by\n * {@link CryptoUtils.ICryptoProvider.generateKeyPair | generateKeyPair} with\n * `'ecdsa-p256'` or `'ed25519'`).\n * @param data - The bytes to sign.\n * @returns `Success` with the raw signature bytes, or `Failure` with error context.\n */\n sign(privateKey: CryptoKey, data: Uint8Array): Promise<Result<Uint8Array>>;\n\n /**\n * Verifies a signature produced by {@link ICryptoProvider.sign}.\n * Delegates to `crypto.subtle.verify`; the algorithm is derived from\n * `publicKey.algorithm.name` — ECDSA keys are augmented with\n * `hash: 'SHA-256'`; all other algorithm names are passed through as-is.\n * Intended for Ed25519 and ECDSA-P256 asymmetric public keys; for\n * HMAC-SHA256 verification use {@link ICryptoProvider.verifyHmacSha256} instead.\n * @param publicKey - A `CryptoKey` with `'verify'` usage (e.g. the public\n * half of a keypair generated by\n * {@link CryptoUtils.ICryptoProvider.generateKeyPair | generateKeyPair} with\n * `'ecdsa-p256'` or `'ed25519'`).\n * @param signature - The raw signature bytes produced by `sign`.\n * @param data - The original data that was signed.\n * @returns `Success` with `true` if the signature is valid, `false` if it is\n * not, or `Failure` with error context if the operation itself failed.\n */\n verify(publicKey: CryptoKey, signature: Uint8Array, data: Uint8Array): Promise<Result<boolean>>;\n\n /**\n * Compares two byte arrays in constant time.\n *\n * The comparison visits all bytes of `a` and `b` regardless of where they\n * diverge, accumulating XOR differences with bitwise-OR. No early-return is\n * possible once the length check passes, making timing independent of the\n * byte values. This prevents timing side-channels when comparing MAC outputs,\n * signed-token bytes, or any secret-derived byte sequences.\n *\n * Returns `false` immediately (before the loop) when `a.length !== b.length`;\n * the length mismatch itself is not secret in normal use.\n * @param a - First byte array.\n * @param b - Second byte array.\n * @returns `true` if the arrays have the same length and identical contents,\n * `false` otherwise.\n */\n timingSafeEqual(a: Uint8Array, b: Uint8Array): boolean;\n\n /**\n * Computes an HMAC-SHA256 authentication code for `data` using `key`.\n *\n * The key must be a `CryptoKey` with `'sign'` usage and algorithm name\n * `'HMAC'` (e.g. derived via PBKDF2 or imported with\n * `crypto.subtle.importKey`). Use {@link ICryptoProvider.verifyHmacSha256}\n * for constant-time verification of the output.\n * @param key - An HMAC `CryptoKey` with `'sign'` usage.\n * @param data - The bytes to authenticate.\n * @returns `Success` with the 32-byte MAC, or `Failure` with error context.\n */\n hmacSha256(key: CryptoKey, data: Uint8Array): Promise<Result<Uint8Array>>;\n\n /**\n * Verifies an HMAC-SHA256 authentication code in constant time.\n *\n * Computes the expected MAC over `data` with `key`, then compares it to\n * `signature` using {@link ICryptoProvider.timingSafeEqual} so that\n * mismatches do not leak information through timing.\n * @param key - An HMAC `CryptoKey` with `'sign'` usage.\n * @param signature - The MAC bytes to verify (typically 32 bytes).\n * @param data - The original data that was authenticated.\n * @returns `Success` with `true` if the MAC is valid, `false` if it is not,\n * or `Failure` with error context if the MAC computation itself failed.\n */\n verifyHmacSha256(key: CryptoKey, signature: Uint8Array, data: Uint8Array): Promise<Result<boolean>>;\n}\n\n// ============================================================================\n// Encryption Provider Interface\n// ============================================================================\n\n/**\n * High-level interface for encrypting JSON content by secret name.\n *\n * This abstraction unifies two common encryption workflows:\n * - **KeyStore**: looks up the named secret and crypto provider from the vault\n * - **DirectEncryptionProvider**: uses a pre-supplied key and crypto provider,\n * optionally bound to a specific secret name for safety\n *\n * Callers that need to encrypt (e.g. `EditableCollection.save()`) depend on\n * this interface rather than on `KeyStore` directly, allowing mix-and-match.\n *\n * @public\n */\nexport interface IEncryptionProvider {\n /**\n * Encrypts JSON content under a named secret.\n *\n * @param secretName - Name of the secret to encrypt with\n * @param content - JSON-safe content to encrypt\n * @param metadata - Optional unencrypted metadata to include in the encrypted file\n * @returns Success with encrypted file structure, or Failure with error context\n */\n encryptByName<TMetadata = JsonValue>(\n secretName: string,\n content: JsonValue,\n metadata?: TMetadata\n ): Promise<Result<IEncryptedFile<TMetadata>>>;\n}\n\n// ============================================================================\n// Encryption Configuration\n// ============================================================================\n\n/**\n * Behavior when an encrypted file cannot be decrypted.\n * @public\n */\nexport type EncryptedFileErrorMode =\n | 'fail' // Return failure, abort loading\n | 'skip' // Skip file silently, continue loading others\n | 'warn'; // Log warning, skip file, continue loading\n\n/**\n * Function type for dynamic secret retrieval.\n * @public\n */\nexport type SecretProvider = (secretName: string) => Promise<Result<Uint8Array>>;\n\n/**\n * Configuration for encrypted file handling during loading.\n * @public\n */\nexport interface IEncryptionConfig {\n /**\n * Named secrets available for decryption.\n */\n readonly secrets?: ReadonlyArray<INamedSecret>;\n\n /**\n * Alternative: dynamic secret provider function.\n * Called when a secret is not found in the secrets array.\n */\n readonly secretProvider?: SecretProvider;\n\n /**\n * Crypto provider implementation (Node.js or browser).\n */\n readonly cryptoProvider: ICryptoProvider;\n\n /**\n * Behavior when decryption key is missing (default: 'fail').\n */\n readonly onMissingKey?: EncryptedFileErrorMode;\n\n /**\n * Behavior when decryption fails (default: 'fail').\n */\n readonly onDecryptionError?: EncryptedFileErrorMode;\n}\n\n// ============================================================================\n// Detection Helper\n// ============================================================================\n\n/**\n * Checks if a JSON object appears to be an encrypted file.\n * Uses the format field as a discriminator.\n * @param json - JSON object to check\n * @returns true if the object has the encrypted file format field\n * @public\n */\nexport function isEncryptedFile(json: unknown): boolean {\n if (typeof json !== 'object' || json === null) {\n return false;\n }\n const obj = json as Record<string, unknown>;\n return obj.format === Constants.ENCRYPTED_FILE_FORMAT;\n}\n"]}
@@ -1,5 +1,58 @@
1
1
  import { Result } from '@fgv/ts-utils';
2
- import { ICryptoProvider, KeyPairAlgorithm } from './model';
2
+ import { ICryptoProvider, KeyPairAlgorithm, MultibaseSpkiPublicKey } from './model';
3
+ /**
4
+ * Encodes a `Uint8Array` as a base64url (no-padding) string (RFC 4648 §5).
5
+ *
6
+ * The body uses the base64url alphabet (`+` → `-`, `/` → `_`) and trailing `=`
7
+ * padding is stripped. This is the bare primitive with no multibase prefix; use
8
+ * {@link CryptoUtils.multibaseBase64UrlEncode} when a multibase-`'m'`-prefixed value is required.
9
+ *
10
+ * @param data - The binary data to encode.
11
+ * @returns The base64url-no-pad string.
12
+ * @public
13
+ */
14
+ export declare function base64UrlNoPadEncode(data: Uint8Array): string;
15
+ /**
16
+ * Decodes a base64url (no-padding) string (RFC 4648 §5) back to a `Uint8Array`.
17
+ *
18
+ * This is the bare primitive with no multibase prefix; use
19
+ * {@link CryptoUtils.multibaseBase64UrlDecode} to decode a multibase-`'m'`-prefixed value.
20
+ *
21
+ * @param encoded - The base64url-no-pad body to decode.
22
+ * @returns `Success` with the decoded bytes, or `Failure` with error context.
23
+ * @public
24
+ */
25
+ export declare function base64UrlNoPadDecode(encoded: string): Result<Uint8Array>;
26
+ /**
27
+ * The structural *shape* of a {@link CryptoUtils.MultibaseSpkiPublicKey}: the
28
+ * multibase `'m'` prefix followed by a non-empty base64url-no-pad body
29
+ * (`A-Z`, `a-z`, `0-9`, `-`, `_`).
30
+ *
31
+ * This is a shape prefilter, not full validation: it does not decode the DER
32
+ * SPKI or verify the key material or algorithm (that happens in
33
+ * {@link CryptoUtils.importPublicKeyFromMultibaseSpki}). The authoritative guard
34
+ * is {@link CryptoUtils.isValidMultibaseSpkiPublicKey}, which enforces this
35
+ * pattern **and** additionally rejects a body whose length is an impossible
36
+ * base64 remainder. Exposed for callers that need the pattern directly (e.g. a
37
+ * JSON-schema `pattern` field); prefer the guard/converter for validation.
38
+ *
39
+ * @public
40
+ */
41
+ export declare const MultibaseSpkiPublicKeyRegExp: RegExp;
42
+ /**
43
+ * Type guard for {@link CryptoUtils.MultibaseSpkiPublicKey}: a string matching
44
+ * {@link CryptoUtils.MultibaseSpkiPublicKeyRegExp} (multibase `'m'` prefix + a
45
+ * non-empty base64url-no-pad body) whose body also satisfies the base64url-no-pad
46
+ * length rule shared with {@link CryptoUtils.base64UrlNoPadDecode}. This is a
47
+ * structural *shape* check, not full validation — it does not decode the DER SPKI
48
+ * or verify the key material/algorithm (a malformed-but-well-shaped string fails
49
+ * later, with clear context, in {@link CryptoUtils.importPublicKeyFromMultibaseSpki}).
50
+ *
51
+ * @param value - The value to test.
52
+ * @returns `true` if `value` is a well-formed multibase SPKI public key string.
53
+ * @public
54
+ */
55
+ export declare function isValidMultibaseSpkiPublicKey(value: unknown): value is MultibaseSpkiPublicKey;
3
56
  /**
4
57
  * Encodes a `Uint8Array` as a multibase base64url (no-padding) string.
5
58
  *
@@ -35,7 +88,7 @@ export declare function multibaseBase64UrlDecode(encoded: string): Result<Uint8A
35
88
  * @returns `Success` with the multibase SPKI string, or `Failure` with error context.
36
89
  * @public
37
90
  */
38
- export declare function exportPublicKeyAsMultibaseSpki(key: CryptoKey, provider: ICryptoProvider): Promise<Result<string>>;
91
+ export declare function exportPublicKeyAsMultibaseSpki(key: CryptoKey, provider: ICryptoProvider): Promise<Result<MultibaseSpkiPublicKey>>;
39
92
  /**
40
93
  * Imports a public key from a multibase base64url-encoded SPKI blob.
41
94
  *
@@ -43,6 +96,10 @@ export declare function exportPublicKeyAsMultibaseSpki(key: CryptoKey, provider:
43
96
  * the provider to import the key with the algorithm parameters from
44
97
  * {@link CryptoUtils.keyPairAlgorithmParams}.
45
98
  *
99
+ * Accepts a plain `string` (not only a branded {@link CryptoUtils.MultibaseSpkiPublicKey}),
100
+ * so callers holding an unbranded value read from storage or the wire can import
101
+ * it directly; a malformed value fails with error context rather than throwing.
102
+ *
46
103
  * @param encoded - A multibase SPKI string produced by {@link CryptoUtils.exportPublicKeyAsMultibaseSpki}.
47
104
  * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm} the key was generated for.
48
105
  * @param provider - The {@link CryptoUtils.ICryptoProvider} to use for the import operation.
@@ -1 +1 @@
1
- {"version":3,"file":"spkiHelpers.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/spkiHelpers.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE5D;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAgBjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAiC5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAIzB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAQ5B"}
1
+ {"version":3,"file":"spkiHelpers.d.ts","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/spkiHelpers.ts"],"names":[],"mappings":"AAoBA,OAAO,EAAE,MAAM,EAAiB,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,SAAS,CAAC;AAYpF;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAe7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAyBxE;AAED;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,4BAA4B,EAAE,MAA4B,CAAC;AAExE;;;;;;;;;;;;GAYG;AACH,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,sBAAsB,CAM7F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAEjE;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAAC,CAe5E;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,CAAC,sBAAsB,CAAC,CAAC,CAQzC;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,gCAAgC,CACpD,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,gBAAgB,EAC3B,QAAQ,EAAE,eAAe,GACxB,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAQ5B"}
@@ -19,23 +19,36 @@
19
19
  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
20
  // SOFTWARE.
21
21
  Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.MultibaseSpkiPublicKeyRegExp = void 0;
23
+ exports.base64UrlNoPadEncode = base64UrlNoPadEncode;
24
+ exports.base64UrlNoPadDecode = base64UrlNoPadDecode;
25
+ exports.isValidMultibaseSpkiPublicKey = isValidMultibaseSpkiPublicKey;
22
26
  exports.multibaseBase64UrlEncode = multibaseBase64UrlEncode;
23
27
  exports.multibaseBase64UrlDecode = multibaseBase64UrlDecode;
24
28
  exports.exportPublicKeyAsMultibaseSpki = exportPublicKeyAsMultibaseSpki;
25
29
  exports.importPublicKeyFromMultibaseSpki = importPublicKeyFromMultibaseSpki;
26
30
  const ts_utils_1 = require("@fgv/ts-utils");
27
31
  /**
28
- * Encodes a `Uint8Array` as a multibase base64url (no-padding) string.
32
+ * Shared shape check for a base64url (no-padding) body: only base64url alphabet
33
+ * characters (`A-Z`, `a-z`, `0-9`, `-`, `_`) and a length that is never `% 4 === 1`
34
+ * (an impossible base64 remainder). Factored so the decoder and the
35
+ * {@link isValidMultibaseSpkiPublicKey} guard agree on exactly one rule.
36
+ */
37
+ function isBase64UrlNoPadBody(body) {
38
+ return /^[A-Za-z0-9_-]*$/.test(body) && body.length % 4 !== 1;
39
+ }
40
+ /**
41
+ * Encodes a `Uint8Array` as a base64url (no-padding) string (RFC 4648 §5).
29
42
  *
30
- * The multibase prefix `'m'` identifies the encoding as RFC 4648 base64url
31
- * without padding. The body uses base64url alphabet: `+` `-`, `/` → `_`,
32
- * and trailing `=` padding is stripped.
43
+ * The body uses the base64url alphabet (`+` `-`, `/` → `_`) and trailing `=`
44
+ * padding is stripped. This is the bare primitive with no multibase prefix; use
45
+ * {@link CryptoUtils.multibaseBase64UrlEncode} when a multibase-`'m'`-prefixed value is required.
33
46
  *
34
47
  * @param data - The binary data to encode.
35
- * @returns A multibase-prefixed base64url string (`'m' + base64url-no-pad`).
48
+ * @returns The base64url-no-pad string.
36
49
  * @public
37
50
  */
38
- function multibaseBase64UrlEncode(data) {
51
+ function base64UrlNoPadEncode(data) {
39
52
  let base64;
40
53
  if (typeof Buffer !== 'undefined') {
41
54
  base64 = Buffer.from(data).toString('base64');
@@ -50,30 +63,24 @@ function multibaseBase64UrlEncode(data) {
50
63
  }
51
64
  /* c8 ignore stop */
52
65
  // Convert to base64url: + → -, / → _, strip = padding
53
- const base64url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
54
- return 'm' + base64url;
66
+ return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');
55
67
  }
56
68
  /**
57
- * Decodes a multibase base64url (no-padding) string back to a `Uint8Array`.
69
+ * Decodes a base64url (no-padding) string (RFC 4648 §5) back to a `Uint8Array`.
58
70
  *
59
- * Validates that the first character is `'m'` (the multibase prefix for
60
- * RFC 4648 base64url without padding), then decodes the remaining body.
71
+ * This is the bare primitive with no multibase prefix; use
72
+ * {@link CryptoUtils.multibaseBase64UrlDecode} to decode a multibase-`'m'`-prefixed value.
61
73
  *
62
- * @param encoded - A multibase-prefixed base64url string.
74
+ * @param encoded - The base64url-no-pad body to decode.
63
75
  * @returns `Success` with the decoded bytes, or `Failure` with error context.
64
76
  * @public
65
77
  */
66
- function multibaseBase64UrlDecode(encoded) {
67
- var _a;
68
- if (!encoded.startsWith('m')) {
69
- return (0, ts_utils_1.fail)(`multibaseBase64UrlDecode: invalid multibase prefix '${(_a = encoded[0]) !== null && _a !== void 0 ? _a : '(empty)'}' — expected 'm' (base64url)`);
70
- }
71
- const body = encoded.slice(1);
72
- if (!/^[A-Za-z0-9_-]*$/.test(body) || body.length % 4 === 1) {
73
- return (0, ts_utils_1.fail)(`multibaseBase64UrlDecode: malformed base64url body`);
78
+ function base64UrlNoPadDecode(encoded) {
79
+ if (!isBase64UrlNoPadBody(encoded)) {
80
+ return (0, ts_utils_1.fail)(`base64UrlNoPadDecode: malformed base64url body`);
74
81
  }
75
82
  // Convert base64url back to standard base64 and restore padding
76
- const base64 = body.replace(/-/g, '+').replace(/_/g, '/');
83
+ const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');
77
84
  const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);
78
85
  try {
79
86
  let bytes;
@@ -90,11 +97,80 @@ function multibaseBase64UrlDecode(encoded) {
90
97
  }
91
98
  /* c8 ignore stop */
92
99
  return (0, ts_utils_1.succeed)(bytes);
93
- /* c8 ignore next 3 - defensive: regex validation above prevents invalid chars from reaching here */
100
+ /* c8 ignore next 3 - defensive: shape check above prevents invalid chars from reaching here */
101
+ }
102
+ catch (_a) {
103
+ return (0, ts_utils_1.fail)(`base64UrlNoPadDecode: malformed base64url body`);
94
104
  }
95
- catch (_b) {
96
- return (0, ts_utils_1.fail)(`multibaseBase64UrlDecode: malformed base64url body`);
105
+ }
106
+ /**
107
+ * The structural *shape* of a {@link CryptoUtils.MultibaseSpkiPublicKey}: the
108
+ * multibase `'m'` prefix followed by a non-empty base64url-no-pad body
109
+ * (`A-Z`, `a-z`, `0-9`, `-`, `_`).
110
+ *
111
+ * This is a shape prefilter, not full validation: it does not decode the DER
112
+ * SPKI or verify the key material or algorithm (that happens in
113
+ * {@link CryptoUtils.importPublicKeyFromMultibaseSpki}). The authoritative guard
114
+ * is {@link CryptoUtils.isValidMultibaseSpkiPublicKey}, which enforces this
115
+ * pattern **and** additionally rejects a body whose length is an impossible
116
+ * base64 remainder. Exposed for callers that need the pattern directly (e.g. a
117
+ * JSON-schema `pattern` field); prefer the guard/converter for validation.
118
+ *
119
+ * @public
120
+ */
121
+ exports.MultibaseSpkiPublicKeyRegExp = /^m[A-Za-z0-9_-]+$/;
122
+ /**
123
+ * Type guard for {@link CryptoUtils.MultibaseSpkiPublicKey}: a string matching
124
+ * {@link CryptoUtils.MultibaseSpkiPublicKeyRegExp} (multibase `'m'` prefix + a
125
+ * non-empty base64url-no-pad body) whose body also satisfies the base64url-no-pad
126
+ * length rule shared with {@link CryptoUtils.base64UrlNoPadDecode}. This is a
127
+ * structural *shape* check, not full validation — it does not decode the DER SPKI
128
+ * or verify the key material/algorithm (a malformed-but-well-shaped string fails
129
+ * later, with clear context, in {@link CryptoUtils.importPublicKeyFromMultibaseSpki}).
130
+ *
131
+ * @param value - The value to test.
132
+ * @returns `true` if `value` is a well-formed multibase SPKI public key string.
133
+ * @public
134
+ */
135
+ function isValidMultibaseSpkiPublicKey(value) {
136
+ return (typeof value === 'string' &&
137
+ exports.MultibaseSpkiPublicKeyRegExp.test(value) &&
138
+ isBase64UrlNoPadBody(value.slice(1)));
139
+ }
140
+ /**
141
+ * Encodes a `Uint8Array` as a multibase base64url (no-padding) string.
142
+ *
143
+ * The multibase prefix `'m'` identifies the encoding as RFC 4648 base64url
144
+ * without padding. The body uses base64url alphabet: `+` → `-`, `/` → `_`,
145
+ * and trailing `=` padding is stripped.
146
+ *
147
+ * @param data - The binary data to encode.
148
+ * @returns A multibase-prefixed base64url string (`'m' + base64url-no-pad`).
149
+ * @public
150
+ */
151
+ function multibaseBase64UrlEncode(data) {
152
+ return 'm' + base64UrlNoPadEncode(data);
153
+ }
154
+ /**
155
+ * Decodes a multibase base64url (no-padding) string back to a `Uint8Array`.
156
+ *
157
+ * Validates that the first character is `'m'` (the multibase prefix for
158
+ * RFC 4648 base64url without padding), then decodes the remaining body.
159
+ *
160
+ * @param encoded - A multibase-prefixed base64url string.
161
+ * @returns `Success` with the decoded bytes, or `Failure` with error context.
162
+ * @public
163
+ */
164
+ function multibaseBase64UrlDecode(encoded) {
165
+ var _a;
166
+ if (!encoded.startsWith('m')) {
167
+ return (0, ts_utils_1.fail)(`multibaseBase64UrlDecode: invalid multibase prefix '${(_a = encoded[0]) !== null && _a !== void 0 ? _a : '(empty)'}' — expected 'm' (base64url)`);
97
168
  }
169
+ // Intentionally pin the exact original message (the delegate has a single failure
170
+ // mode) to keep this established public function byte-for-byte behavior-preserving
171
+ // after the extract-and-delegate refactor — do not "fix" this into composing the
172
+ // delegate's message without updating the delegation-equivalence tests.
173
+ return base64UrlNoPadDecode(encoded.slice(1)).withErrorFormat(() => `multibaseBase64UrlDecode: malformed base64url body`);
98
174
  }
99
175
  /**
100
176
  * Exports a public `CryptoKey` as a multibase base64url-encoded SPKI blob.
@@ -111,7 +187,10 @@ function multibaseBase64UrlDecode(encoded) {
111
187
  async function exportPublicKeyAsMultibaseSpki(key, provider) {
112
188
  return (await provider.exportPublicKeySpki(key))
113
189
  .withErrorFormat((e) => `exportPublicKeyAsMultibaseSpki: ${e}`)
114
- .onSuccess((buf) => (0, ts_utils_1.succeed)(multibaseBase64UrlEncode(buf)));
190
+ .onSuccess((buf) =>
191
+ // The output is a freshly-built valid multibase SPKI string (`'m'` prefix +
192
+ // base64url-no-pad body), so brand it at the construction site — no re-validation needed.
193
+ (0, ts_utils_1.succeed)(multibaseBase64UrlEncode(buf)));
115
194
  }
116
195
  /**
117
196
  * Imports a public key from a multibase base64url-encoded SPKI blob.
@@ -120,6 +199,10 @@ async function exportPublicKeyAsMultibaseSpki(key, provider) {
120
199
  * the provider to import the key with the algorithm parameters from
121
200
  * {@link CryptoUtils.keyPairAlgorithmParams}.
122
201
  *
202
+ * Accepts a plain `string` (not only a branded {@link CryptoUtils.MultibaseSpkiPublicKey}),
203
+ * so callers holding an unbranded value read from storage or the wire can import
204
+ * it directly; a malformed value fails with error context rather than throwing.
205
+ *
123
206
  * @param encoded - A multibase SPKI string produced by {@link CryptoUtils.exportPublicKeyAsMultibaseSpki}.
124
207
  * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm} the key was generated for.
125
208
  * @param provider - The {@link CryptoUtils.ICryptoProvider} to use for the import operation.
@@ -1 +1 @@
1
- {"version":3,"file":"spkiHelpers.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/spkiHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { ICryptoProvider, KeyPairAlgorithm } from './model';\n\n/**\n * Encodes a `Uint8Array` as a multibase base64url (no-padding) string.\n *\n * The multibase prefix `'m'` identifies the encoding as RFC 4648 base64url\n * without padding. The body uses base64url alphabet: `+` → `-`, `/` → `_`,\n * and trailing `=` padding is stripped.\n *\n * @param data - The binary data to encode.\n * @returns A multibase-prefixed base64url string (`'m' + base64url-no-pad`).\n * @public\n */\nexport function multibaseBase64UrlEncode(data: Uint8Array): string {\n let base64: string;\n if (typeof Buffer !== 'undefined') {\n base64 = Buffer.from(data).toString('base64');\n /* c8 ignore start - browser-only: btoa path not available in Node tests */\n } else {\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n base64 = btoa(binary);\n }\n /* c8 ignore stop */\n // Convert to base64url: + → -, / → _, strip = padding\n const base64url = base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n return 'm' + base64url;\n}\n\n/**\n * Decodes a multibase base64url (no-padding) string back to a `Uint8Array`.\n *\n * Validates that the first character is `'m'` (the multibase prefix for\n * RFC 4648 base64url without padding), then decodes the remaining body.\n *\n * @param encoded - A multibase-prefixed base64url string.\n * @returns `Success` with the decoded bytes, or `Failure` with error context.\n * @public\n */\nexport function multibaseBase64UrlDecode(encoded: string): Result<Uint8Array> {\n if (!encoded.startsWith('m')) {\n return fail(\n `multibaseBase64UrlDecode: invalid multibase prefix '${\n encoded[0] ?? '(empty)'\n }' — expected 'm' (base64url)`\n );\n }\n const body = encoded.slice(1);\n if (!/^[A-Za-z0-9_-]*$/.test(body) || body.length % 4 === 1) {\n return fail(`multibaseBase64UrlDecode: malformed base64url body`);\n }\n // Convert base64url back to standard base64 and restore padding\n const base64 = body.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n try {\n let bytes: Uint8Array;\n if (typeof Buffer !== 'undefined') {\n bytes = new Uint8Array(Buffer.from(padded, 'base64'));\n /* c8 ignore start - browser-only: atob path not available in Node tests */\n } else {\n const binary = atob(padded);\n bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n }\n /* c8 ignore stop */\n return succeed(bytes);\n /* c8 ignore next 3 - defensive: regex validation above prevents invalid chars from reaching here */\n } catch {\n return fail(`multibaseBase64UrlDecode: malformed base64url body`);\n }\n}\n\n/**\n * Exports a public `CryptoKey` as a multibase base64url-encoded SPKI blob.\n *\n * The SPKI (SubjectPublicKeyInfo) format is the standard DER-encoded structure\n * for public keys defined in RFC 5280, RFC 5480, and RFC 8410. It is\n * algorithm-agnostic and suitable for storage and transmission.\n *\n * @param key - The `CryptoKey` to export. Must have `key.type === 'public'`.\n * @param provider - The {@link CryptoUtils.ICryptoProvider} to use for the export operation.\n * @returns `Success` with the multibase SPKI string, or `Failure` with error context.\n * @public\n */\nexport async function exportPublicKeyAsMultibaseSpki(\n key: CryptoKey,\n provider: ICryptoProvider\n): Promise<Result<string>> {\n return (await provider.exportPublicKeySpki(key))\n .withErrorFormat((e) => `exportPublicKeyAsMultibaseSpki: ${e}`)\n .onSuccess((buf) => succeed(multibaseBase64UrlEncode(buf)));\n}\n\n/**\n * Imports a public key from a multibase base64url-encoded SPKI blob.\n *\n * Decodes the multibase prefix, decodes the base64url body, then uses\n * the provider to import the key with the algorithm parameters from\n * {@link CryptoUtils.keyPairAlgorithmParams}.\n *\n * @param encoded - A multibase SPKI string produced by {@link CryptoUtils.exportPublicKeyAsMultibaseSpki}.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm} the key was generated for.\n * @param provider - The {@link CryptoUtils.ICryptoProvider} to use for the import operation.\n * @returns `Success` with the imported public `CryptoKey`, or `Failure` with error context.\n * @public\n */\nexport async function importPublicKeyFromMultibaseSpki(\n encoded: string,\n algorithm: KeyPairAlgorithm,\n provider: ICryptoProvider\n): Promise<Result<CryptoKey>> {\n const decodeResult = multibaseBase64UrlDecode(encoded);\n if (decodeResult.isFailure()) {\n return fail(`importPublicKeyFromMultibaseSpki: ${decodeResult.message}`);\n }\n return (await provider.importPublicKeySpki(decodeResult.value, algorithm)).withErrorFormat(\n (e) => `importPublicKeyFromMultibaseSpki: ${e}`\n );\n}\n"]}
1
+ {"version":3,"file":"spkiHelpers.js","sourceRoot":"","sources":["../../../src/packlets/crypto-utils/spkiHelpers.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) 2026 Erik Fortune\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\nimport { Result, fail, succeed } from '@fgv/ts-utils';\nimport { ICryptoProvider, KeyPairAlgorithm, MultibaseSpkiPublicKey } from './model';\n\n/**\n * Shared shape check for a base64url (no-padding) body: only base64url alphabet\n * characters (`A-Z`, `a-z`, `0-9`, `-`, `_`) and a length that is never `% 4 === 1`\n * (an impossible base64 remainder). Factored so the decoder and the\n * {@link isValidMultibaseSpkiPublicKey} guard agree on exactly one rule.\n */\nfunction isBase64UrlNoPadBody(body: string): boolean {\n return /^[A-Za-z0-9_-]*$/.test(body) && body.length % 4 !== 1;\n}\n\n/**\n * Encodes a `Uint8Array` as a base64url (no-padding) string (RFC 4648 §5).\n *\n * The body uses the base64url alphabet (`+` → `-`, `/` → `_`) and trailing `=`\n * padding is stripped. This is the bare primitive with no multibase prefix; use\n * {@link CryptoUtils.multibaseBase64UrlEncode} when a multibase-`'m'`-prefixed value is required.\n *\n * @param data - The binary data to encode.\n * @returns The base64url-no-pad string.\n * @public\n */\nexport function base64UrlNoPadEncode(data: Uint8Array): string {\n let base64: string;\n if (typeof Buffer !== 'undefined') {\n base64 = Buffer.from(data).toString('base64');\n /* c8 ignore start - browser-only: btoa path not available in Node tests */\n } else {\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n base64 = btoa(binary);\n }\n /* c8 ignore stop */\n // Convert to base64url: + → -, / → _, strip = padding\n return base64.replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '');\n}\n\n/**\n * Decodes a base64url (no-padding) string (RFC 4648 §5) back to a `Uint8Array`.\n *\n * This is the bare primitive with no multibase prefix; use\n * {@link CryptoUtils.multibaseBase64UrlDecode} to decode a multibase-`'m'`-prefixed value.\n *\n * @param encoded - The base64url-no-pad body to decode.\n * @returns `Success` with the decoded bytes, or `Failure` with error context.\n * @public\n */\nexport function base64UrlNoPadDecode(encoded: string): Result<Uint8Array> {\n if (!isBase64UrlNoPadBody(encoded)) {\n return fail(`base64UrlNoPadDecode: malformed base64url body`);\n }\n // Convert base64url back to standard base64 and restore padding\n const base64 = encoded.replace(/-/g, '+').replace(/_/g, '/');\n const padded = base64 + '='.repeat((4 - (base64.length % 4)) % 4);\n try {\n let bytes: Uint8Array;\n if (typeof Buffer !== 'undefined') {\n bytes = new Uint8Array(Buffer.from(padded, 'base64'));\n /* c8 ignore start - browser-only: atob path not available in Node tests */\n } else {\n const binary = atob(padded);\n bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n }\n /* c8 ignore stop */\n return succeed(bytes);\n /* c8 ignore next 3 - defensive: shape check above prevents invalid chars from reaching here */\n } catch {\n return fail(`base64UrlNoPadDecode: malformed base64url body`);\n }\n}\n\n/**\n * The structural *shape* of a {@link CryptoUtils.MultibaseSpkiPublicKey}: the\n * multibase `'m'` prefix followed by a non-empty base64url-no-pad body\n * (`A-Z`, `a-z`, `0-9`, `-`, `_`).\n *\n * This is a shape prefilter, not full validation: it does not decode the DER\n * SPKI or verify the key material or algorithm (that happens in\n * {@link CryptoUtils.importPublicKeyFromMultibaseSpki}). The authoritative guard\n * is {@link CryptoUtils.isValidMultibaseSpkiPublicKey}, which enforces this\n * pattern **and** additionally rejects a body whose length is an impossible\n * base64 remainder. Exposed for callers that need the pattern directly (e.g. a\n * JSON-schema `pattern` field); prefer the guard/converter for validation.\n *\n * @public\n */\nexport const MultibaseSpkiPublicKeyRegExp: RegExp = /^m[A-Za-z0-9_-]+$/;\n\n/**\n * Type guard for {@link CryptoUtils.MultibaseSpkiPublicKey}: a string matching\n * {@link CryptoUtils.MultibaseSpkiPublicKeyRegExp} (multibase `'m'` prefix + a\n * non-empty base64url-no-pad body) whose body also satisfies the base64url-no-pad\n * length rule shared with {@link CryptoUtils.base64UrlNoPadDecode}. This is a\n * structural *shape* check, not full validation — it does not decode the DER SPKI\n * or verify the key material/algorithm (a malformed-but-well-shaped string fails\n * later, with clear context, in {@link CryptoUtils.importPublicKeyFromMultibaseSpki}).\n *\n * @param value - The value to test.\n * @returns `true` if `value` is a well-formed multibase SPKI public key string.\n * @public\n */\nexport function isValidMultibaseSpkiPublicKey(value: unknown): value is MultibaseSpkiPublicKey {\n return (\n typeof value === 'string' &&\n MultibaseSpkiPublicKeyRegExp.test(value) &&\n isBase64UrlNoPadBody(value.slice(1))\n );\n}\n\n/**\n * Encodes a `Uint8Array` as a multibase base64url (no-padding) string.\n *\n * The multibase prefix `'m'` identifies the encoding as RFC 4648 base64url\n * without padding. The body uses base64url alphabet: `+` → `-`, `/` → `_`,\n * and trailing `=` padding is stripped.\n *\n * @param data - The binary data to encode.\n * @returns A multibase-prefixed base64url string (`'m' + base64url-no-pad`).\n * @public\n */\nexport function multibaseBase64UrlEncode(data: Uint8Array): string {\n return 'm' + base64UrlNoPadEncode(data);\n}\n\n/**\n * Decodes a multibase base64url (no-padding) string back to a `Uint8Array`.\n *\n * Validates that the first character is `'m'` (the multibase prefix for\n * RFC 4648 base64url without padding), then decodes the remaining body.\n *\n * @param encoded - A multibase-prefixed base64url string.\n * @returns `Success` with the decoded bytes, or `Failure` with error context.\n * @public\n */\nexport function multibaseBase64UrlDecode(encoded: string): Result<Uint8Array> {\n if (!encoded.startsWith('m')) {\n return fail(\n `multibaseBase64UrlDecode: invalid multibase prefix '${\n encoded[0] ?? '(empty)'\n }' — expected 'm' (base64url)`\n );\n }\n // Intentionally pin the exact original message (the delegate has a single failure\n // mode) to keep this established public function byte-for-byte behavior-preserving\n // after the extract-and-delegate refactor — do not \"fix\" this into composing the\n // delegate's message without updating the delegation-equivalence tests.\n return base64UrlNoPadDecode(encoded.slice(1)).withErrorFormat(\n () => `multibaseBase64UrlDecode: malformed base64url body`\n );\n}\n\n/**\n * Exports a public `CryptoKey` as a multibase base64url-encoded SPKI blob.\n *\n * The SPKI (SubjectPublicKeyInfo) format is the standard DER-encoded structure\n * for public keys defined in RFC 5280, RFC 5480, and RFC 8410. It is\n * algorithm-agnostic and suitable for storage and transmission.\n *\n * @param key - The `CryptoKey` to export. Must have `key.type === 'public'`.\n * @param provider - The {@link CryptoUtils.ICryptoProvider} to use for the export operation.\n * @returns `Success` with the multibase SPKI string, or `Failure` with error context.\n * @public\n */\nexport async function exportPublicKeyAsMultibaseSpki(\n key: CryptoKey,\n provider: ICryptoProvider\n): Promise<Result<MultibaseSpkiPublicKey>> {\n return (await provider.exportPublicKeySpki(key))\n .withErrorFormat((e) => `exportPublicKeyAsMultibaseSpki: ${e}`)\n .onSuccess((buf) =>\n // The output is a freshly-built valid multibase SPKI string (`'m'` prefix +\n // base64url-no-pad body), so brand it at the construction site — no re-validation needed.\n succeed(multibaseBase64UrlEncode(buf) as MultibaseSpkiPublicKey)\n );\n}\n\n/**\n * Imports a public key from a multibase base64url-encoded SPKI blob.\n *\n * Decodes the multibase prefix, decodes the base64url body, then uses\n * the provider to import the key with the algorithm parameters from\n * {@link CryptoUtils.keyPairAlgorithmParams}.\n *\n * Accepts a plain `string` (not only a branded {@link CryptoUtils.MultibaseSpkiPublicKey}),\n * so callers holding an unbranded value read from storage or the wire can import\n * it directly; a malformed value fails with error context rather than throwing.\n *\n * @param encoded - A multibase SPKI string produced by {@link CryptoUtils.exportPublicKeyAsMultibaseSpki}.\n * @param algorithm - The {@link CryptoUtils.KeyPairAlgorithm} the key was generated for.\n * @param provider - The {@link CryptoUtils.ICryptoProvider} to use for the import operation.\n * @returns `Success` with the imported public `CryptoKey`, or `Failure` with error context.\n * @public\n */\nexport async function importPublicKeyFromMultibaseSpki(\n encoded: string,\n algorithm: KeyPairAlgorithm,\n provider: ICryptoProvider\n): Promise<Result<CryptoKey>> {\n const decodeResult = multibaseBase64UrlDecode(encoded);\n if (decodeResult.isFailure()) {\n return fail(`importPublicKeyFromMultibaseSpki: ${decodeResult.message}`);\n }\n return (await provider.importPublicKeySpki(decodeResult.value, algorithm)).withErrorFormat(\n (e) => `importPublicKeyFromMultibaseSpki: ${e}`\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fgv/ts-extras",
3
- "version": "5.1.0-36",
3
+ "version": "5.1.0-39",
4
4
  "description": "Assorted Typescript Utilities",
5
5
  "main": "lib/index.js",
6
6
  "types": "dist/ts-extras.d.ts",
@@ -90,10 +90,10 @@
90
90
  "@types/js-yaml": "~4.0.9",
91
91
  "typedoc": "~0.28.16",
92
92
  "typedoc-plugin-markdown": "~4.9.0",
93
- "@fgv/ts-utils-jest": "5.1.0-36",
94
- "@fgv/ts-utils": "5.1.0-36",
95
- "@fgv/typedoc-compact-theme": "5.1.0-36",
96
- "@fgv/heft-dual-rig": "5.1.0-36"
93
+ "@fgv/heft-dual-rig": "5.1.0-39",
94
+ "@fgv/ts-utils-jest": "5.1.0-39",
95
+ "@fgv/ts-utils": "5.1.0-39",
96
+ "@fgv/typedoc-compact-theme": "5.1.0-39"
97
97
  },
98
98
  "dependencies": {
99
99
  "@types/luxon": "^3.7.1",
@@ -102,10 +102,10 @@
102
102
  "papaparse": "^5.4.1",
103
103
  "fflate": "~0.8.2",
104
104
  "js-yaml": "~4.1.1",
105
- "@fgv/ts-json-base": "5.1.0-36"
105
+ "@fgv/ts-json-base": "5.1.0-39"
106
106
  },
107
107
  "peerDependencies": {
108
- "@fgv/ts-utils": "5.1.0-36"
108
+ "@fgv/ts-utils": "5.1.0-39"
109
109
  },
110
110
  "repository": {
111
111
  "type": "git",