@metamask-previews/kyc-controller 0.0.0-preview-4f4c98e → 0.0.0-preview-eb263b0ee

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 (191) hide show
  1. package/dist/KycController-method-action-types.cjs +7 -0
  2. package/dist/KycController-method-action-types.cjs.map +1 -0
  3. package/dist/{KycController-method-action-types.d.ts → KycController-method-action-types.d.cts} +2 -2
  4. package/dist/KycController-method-action-types.d.cts.map +1 -0
  5. package/dist/KycController-method-action-types.d.mts +237 -0
  6. package/dist/KycController-method-action-types.d.mts.map +1 -0
  7. package/dist/{KycController-method-action-types.js → KycController-method-action-types.mjs} +1 -1
  8. package/dist/KycController-method-action-types.mjs.map +1 -0
  9. package/dist/KycController.cjs +1843 -0
  10. package/dist/KycController.cjs.map +1 -0
  11. package/dist/{KycController.d.ts → KycController.d.cts} +9 -9
  12. package/dist/KycController.d.cts.map +1 -0
  13. package/dist/KycController.d.mts +393 -0
  14. package/dist/KycController.d.mts.map +1 -0
  15. package/dist/{KycController.js → KycController.mjs} +10 -10
  16. package/dist/KycController.mjs.map +1 -0
  17. package/dist/KycService-method-action-types.cjs +7 -0
  18. package/dist/KycService-method-action-types.cjs.map +1 -0
  19. package/dist/{KycService-method-action-types.d.ts → KycService-method-action-types.d.cts} +2 -2
  20. package/dist/KycService-method-action-types.d.cts.map +1 -0
  21. package/dist/KycService-method-action-types.d.mts +186 -0
  22. package/dist/KycService-method-action-types.d.mts.map +1 -0
  23. package/dist/{KycService-method-action-types.js → KycService-method-action-types.mjs} +1 -1
  24. package/dist/KycService-method-action-types.mjs.map +1 -0
  25. package/dist/KycService.cjs +628 -0
  26. package/dist/KycService.cjs.map +1 -0
  27. package/dist/{KycService.d.ts → KycService.d.cts} +12 -12
  28. package/dist/KycService.d.cts.map +1 -0
  29. package/dist/KycService.d.mts +494 -0
  30. package/dist/KycService.d.mts.map +1 -0
  31. package/dist/{KycService.js → KycService.mjs} +7 -7
  32. package/dist/{KycService.js.map → KycService.mjs.map} +1 -1
  33. package/dist/countryCodes.cjs +274 -0
  34. package/dist/countryCodes.cjs.map +1 -0
  35. package/dist/{countryCodes.d.ts → countryCodes.d.cts} +1 -1
  36. package/dist/countryCodes.d.cts.map +1 -0
  37. package/dist/countryCodes.d.mts +18 -0
  38. package/dist/countryCodes.d.mts.map +1 -0
  39. package/dist/{countryCodes.js → countryCodes.mjs} +1 -1
  40. package/dist/countryCodes.mjs.map +1 -0
  41. package/dist/crypto.cjs +154 -0
  42. package/dist/crypto.cjs.map +1 -0
  43. package/dist/{crypto.d.ts → crypto.d.cts} +1 -1
  44. package/dist/crypto.d.cts.map +1 -0
  45. package/dist/crypto.d.mts +84 -0
  46. package/dist/crypto.d.mts.map +1 -0
  47. package/dist/{crypto.js → crypto.mjs} +7 -7
  48. package/dist/crypto.mjs.map +1 -0
  49. package/dist/encoding.cjs +40 -0
  50. package/dist/encoding.cjs.map +1 -0
  51. package/dist/{encoding.d.ts → encoding.d.cts} +1 -1
  52. package/dist/encoding.d.cts.map +1 -0
  53. package/dist/encoding.d.mts +24 -0
  54. package/dist/encoding.d.mts.map +1 -0
  55. package/dist/{encoding.js → encoding.mjs} +2 -2
  56. package/dist/encoding.mjs.map +1 -0
  57. package/dist/index.cjs +37 -0
  58. package/dist/index.cjs.map +1 -0
  59. package/dist/{index.d.ts → index.d.cts} +19 -19
  60. package/dist/index.d.cts.map +1 -0
  61. package/dist/index.d.mts +19 -0
  62. package/dist/index.d.mts.map +1 -0
  63. package/dist/index.mjs +12 -0
  64. package/dist/index.mjs.map +1 -0
  65. package/dist/selectors.cjs +30 -0
  66. package/dist/selectors.cjs.map +1 -0
  67. package/dist/{selectors.d.ts → selectors.d.cts} +4 -4
  68. package/dist/selectors.d.cts.map +1 -0
  69. package/dist/selectors.d.mts +24 -0
  70. package/dist/selectors.d.mts.map +1 -0
  71. package/dist/{selectors.js → selectors.mjs} +2 -2
  72. package/dist/selectors.mjs.map +1 -0
  73. package/dist/types.cjs +10 -0
  74. package/dist/types.cjs.map +1 -0
  75. package/dist/{types.d.ts → types.d.cts} +1 -1
  76. package/dist/types.d.cts.map +1 -0
  77. package/dist/types.d.mts +208 -0
  78. package/dist/types.d.mts.map +1 -0
  79. package/dist/{types.js → types.mjs} +1 -1
  80. package/dist/types.mjs.map +1 -0
  81. package/dist/ukyc/constants.cjs +75 -0
  82. package/dist/ukyc/constants.cjs.map +1 -0
  83. package/dist/ukyc/{constants.d.ts → constants.d.cts} +9 -9
  84. package/dist/ukyc/constants.d.cts.map +1 -0
  85. package/dist/ukyc/constants.d.mts +69 -0
  86. package/dist/ukyc/constants.d.mts.map +1 -0
  87. package/dist/ukyc/{constants.js → constants.mjs} +1 -1
  88. package/dist/ukyc/constants.mjs.map +1 -0
  89. package/dist/ukyc/deriveClientMaterial.cjs +65 -0
  90. package/dist/ukyc/deriveClientMaterial.cjs.map +1 -0
  91. package/dist/ukyc/{deriveClientMaterial.d.ts → deriveClientMaterial.d.cts} +1 -1
  92. package/dist/ukyc/deriveClientMaterial.d.cts.map +1 -0
  93. package/dist/ukyc/deriveClientMaterial.d.mts +54 -0
  94. package/dist/ukyc/deriveClientMaterial.d.mts.map +1 -0
  95. package/dist/ukyc/{deriveClientMaterial.js → deriveClientMaterial.mjs} +7 -7
  96. package/dist/ukyc/deriveClientMaterial.mjs.map +1 -0
  97. package/dist/ukyc/jwtChain.cjs +54 -0
  98. package/dist/ukyc/jwtChain.cjs.map +1 -0
  99. package/dist/ukyc/{jwtChain.d.ts → jwtChain.d.cts} +1 -1
  100. package/dist/ukyc/jwtChain.d.cts.map +1 -0
  101. package/dist/ukyc/jwtChain.d.mts +40 -0
  102. package/dist/ukyc/jwtChain.d.mts.map +1 -0
  103. package/dist/ukyc/{jwtChain.js → jwtChain.mjs} +4 -4
  104. package/dist/ukyc/jwtChain.mjs.map +1 -0
  105. package/dist/ukyc/localUserSecret.cjs +98 -0
  106. package/dist/ukyc/localUserSecret.cjs.map +1 -0
  107. package/dist/ukyc/{localUserSecret.d.ts → localUserSecret.d.cts} +1 -1
  108. package/dist/ukyc/localUserSecret.d.cts.map +1 -0
  109. package/dist/ukyc/localUserSecret.d.mts +65 -0
  110. package/dist/ukyc/localUserSecret.d.mts.map +1 -0
  111. package/dist/ukyc/{localUserSecret.js → localUserSecret.mjs} +4 -4
  112. package/dist/ukyc/localUserSecret.mjs.map +1 -0
  113. package/dist/ukyc/storageAccessToken.cjs +139 -0
  114. package/dist/ukyc/storageAccessToken.cjs.map +1 -0
  115. package/dist/ukyc/{storageAccessToken.d.ts → storageAccessToken.d.cts} +2 -2
  116. package/dist/ukyc/storageAccessToken.d.cts.map +1 -0
  117. package/dist/ukyc/storageAccessToken.d.mts +99 -0
  118. package/dist/ukyc/storageAccessToken.d.mts.map +1 -0
  119. package/dist/ukyc/{storageAccessToken.js → storageAccessToken.mjs} +5 -5
  120. package/dist/ukyc/storageAccessToken.mjs.map +1 -0
  121. package/dist/ukyc/testToken.cjs +61 -0
  122. package/dist/ukyc/testToken.cjs.map +1 -0
  123. package/dist/ukyc/{testToken.d.ts → testToken.d.cts} +2 -2
  124. package/dist/ukyc/testToken.d.cts.map +1 -0
  125. package/dist/ukyc/testToken.d.mts +50 -0
  126. package/dist/ukyc/testToken.d.mts.map +1 -0
  127. package/dist/ukyc/{testToken.js → testToken.mjs} +5 -5
  128. package/dist/ukyc/testToken.mjs.map +1 -0
  129. package/dist/ukyc/wrapEncryptionKey.cjs +37 -0
  130. package/dist/ukyc/wrapEncryptionKey.cjs.map +1 -0
  131. package/dist/ukyc/{wrapEncryptionKey.d.ts → wrapEncryptionKey.d.cts} +1 -1
  132. package/dist/ukyc/wrapEncryptionKey.d.cts.map +1 -0
  133. package/dist/ukyc/wrapEncryptionKey.d.mts +41 -0
  134. package/dist/ukyc/wrapEncryptionKey.d.mts.map +1 -0
  135. package/dist/ukyc/{wrapEncryptionKey.js → wrapEncryptionKey.mjs} +4 -3
  136. package/dist/ukyc/wrapEncryptionKey.mjs.map +1 -0
  137. package/dist/ukyc/wrapUserKey.cjs +80 -0
  138. package/dist/ukyc/wrapUserKey.cjs.map +1 -0
  139. package/dist/ukyc/{wrapUserKey.d.ts → wrapUserKey.d.cts} +1 -1
  140. package/dist/ukyc/wrapUserKey.d.cts.map +1 -0
  141. package/dist/ukyc/wrapUserKey.d.mts +26 -0
  142. package/dist/ukyc/wrapUserKey.d.mts.map +1 -0
  143. package/dist/ukyc/{wrapUserKey.js → wrapUserKey.mjs} +8 -8
  144. package/dist/ukyc/wrapUserKey.mjs.map +1 -0
  145. package/dist/ukyc/wrappedRelayPayload.cjs +32 -0
  146. package/dist/ukyc/wrappedRelayPayload.cjs.map +1 -0
  147. package/dist/ukyc/{wrappedRelayPayload.d.ts → wrappedRelayPayload.d.cts} +3 -3
  148. package/dist/ukyc/wrappedRelayPayload.d.cts.map +1 -0
  149. package/dist/ukyc/wrappedRelayPayload.d.mts +35 -0
  150. package/dist/ukyc/wrappedRelayPayload.d.mts.map +1 -0
  151. package/dist/ukyc/{wrappedRelayPayload.js → wrappedRelayPayload.mjs} +2 -2
  152. package/dist/ukyc/wrappedRelayPayload.mjs.map +1 -0
  153. package/package.json +17 -13
  154. package/dist/KycController-method-action-types.d.ts.map +0 -1
  155. package/dist/KycController-method-action-types.js.map +0 -1
  156. package/dist/KycController.d.ts.map +0 -1
  157. package/dist/KycController.js.map +0 -1
  158. package/dist/KycService-method-action-types.d.ts.map +0 -1
  159. package/dist/KycService-method-action-types.js.map +0 -1
  160. package/dist/KycService.d.ts.map +0 -1
  161. package/dist/countryCodes.d.ts.map +0 -1
  162. package/dist/countryCodes.js.map +0 -1
  163. package/dist/crypto.d.ts.map +0 -1
  164. package/dist/crypto.js.map +0 -1
  165. package/dist/encoding.d.ts.map +0 -1
  166. package/dist/encoding.js.map +0 -1
  167. package/dist/index.d.ts.map +0 -1
  168. package/dist/index.js +0 -12
  169. package/dist/index.js.map +0 -1
  170. package/dist/selectors.d.ts.map +0 -1
  171. package/dist/selectors.js.map +0 -1
  172. package/dist/types.d.ts.map +0 -1
  173. package/dist/types.js.map +0 -1
  174. package/dist/ukyc/constants.d.ts.map +0 -1
  175. package/dist/ukyc/constants.js.map +0 -1
  176. package/dist/ukyc/deriveClientMaterial.d.ts.map +0 -1
  177. package/dist/ukyc/deriveClientMaterial.js.map +0 -1
  178. package/dist/ukyc/jwtChain.d.ts.map +0 -1
  179. package/dist/ukyc/jwtChain.js.map +0 -1
  180. package/dist/ukyc/localUserSecret.d.ts.map +0 -1
  181. package/dist/ukyc/localUserSecret.js.map +0 -1
  182. package/dist/ukyc/storageAccessToken.d.ts.map +0 -1
  183. package/dist/ukyc/storageAccessToken.js.map +0 -1
  184. package/dist/ukyc/testToken.d.ts.map +0 -1
  185. package/dist/ukyc/testToken.js.map +0 -1
  186. package/dist/ukyc/wrapEncryptionKey.d.ts.map +0 -1
  187. package/dist/ukyc/wrapEncryptionKey.js.map +0 -1
  188. package/dist/ukyc/wrapUserKey.d.ts.map +0 -1
  189. package/dist/ukyc/wrapUserKey.js.map +0 -1
  190. package/dist/ukyc/wrappedRelayPayload.d.ts.map +0 -1
  191. package/dist/ukyc/wrappedRelayPayload.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"jwtChain.js","sourceRoot":"","sources":["../../src/ukyc/jwtChain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,OAAO,EAAE,MAAM,uBAAuB,CAAC;AAEhD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AA0ClD;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAO,OAAe,EAAE,KAAa;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAS,CAAC;IACtE,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CACb,mCAAmC,KAAK,KAAK,MAAM,CAAC,KAAK,CAAC,EAAE,CAC7D,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,IAAW,EAAE,QAAgB;IAC1D,MAAM,CAAC,aAAa,EAAE,cAAc,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC9E,IAAI,CAAC,aAAa,IAAI,CAAC,cAAc,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAC3D,MAAM,IAAI,KAAK,CACb,gEAAgE,CACjE,CAAC;IACJ,CAAC;IAED,MAAM,MAAM,GAAG,iBAAiB,CAAY,aAAa,EAAE,QAAQ,CAAC,CAAC;IACrE,IAAI,MAAM,CAAC,GAAG,KAAK,OAAO,EAAE,CAAC;QAC3B,MAAM,IAAI,KAAK,CACb,mCAAmC,MAAM,CACvC,MAAM,CAAC,GAAG,CACX,qBAAqB,CACvB,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,KAAK,MAAM,CAAC,GAAG,CAAC,CAAC;IACvD,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,MAAM,IAAI,KAAK,CACb,2CAA2C,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAClE,CAAC;IACJ,CAAC;IACD,IAAI,GAAG,CAAC,GAAG,KAAK,KAAK,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;QAC/C,MAAM,IAAI,KAAK,CACb,kBAAkB,GAAG,CAAC,GAAG,mCAAmC,GAAG,CAAC,GAAG,SAAS,GAAG,CAAC,GAAG,IAAI,CACxF,CAAC;IACJ,CAAC;IAED,MAAM,OAAO,GAAG,OAAO,CAAC,MAAM,CAC5B,gBAAgB,CAAC,gBAAgB,CAAC,EAClC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,aAAa,IAAI,cAAc,EAAE,CAAC,EAC9D,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CACxB,CAAC;IACF,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,4DAA4D,CAC7D,CAAC;IACJ,CAAC;IAED,OAAO,iBAAiB,CAAkB,cAAc,EAAE,SAAS,CAAC,CAAC;AACvE,CAAC","sourcesContent":["import { bytesToString } from '@metamask/utils';\nimport { ed25519 } from '@noble/curves/ed25519';\n\nimport { base64UrlToBytes } from '../encoding.js';\n\n/**\n * Verifies the `jwtChain` returned by the Fractal encryption service against\n * its published JWKS.\n *\n * The signature check is done with `@noble/curves` (rather than WebCrypto\n * `subtle`) because not every MetaMask runtime exposes a `subtle`\n * implementation for Ed25519; JWT parsing is a plain base64url/JSON decode, so\n * no `jose` dependency is required.\n */\n\n/**\n * A single Ed25519 (OKP) JSON Web Key from the Fractal JWKS.\n */\nexport type Jwk = {\n kty: string;\n crv: string;\n x: string;\n kid: string;\n use?: string;\n alg?: string;\n};\n\n/**\n * The verified `jwtChain` payload. `sessionServerPublicKeyX` attests the\n * server's X25519 public key so the client can confirm the value returned\n * out-of-band in an encryption schema was not tampered with.\n */\nexport type JwtChainPayload = {\n sessionServerPublicKeyX: string;\n nonce: string;\n};\n\n/**\n * The protected header of a compact JWT.\n */\ntype JwtHeader = {\n alg?: string;\n kid?: string;\n};\n\n/**\n * Decodes a base64url JWT segment into a parsed JSON object.\n *\n * @param segment - The base64url-encoded segment.\n * @param label - Human-readable segment name for error messages.\n * @returns The parsed JSON object.\n */\nfunction decodeJsonSegment<Type>(segment: string, label: string): Type {\n try {\n return JSON.parse(bytesToString(base64UrlToBytes(segment))) as Type;\n } catch (error) {\n throw new Error(\n `UKYC: failed to decode jwtChain ${label}: ${String(error)}`,\n );\n }\n}\n\n/**\n * Verifies `jwtChain` against `keys`: matches the JWT header `kid` to a\n * published Ed25519 signing key and checks the EdDSA signature over the\n * `header.payload` input. Returns the decoded, verified payload.\n *\n * @param keys - The JWKS keys published by the Fractal encryption service.\n * @param jwtChain - The compact-serialized EdDSA JWT from an encryption schema.\n * @returns The verified JWT payload.\n */\nexport function verifyJwtChain(keys: Jwk[], jwtChain: string): JwtChainPayload {\n const [headerSegment, payloadSegment, signatureSegment] = jwtChain.split('.');\n if (!headerSegment || !payloadSegment || !signatureSegment) {\n throw new Error(\n 'UKYC: jwtChain is not a well-formed JWT (expected 3 segments).',\n );\n }\n\n const header = decodeJsonSegment<JwtHeader>(headerSegment, 'header');\n if (header.alg !== 'EdDSA') {\n throw new Error(\n `UKYC: unsupported jwtChain alg \"${String(\n header.alg,\n )}\" (expected EdDSA).`,\n );\n }\n\n const jwk = keys.find((key) => key.kid === header.kid);\n if (!jwk) {\n throw new Error(\n `UKYC: no JWKS key matches jwtChain kid \"${String(header.kid)}\".`,\n );\n }\n if (jwk.kty !== 'OKP' || jwk.crv !== 'Ed25519') {\n throw new Error(\n `UKYC: JWKS key ${jwk.kid} is not an Ed25519 OKP key (kty=${jwk.kty}, crv=${jwk.crv}).`,\n );\n }\n\n const isValid = ed25519.verify(\n base64UrlToBytes(signatureSegment),\n new TextEncoder().encode(`${headerSegment}.${payloadSegment}`),\n base64UrlToBytes(jwk.x),\n );\n if (!isValid) {\n throw new Error(\n 'UKYC: jwtChain signature verification failed against JWKS.',\n );\n }\n\n return decodeJsonSegment<JwtChainPayload>(payloadSegment, 'payload');\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"localUserSecret.d.ts","sourceRoot":"","sources":["../../src/ukyc/localUserSecret.ts"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;GAaG;AAEH;;;;GAIG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC;;OAEG;IACH,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACxE;;OAEG;IACH,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC/E,CAAC;AASF;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,wBAAwB,EAC/B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAgB5B;AAqBD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,0BAA0B,CAC9C,KAAK,EAAE,wBAAwB,EAC/B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,UAAU,CAAC,CAgCrB;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,wBAAwB,EAC/B,eAAe,CAAC,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC,CAElB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"localUserSecret.js","sourceRoot":"","sources":["../../src/ukyc/localUserSecret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,gBAAgB,CAAC;AAiCxB;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,IAAI,GAAG,EAA+B,CAAC;AAEjE;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,KAA+B,EAC/B,eAAwB;IAExB,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,GAAG,CAAC,2BAA2B,EAAE,eAAe,CAAC,CAAC;IAE7E,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,eAAe,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IAE9C,IAAI,eAAe,CAAC,MAAM,KAAK,iCAAiC,EAAE,CAAC;QACjE,MAAM,IAAI,KAAK,CACb,wDAAwD,eAAe,CAAC,MAAM,cAAc,iCAAiC,GAAG,CACjI,CAAC;IACJ,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,sBAAsB,CACnC,KAA+B,EAC/B,eAA2B,EAC3B,eAAwB;IAExB,MAAM,KAAK,CAAC,GAAG,CACb,2BAA2B,EAC3B,aAAa,CAAC,eAAe,CAAC,EAC9B,eAAe,CAChB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,KAA+B,EAC/B,eAAwB;IAExB,MAAM,QAAQ,GAAG,eAAe,IAAI,EAAE,CAAC;IAEvC,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAChD,IAAI,OAAO,EAAE,CAAC;QACZ,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,KAAK,IAAyB,EAAE;QAChD,MAAM,QAAQ,GAAG,MAAM,mBAAmB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC;QACnE,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,eAAe,GAAG,WAAW,CAAC,iCAAiC,CAAC,CAAC;QACvE,MAAM,sBAAsB,CAAC,KAAK,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;QAEtE,0EAA0E;QAC1E,yEAAyE;QACzE,yEAAyE;QACzE,OAAO,CACL,CAAC,MAAM,mBAAmB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,IAAI,eAAe,CACvE,CAAC;IACJ,CAAC,CAAC,EAAE,CAAC;IAEL,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAE1C,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC;IACxB,CAAC;YAAS,CAAC;QACT,iBAAiB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,KAA+B,EAC/B,eAAwB;IAExB,OAAO,CAAC,MAAM,mBAAmB,CAAC,KAAK,EAAE,eAAe,CAAC,CAAC,KAAK,IAAI,CAAC;AACtE,CAAC","sourcesContent":["import { base64ToBytes, bytesToBase64 } from '@metamask/utils';\nimport { randomBytes } from '@noble/hashes/utils';\n\nimport {\n UKYC_LOCAL_USER_SECRET_PATH,\n UKYC_LOCAL_USER_SECRET_SIZE_BYTES,\n} from './constants.js';\n\n/**\n * Orchestrates creation and loading of the UKYC `local_user_secret`.\n *\n * The `local_user_secret` is the root secret for all UKYC client-derived\n * material. It is generated once, on first enrollment, and persisted to\n * MetaMask Encrypted User Storage. It is never transmitted off the device, not\n * even to the idOS Relay. Every subsequent value (`storage_id`,\n * `data_encryption_key`, `signing_key`, `relay_tunnel_key`) is derived from it\n * via HKDF — see `deriveClientMaterial`.\n *\n * This module is platform-agnostic: the Encrypted User Storage backing is\n * injected as a {@link UkycLocalUserSecretStore} so the controller (which owns\n * the messenger) supplies the concrete `UserStorageController` calls.\n */\n\n/**\n * The Encrypted User Storage operations this module needs. On MetaMask clients\n * these are backed by `UserStorageController:performGetStorage` /\n * `performSetStorage`.\n */\nexport type UkycLocalUserSecretStore = {\n /**\n * Reads the base64 string stored at `path`, or `null` if none exists.\n */\n get: (path: string, entropySourceId?: string) => Promise<string | null>;\n /**\n * Writes the base64 string `value` at `path`.\n */\n set: (path: string, value: string, entropySourceId?: string) => Promise<void>;\n};\n\n/**\n * In-flight `getOrCreateLocalUserSecret` calls, keyed by entropy source.\n * Deduplicates concurrent enrollments in a single client session so we never\n * generate and persist two competing `local_user_secret`s for the same source.\n */\nconst inFlightCreations = new Map<string, Promise<Uint8Array>>();\n\n/**\n * Loads the persisted `local_user_secret` from Encrypted User Storage, if one\n * exists.\n *\n * @param store - The Encrypted User Storage adapter.\n * @param entropySourceId - Optional HD keyring entropy source id, used to scope\n * the secret to a specific SRP in multi-SRP wallets. Defaults to the primary SRP.\n * @returns The decoded `local_user_secret` bytes, or `null` if none has been\n * enrolled.\n */\nexport async function loadLocalUserSecret(\n store: UkycLocalUserSecretStore,\n entropySourceId?: string,\n): Promise<Uint8Array | null> {\n const stored = await store.get(UKYC_LOCAL_USER_SECRET_PATH, entropySourceId);\n\n if (!stored) {\n return null;\n }\n\n const localUserSecret = base64ToBytes(stored);\n\n if (localUserSecret.length !== UKYC_LOCAL_USER_SECRET_SIZE_BYTES) {\n throw new Error(\n `UKYC: stored local_user_secret has unexpected length ${localUserSecret.length}, expected ${UKYC_LOCAL_USER_SECRET_SIZE_BYTES}.`,\n );\n }\n\n return localUserSecret;\n}\n\n/**\n * Persists a freshly generated `local_user_secret` to Encrypted User Storage.\n *\n * @param store - The Encrypted User Storage adapter.\n * @param localUserSecret - The `local_user_secret` bytes to persist.\n * @param entropySourceId - Optional HD keyring entropy source id.\n */\nasync function persistLocalUserSecret(\n store: UkycLocalUserSecretStore,\n localUserSecret: Uint8Array,\n entropySourceId?: string,\n): Promise<void> {\n await store.set(\n UKYC_LOCAL_USER_SECRET_PATH,\n bytesToBase64(localUserSecret),\n entropySourceId,\n );\n}\n\n/**\n * Creates the UKYC `local_user_secret` if it does not already exist, otherwise\n * loads the existing one. This is the single entry point used on UKYC\n * enrollment.\n *\n * The operation is idempotent and safe against concurrent callers in the same\n * session: repeated or parallel calls resolve to the same `local_user_secret`\n * and never generate more than one secret for a given entropy source.\n *\n * @param store - The Encrypted User Storage adapter.\n * @param entropySourceId - Optional HD keyring entropy source id, used to scope\n * the secret to a specific SRP in multi-SRP wallets. Defaults to the primary SRP.\n * @returns The `local_user_secret` bytes (existing or newly created).\n */\nexport async function getOrCreateLocalUserSecret(\n store: UkycLocalUserSecretStore,\n entropySourceId?: string,\n): Promise<Uint8Array> {\n const cacheKey = entropySourceId ?? '';\n\n const pending = inFlightCreations.get(cacheKey);\n if (pending) {\n return pending;\n }\n\n const creation = (async (): Promise<Uint8Array> => {\n const existing = await loadLocalUserSecret(store, entropySourceId);\n if (existing) {\n return existing;\n }\n\n const localUserSecret = randomBytes(UKYC_LOCAL_USER_SECRET_SIZE_BYTES);\n await persistLocalUserSecret(store, localUserSecret, entropySourceId);\n\n // Re-read after persisting so that all callers converge on whatever value\n // actually landed in storage (defends against a competing write that may\n // have won the race, e.g. from another device syncing the same feature).\n return (\n (await loadLocalUserSecret(store, entropySourceId)) ?? localUserSecret\n );\n })();\n\n inFlightCreations.set(cacheKey, creation);\n\n try {\n return await creation;\n } finally {\n inFlightCreations.delete(cacheKey);\n }\n}\n\n/**\n * Whether a `local_user_secret` has already been enrolled for the given entropy\n * source.\n *\n * @param store - The Encrypted User Storage adapter.\n * @param entropySourceId - Optional HD keyring entropy source id.\n * @returns `true` if a `local_user_secret` exists in Encrypted User Storage.\n */\nexport async function hasLocalUserSecret(\n store: UkycLocalUserSecretStore,\n entropySourceId?: string,\n): Promise<boolean> {\n return (await loadLocalUserSecret(store, entropySourceId)) !== null;\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"storageAccessToken.d.ts","sourceRoot":"","sources":["../../src/ukyc/storageAccessToken.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AAEpE;;;;;;;;;GASG;AAEH;;GAEG;AACH,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE/D;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAAG,QAAQ,GAAG,YAAY,CAAC;AAEzD;;;GAGG;AACH,MAAM,MAAM,6BAA6B,GAAG;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,iFAAiF;IACjF,GAAG,EAAE,MAAM,EAAE,CAAC;IAId,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,SAAS,EAAE,kBAAkB,CAAC;IAC9B,mFAAmF;IACnF,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CAEpB,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,sBAAsB,GAAG;IACnC,OAAO,EAAE,6BAA6B,CAAC;IACvC,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,4BAA4B,GAAG;IACzC,wDAAwD;IACxD,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,4EAA4E;IAC5E,UAAU,EAAE,oBAAoB,EAAE,CAAC;IACnC,wDAAwD;IACxD,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,gEAAgE;IAChE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,uDAAuD;IACvD,SAAS,EAAE,IAAI,CAAC;CACjB,CAAC;AAEF,KAAK,SAAS,GACV,IAAI,GACJ,OAAO,GACP,MAAM,GACN,MAAM,GACN,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAAA;CAAE,CAAC;AAE7C;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAqCzD;AAeD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,4BAA4B,GACnC,sBAAsB,CAwDxB;AAED;;;;;;;;GAQG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,sBAAsB,GAC5B,MAAM,CAER"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"storageAccessToken.js","sourceRoot":"","sources":["../../src/ukyc/storageAccessToken.ts"],"names":[],"mappings":"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","sourcesContent":["import { stringToBytes } from '@metamask/utils';\nimport { ed25519 } from '@noble/curves/ed25519';\n\nimport { toBase64Url } from '../encoding.js';\nimport {\n UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES,\n UKYC_STORAGE_ACCESS_TOKEN_VERSION,\n} from './constants.js';\nimport type { UkycClientMaterial } from './deriveClientMaterial.js';\n\n/**\n * Mints `storage_access_token` capabilities — the client-signed, scoped,\n * session-bound proofs that authorize UKYC storage operations. See the\n * architecture doc, section \"Storage Authentication\".\n *\n * The token is Ed25519 over RFC 8785 (JCS) canonical JSON of the payload. Only\n * the client holds the private `signing_key`, so only the client can mint a\n * token; a `read`/`write`-scoped token may then be handed to the Relay to\n * present, but `delete` is never delegated.\n */\n\n/**\n * Storage operations a `storage_access_token` can authorize.\n */\nexport type UkycStorageOperation = 'read' | 'write' | 'delete';\n\n/**\n * Who presents the token to UKYC storage. The Relay may only present\n * `read`/`write` tokens; `delete` is always client-presented.\n */\nexport type UkycTokenPresenter = 'client' | 'idos-relay';\n\n/**\n * The signed `storage_access_token` payload. Field names are snake_case because\n * they are canonicalized and hashed exactly as they appear on the wire.\n */\nexport type UkycStorageAccessTokenPayload = {\n version: number;\n /** Every verifier that may accept the token, e.g. UKYC Storage and idOS Kwil. */\n aud: string[];\n // Wire-shape fields are snake_case; they are canonicalized and signed exactly\n // as they appear on the wire.\n /* eslint-disable @typescript-eslint/naming-convention */\n storage_id: string;\n signing_public_key: string;\n operations: UkycStorageOperation[];\n presenter: UkycTokenPresenter;\n /** UKYC session id. Required (and only present) when presenter is `idos-relay`. */\n session_id?: string;\n issued_at: string;\n expires_at: string;\n /* eslint-enable @typescript-eslint/naming-convention */\n};\n\n/**\n * The on-the-wire envelope: the payload plus its detached Ed25519 signature\n * (base64url) over the JCS canonicalization of the payload.\n */\nexport type UkycStorageAccessToken = {\n payload: UkycStorageAccessTokenPayload;\n signature: string;\n};\n\n/**\n * Inputs for minting a `storage_access_token`.\n */\nexport type SignStorageAccessTokenParams = {\n /** Client material derived from `local_user_secret`. */\n material: UkycClientMaterial;\n /** Operations the token authorizes. `delete` must be the sole operation. */\n operations: UkycStorageOperation[];\n /** Who will present the token. Defaults to `client`. */\n presenter?: UkycTokenPresenter;\n /** UKYC session id. Required when presenter is `idos-relay`. */\n sessionId?: string;\n /** Token issue time. Defaults to now. */\n issuedAt?: Date;\n /** Token expiry. Must be strictly after `issuedAt`. */\n expiresAt: Date;\n};\n\ntype JsonValue =\n | null\n | boolean\n | number\n | string\n | JsonValue[]\n | { [key: string]: JsonValue | undefined };\n\n/**\n * Serializes a JSON value to RFC 8785 (JCS) canonical form.\n *\n * Scope note: this implementation covers the JSON shapes used by UKYC storage\n * payloads — objects, arrays, strings, integers, booleans, and null. Object\n * members are sorted by their UTF-16 code units (matching JS default string\n * ordering, which is what JCS requires) and `undefined` members are dropped.\n * Non-finite and non-integer numbers are rejected, since the payloads never\n * contain them and correct JCS number formatting for the general case is\n * intentionally out of scope here.\n *\n * @param value - The value to canonicalize.\n * @returns The canonical JSON string.\n */\nexport function canonicalizeJson(value: JsonValue): string {\n if (value === null) {\n return 'null';\n }\n\n if (typeof value === 'boolean') {\n return value ? 'true' : 'false';\n }\n\n if (typeof value === 'number') {\n if (!Number.isInteger(value)) {\n throw new Error(\n 'UKYC: cannot canonicalize a non-integer number for JCS.',\n );\n }\n return JSON.stringify(value);\n }\n\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n return `[${value.map((item) => canonicalizeJson(item)).join(',')}]`;\n }\n\n const entries = Object.keys(value)\n .sort()\n .reduce<string[]>((acc, key) => {\n const child = value[key];\n if (child !== undefined) {\n acc.push(`${JSON.stringify(key)}:${canonicalizeJson(child)}`);\n }\n return acc;\n }, []);\n\n return `{${entries.join(',')}}`;\n}\n\n/**\n * Formats a date as RFC 3339 UTC with whole-second precision (e.g.\n * `2026-07-07T00:00:00Z`). `Date.prototype.toISOString` always emits\n * milliseconds (`...:00.000Z`); the `storage_access_token` wire format omits\n * fractional seconds, so the sub-second component is truncated (not rounded).\n *\n * @param date - The date to format.\n * @returns The RFC 3339 timestamp without fractional seconds.\n */\nfunction toRfc3339Seconds(date: Date): string {\n return `${date.toISOString().slice(0, 19)}Z`;\n}\n\n/**\n * Builds and signs a `storage_access_token`.\n *\n * @param params - See {@link SignStorageAccessTokenParams}.\n * @returns The signed token envelope.\n */\nexport function signStorageAccessToken(\n params: SignStorageAccessTokenParams,\n): UkycStorageAccessToken {\n const {\n material,\n operations,\n presenter = 'client',\n sessionId,\n issuedAt = new Date(),\n expiresAt,\n } = params;\n\n assertValidOperations(operations);\n\n if (expiresAt.getTime() <= issuedAt.getTime()) {\n throw new Error(\n 'UKYC: storage_access_token expires_at must be after issued_at.',\n );\n }\n\n const isDelete = operations.includes('delete');\n\n if (presenter === 'idos-relay' && isDelete) {\n throw new Error(\n 'UKYC: a delete-scoped storage_access_token cannot be delegated to the Relay.',\n );\n }\n\n if (presenter === 'idos-relay' && !sessionId) {\n throw new Error(\n 'UKYC: a Relay-presented storage_access_token requires a session_id.',\n );\n }\n\n const payload: UkycStorageAccessTokenPayload = {\n version: UKYC_STORAGE_ACCESS_TOKEN_VERSION,\n aud: [...UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES],\n storage_id: toBase64Url(material.storageId),\n signing_public_key: toBase64Url(material.signingPublicKey),\n operations,\n presenter,\n issued_at: toRfc3339Seconds(issuedAt),\n expires_at: toRfc3339Seconds(expiresAt),\n };\n\n // Only bind session_id for Relay-presented tokens; omit the key entirely for\n // client-presented tokens so it does not appear in the canonicalized payload.\n if (presenter === 'idos-relay') {\n payload.session_id = sessionId;\n }\n\n const message = stringToBytes(canonicalizeJson(payload));\n const signature = ed25519.sign(message, material.signingKey);\n\n return {\n payload,\n signature: toBase64Url(signature),\n };\n}\n\n/**\n * Serializes a signed token to a compact string suitable for header transport\n * (`Authorization: AccessToken <token>`, see `UKYC_CAPABILITY_AUTH_SCHEME`). The\n * complete envelope is base64url-encoded; the private `signing_key` is never\n * included.\n *\n * @param token - The signed token envelope.\n * @returns The base64url-encoded envelope string.\n */\nexport function encodeStorageAccessTokenForHeader(\n token: UkycStorageAccessToken,\n): string {\n return toBase64Url(stringToBytes(JSON.stringify(token)));\n}\n\n/**\n * Validates that an operations list is one storage understands: a non-empty set\n * of `read`/`write`, or exactly `['delete']`. `delete` is never combined with\n * other operations.\n *\n * @param operations - The requested operations.\n */\nfunction assertValidOperations(operations: UkycStorageOperation[]): void {\n if (operations.length === 0) {\n throw new Error(\n 'UKYC: storage_access_token requires at least one operation.',\n );\n }\n\n const unique = new Set(operations);\n\n if (unique.size !== operations.length) {\n throw new Error('UKYC: storage_access_token operations must be unique.');\n }\n\n if (unique.has('delete') && operations.length > 1) {\n throw new Error(\n 'UKYC: a delete-scoped storage_access_token must contain only \"delete\".',\n );\n }\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"testToken.d.ts","sourceRoot":"","sources":["../../src/ukyc/testToken.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EACnB,MAAM,yBAAyB,CAAC;AAgBjC;;;;GAIG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;;OAIG;IACH,eAAe,CAAC,EAAE,UAAU,GAAG,MAAM,CAAC;IACtC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;IACpC,wDAAwD;IACxD,SAAS,CAAC,EAAE,kBAAkB,CAAC;IAC/B,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yCAAyC;IACzC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,sDAAsD;IACtD,SAAS,CAAC,EAAE,IAAI,CAAC;CAClB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,mBAAmB,GAAG;IAChC,6EAA6E;IAC7E,eAAe,EAAE,MAAM,CAAC;IACxB,0EAA0E;IAC1E,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,gBAAgB,EAAE,MAAM,CAAC;IACzB,uDAAuD;IACvD,KAAK,EAAE,sBAAsB,CAAC;IAC9B;;;OAGG;IACH,mBAAmB,EAAE,MAAM,CAAC;CAC7B,CAAC;AAgBF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,GAAE,uBAA4B,GACnC,mBAAmB,CAgCrB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"testToken.js","sourceRoot":"","sources":["../../src/ukyc/testToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE1E,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,GAClC,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,iCAAiC,EACjC,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAOjC;;;;;;;;;GASG;AAEH,yEAAyE;AACzE,MAAM,yBAAyB,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AA6CrD;;;;;;GAMG;AACH,SAAS,sBAAsB,CAAC,MAA4B;IAC1D,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,WAAW,CAAC,iCAAiC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAM,GAA4B,EAAE;IAEpC,MAAM,EACJ,UAAU,GAAG,CAAC,MAAM,CAAC,EACrB,SAAS,EACT,SAAS,EACT,QAAQ,GAAG,IAAI,IAAI,EAAE,EACrB,SAAS,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,yBAAyB,CAAC,GACrE,GAAG,MAAM,CAAC;IAEX,MAAM,eAAe,GAAG,sBAAsB,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;IACvE,MAAM,QAAQ,GAAG,oBAAoB,CAAC,eAAe,CAAC,CAAC;IAEvD,MAAM,KAAK,GAAG,sBAAsB,CAAC;QACnC,QAAQ;QACR,UAAU;QACV,SAAS;QACT,SAAS;QACT,QAAQ;QACR,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,oBAAoB,CAAC,QAAQ,CAAC,CAAC;IAEvE,OAAO;QACL,eAAe,EAAE,UAAU,CAAC,eAAe,CAAC;QAC5C,SAAS;QACT,gBAAgB;QAChB,KAAK;QACL,mBAAmB,EAAE,GAAG,2BAA2B,IAAI,iCAAiC,CACtF,KAAK,CACN,EAAE;KACJ,CAAC;AACJ,CAAC","sourcesContent":["import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils';\n\nimport {\n UKYC_CAPABILITY_AUTH_SCHEME,\n UKYC_LOCAL_USER_SECRET_SIZE_BYTES,\n} from './constants.js';\nimport {\n deriveClientMaterial,\n encodeClientMaterial,\n} from './deriveClientMaterial.js';\nimport {\n encodeStorageAccessTokenForHeader,\n signStorageAccessToken,\n} from './storageAccessToken.js';\nimport type {\n UkycStorageAccessToken,\n UkycStorageOperation,\n UkycTokenPresenter,\n} from './storageAccessToken.js';\n\n/**\n * Mints ready-to-use UKYC `storage_access_token`s for testing UKYC Storage.\n *\n * This composes the same pure functions the MetaMask client uses in production\n * (`deriveClientMaterial` + `signStorageAccessToken`), so a third party such as\n * idOS can produce a valid, signed token without deriving one on-device. No\n * server \"fills in\" the `signing_public_key`: it is derived from a\n * `local_user_secret` the caller controls and registered on first write, so\n * reusing the same secret yields a stable `storage_id` and controlling key.\n */\n\n/** Default token lifetime when `expiresAt` is not supplied (4 hours). */\nconst DEFAULT_TOKEN_LIFETIME_MS = 4 * 60 * 60 * 1000;\n\n/**\n * Inputs for {@link mintUkycTestToken}. All fields are optional; the only value\n * a caller usually pins is `localUserSecret`, so `storage_id` and the signing\n * key stay stable across runs.\n */\nexport type MintUkycTestTokenParams = {\n /**\n * The root `local_user_secret`, as raw 32 bytes or a hex string. When omitted\n * a fresh random secret is generated (returned in the result so it can be\n * reused).\n */\n localUserSecret?: Uint8Array | string;\n /** Operations the token authorizes. Defaults to `['read']`. */\n operations?: UkycStorageOperation[];\n /** Who will present the token. Defaults to `client`. */\n presenter?: UkycTokenPresenter;\n /** UKYC session id. Required when `presenter` is `idos-relay`. */\n sessionId?: string;\n /** Token issue time. Defaults to now. */\n issuedAt?: Date;\n /** Token expiry. Defaults to `issuedAt` + 4 hours. */\n expiresAt?: Date;\n};\n\n/**\n * The minted token plus everything needed to exercise UKYC Storage with it.\n */\nexport type MintedUkycTestToken = {\n /** The `local_user_secret` used, hex-encoded, so the caller can reuse it. */\n localUserSecret: string;\n /** base64url `storage_id` — use it as the `{storage_id}` path segment. */\n storageId: string;\n /** base64url `signing_public_key` registered on first write. */\n signingPublicKey: string;\n /** The signed token envelope (payload + signature). */\n token: UkycStorageAccessToken;\n /**\n * The full `Authorization` header value, e.g.\n * `AccessToken <base64url(envelope)>`, ready to send to UKYC Storage.\n */\n authorizationHeader: string;\n};\n\n/**\n * Resolves the caller-supplied secret into raw bytes, generating a random one\n * when none is provided.\n *\n * @param secret - Raw 32 bytes, a hex string, or undefined for a random secret.\n * @returns The `local_user_secret` bytes.\n */\nfunction resolveLocalUserSecret(secret?: Uint8Array | string): Uint8Array {\n if (secret === undefined) {\n return randomBytes(UKYC_LOCAL_USER_SECRET_SIZE_BYTES);\n }\n return typeof secret === 'string' ? hexToBytes(secret) : secret;\n}\n\n/**\n * Mints a signed UKYC `storage_access_token` for testing.\n *\n * @param params - See {@link MintUkycTestTokenParams}.\n * @returns The token, its `Authorization` header, and the derived identifiers.\n */\nexport function mintUkycTestToken(\n params: MintUkycTestTokenParams = {},\n): MintedUkycTestToken {\n const {\n operations = ['read'],\n presenter,\n sessionId,\n issuedAt = new Date(),\n expiresAt = new Date(issuedAt.getTime() + DEFAULT_TOKEN_LIFETIME_MS),\n } = params;\n\n const localUserSecret = resolveLocalUserSecret(params.localUserSecret);\n const material = deriveClientMaterial(localUserSecret);\n\n const token = signStorageAccessToken({\n material,\n operations,\n presenter,\n sessionId,\n issuedAt,\n expiresAt,\n });\n\n const { storageId, signingPublicKey } = encodeClientMaterial(material);\n\n return {\n localUserSecret: bytesToHex(localUserSecret),\n storageId,\n signingPublicKey,\n token,\n authorizationHeader: `${UKYC_CAPABILITY_AUTH_SCHEME} ${encodeStorageAccessTokenForHeader(\n token,\n )}`,\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapEncryptionKey.d.ts","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;GAWG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,iBAAiB,CAC/B,uBAAuB,EAAE,UAAU,EACnC,sBAAsB,EAAE,MAAM,EAC9B,SAAS,EAAE,UAAU,GACpB,yBAAyB,CAmB3B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapEncryptionKey.js","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAE7C,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA0B/D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,uBAAmC,EACnC,sBAA8B,EAC9B,SAAqB;IAErB,MAAM,eAAe,GAAG,gBAAgB,CAAC,sBAAsB,CAAC,CAAC;IACjE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,GAAG,CAAC,OAAO,CAAC,aAAa,CAC9D,uBAAuB,CACxB,CAAC;IACF,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,GAAG,CACpB,SAAS,EACT,KAAK,EACL,eAAe,EACf,uBAAuB,CACxB,CAAC;IACF,MAAM,IAAI,GAAG,IAAI,UAAU,CAAC,eAAe,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IACxE,IAAI,CAAC,GAAG,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IAC7B,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,CAAC,MAAM,CAAC,CAAC;IAC7C,OAAO;QACL,IAAI,EAAE,WAAW,CAAC,IAAI,CAAC;QACvB,KAAK,EAAE,WAAW,CAAC,KAAK,CAAC;KAC1B,CAAC;AACJ,CAAC","sourcesContent":["import { randomBytes, box } from 'tweetnacl';\n\nimport { base64UrlToBytes, toBase64Url } from '../encoding.js';\n\n/**\n * Wraps a secret for the UKYC session server using NaCl's `crypto_box`\n * (X25519 + XSalsa20-Poly1305) established with a per-secret wrapping key\n * returned inside an encryption schema from `createUkycSession`.\n *\n * Unlike {@link wrapUserKey} (which generates a fresh ephemeral keypair per\n * call), this reuses a session client keypair. There is no prior wrapping-key\n * exchange to register that public key, so `data` is\n * `clientPublicKey (32) || ciphertext+tag` and only `{ data, nonce }` need be\n * transmitted. Used for both the `data_encryption_key` and the\n * `ukyc_capability_token`.\n */\n\n/**\n * The transmitted portion of a wrapped secret: the sender public key prefixed\n * to the `crypto_box` ciphertext (which includes the 16-byte Poly1305 auth\n * tag), and the nonce, both unpadded base64url-encoded. Matches the KYC API\n * `CapabilityAuthorization` wire shape.\n */\nexport type WrappedEncryptionKeyParts = {\n data: string;\n nonce: string;\n};\n\n/**\n * Wraps `plaintext` for the UKYC session server.\n *\n * The box is sealed with NaCl's `crypto_box`, keyed by the X25519 shared secret\n * between our session client private key and the session server public key\n * from an encryption schema (`encryptionDataKey` or `ukycCapabilityToken`)\n * returned by `createUkycSession`. The 32-byte client public key is prefixed\n * onto `data` so the server can open the box without a prior key-registration\n * step.\n *\n * @param sessionClientPrivateKey - Our session's X25519 private key.\n * @param sessionServerPublicKey - The server's X25519 public key (base64url).\n * @param plaintext - The raw bytes to encrypt (e.g. the `data_encryption_key`\n * or the encoded `ukyc_capability_token`).\n * @returns The base64url `data` (`clientPublicKey || ciphertext+tag`) and\n * `nonce`.\n */\nexport function wrapEncryptionKey(\n sessionClientPrivateKey: Uint8Array,\n sessionServerPublicKey: string,\n plaintext: Uint8Array,\n): WrappedEncryptionKeyParts {\n const serverPublicKey = base64UrlToBytes(sessionServerPublicKey);\n const { publicKey: clientPublicKey } = box.keyPair.fromSecretKey(\n sessionClientPrivateKey,\n );\n const nonce = randomBytes(box.nonceLength);\n const ciphertext = box(\n plaintext,\n nonce,\n serverPublicKey,\n sessionClientPrivateKey,\n );\n const data = new Uint8Array(clientPublicKey.length + ciphertext.length);\n data.set(clientPublicKey, 0);\n data.set(ciphertext, clientPublicKey.length);\n return {\n data: toBase64Url(data),\n nonce: toBase64Url(nonce),\n };\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapUserKey.d.ts","sourceRoot":"","sources":["../../src/ukyc/wrapUserKey.ts"],"names":[],"mappings":"AAqDA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,WAAW,CACzB,kBAAkB,EAAE,MAAM,EAC1B,SAAS,EAAE,UAAU,GACpB,MAAM,CAoBR"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrapUserKey.js","sourceRoot":"","sources":["../../src/ukyc/wrapUserKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AACzC,OAAO,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAC/C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAC;AAC1C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAE9D,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAE7C;;;;GAIG;AAEH,wEAAwE;AACxE,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC,kDAAkD;AAClD,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB;;;;;GAKG;AACH,SAAS,eAAe,CAAC,kBAA0B;IACjD,MAAM,KAAK,GACT,kBAAkB,CAAC,MAAM,KAAK,qBAAqB,GAAG,CAAC;QACvD,iBAAiB,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IAE7C,MAAM,KAAK,GAAG,KAAK;QACjB,CAAC,CAAC,UAAU,CAAC,kBAAkB,CAAC;QAChC,CAAC,CAAC,aAAa,CACX,kBAAkB;aACf,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,MAAM,CACL,kBAAkB,CAAC,MAAM;YACvB,CAAC,CAAC,CAAC,GAAG,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,EAC7C,GAAG,CACJ,CACJ,CAAC;IAEN,IAAI,KAAK,CAAC,MAAM,KAAK,qBAAqB,EAAE,CAAC;QAC3C,MAAM,IAAI,KAAK,CACb,iDAAiD,KAAK,CAAC,MAAM,cAAc,qBAAqB,GAAG,CACpG,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,WAAW,CACzB,kBAA0B,EAC1B,SAAqB;IAErB,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAEtD,MAAM,mBAAmB,GAAG,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;IAC3D,MAAM,kBAAkB,GAAG,MAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAEpE,MAAM,MAAM,GAAG,MAAM,CAAC,eAAe,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAE/D,MAAM,EAAE,GAAG,WAAW,CAAC,aAAa,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAEvD,MAAM,IAAI,GAAG,IAAI,UAAU,CACzB,kBAAkB,CAAC,MAAM,GAAG,EAAE,CAAC,MAAM,GAAG,UAAU,CAAC,MAAM,CAC1D,CAAC;IACF,IAAI,CAAC,GAAG,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,kBAAkB,CAAC,MAAM,CAAC,CAAC;IACxC,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,kBAAkB,CAAC,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,CAAC;IAE5D,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC","sourcesContent":["import { base64ToBytes } from '@metamask/utils';\nimport { gcm } from '@noble/ciphers/aes';\nimport { x25519 } from '@noble/curves/ed25519';\nimport { hkdf } from '@noble/hashes/hkdf';\nimport { sha256 } from '@noble/hashes/sha2';\nimport { hexToBytes, randomBytes } from '@noble/hashes/utils';\n\nimport { toBase64Url } from '../encoding.js';\n\n/**\n * Wraps (encrypts) a symmetric key so that only the holder of a given X25519\n * private key can recover it — used to seal the `data_encryption_key` for the\n * idOS Relay before it is handed to the UKYC API as `wrappedUserKey`.\n */\n\n/** X25519 public/private keys and AES-256-GCM keys are all 32 bytes. */\nconst X25519_KEY_SIZE_BYTES = 32;\n\n/** 96-bit IV, the AES-GCM standard nonce size. */\nconst IV_SIZE_BYTES = 12;\n\n/**\n * Decodes an X25519 public key presented as either hex or base64/base64url.\n *\n * @param recipientPublicKey - The recipient's 32-byte X25519 public key.\n * @returns The raw 32-byte public key.\n */\nfunction decodePublicKey(recipientPublicKey: string): Uint8Array {\n const isHex =\n recipientPublicKey.length === X25519_KEY_SIZE_BYTES * 2 &&\n /^[0-9a-fA-F]+$/u.test(recipientPublicKey);\n\n const bytes = isHex\n ? hexToBytes(recipientPublicKey)\n : base64ToBytes(\n recipientPublicKey\n .replace(/-/gu, '+')\n .replace(/_/gu, '/')\n .padEnd(\n recipientPublicKey.length +\n ((4 - (recipientPublicKey.length % 4)) % 4),\n '=',\n ),\n );\n\n if (bytes.length !== X25519_KEY_SIZE_BYTES) {\n throw new Error(\n `UKYC: wrappingPublicKey has unexpected length ${bytes.length}, expected ${X25519_KEY_SIZE_BYTES}.`,\n );\n }\n return bytes;\n}\n\n/**\n * Wraps `keyToWrap` for the holder of `recipientPublicKey` using an\n * ephemeral-static ECDH + AES-256-GCM sealed-box scheme:\n *\n * ephemeral = fresh X25519 keypair (one per call)\n * shared = X25519(ephemeralPrivate, recipientPublic)\n * aeadKey = HKDF-SHA256(shared, 32 bytes)\n * iv = 12 random bytes\n * ct = AES-256-GCM(aeadKey, iv).encrypt(keyToWrap) // ct includes tag\n *\n * The recipient reverses it with their private key:\n *\n * shared = X25519(recipientPrivate, ephemeralPublic)\n * aeadKey = HKDF-SHA256(shared, 32 bytes)\n * key = AES-256-GCM(aeadKey, iv).decrypt(ct)\n *\n * This mirrors the X25519 + AES-256-GCM/HKDF decryption used for MoonPay\n * Check/Auth-frame credentials, so both directions share one primitive.\n *\n * @param recipientPublicKey - The recipient's X25519 public key (hex or base64).\n * @param keyToWrap - The raw symmetric key bytes to encrypt (e.g. the\n * `data_encryption_key`).\n * @returns Base64url of `ephemeralPublicKey(32) || iv(12) || ciphertext+tag`.\n */\nexport function wrapUserKey(\n recipientPublicKey: string,\n keyToWrap: Uint8Array,\n): string {\n const recipient = decodePublicKey(recipientPublicKey);\n\n const ephemeralPrivateKey = x25519.utils.randomSecretKey();\n const ephemeralPublicKey = x25519.getPublicKey(ephemeralPrivateKey);\n\n const shared = x25519.getSharedSecret(ephemeralPrivateKey, recipient);\n const aeadKey = hkdf(sha256, shared, undefined, undefined, 32);\n\n const iv = randomBytes(IV_SIZE_BYTES);\n const ciphertext = gcm(aeadKey, iv).encrypt(keyToWrap);\n\n const blob = new Uint8Array(\n ephemeralPublicKey.length + iv.length + ciphertext.length,\n );\n blob.set(ephemeralPublicKey, 0);\n blob.set(iv, ephemeralPublicKey.length);\n blob.set(ciphertext, ephemeralPublicKey.length + iv.length);\n\n return toBase64Url(blob);\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrappedRelayPayload.d.ts","sourceRoot":"","sources":["../../src/ukyc/wrappedRelayPayload.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AAEtE;;;;;;;;;GASG;AAEH;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG;IAGpC,UAAU,EAAE,MAAM,CAAC;IACnB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,oBAAoB,EAAE,sBAAsB,CAAC;CAE9C,CAAC;AAEF;;;;;;;;;;GAUG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,EAAE,kBAAkB,EAC5B,kBAAkB,EAAE,sBAAsB,GACzC,uBAAuB,CAqBzB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"wrappedRelayPayload.js","sourceRoot":"","sources":["../../src/ukyc/wrappedRelayPayload.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AA6B7C;;;;;;;;;;GAUG;AACH,MAAM,UAAU,wBAAwB,CACtC,QAA4B,EAC5B,kBAA0C;IAE1C,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,kBAAkB,CAAC,OAAO,CAAC;IAE7D,IAAI,SAAS,KAAK,YAAY,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CACb,8EAA8E,CAC/E,CAAC;IACJ,CAAC;IAED,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,kFAAkF,CACnF,CAAC;IACJ,CAAC;IAED,OAAO;QACL,UAAU,EAAE,WAAW,CAAC,QAAQ,CAAC,SAAS,CAAC;QAC3C,mBAAmB,EAAE,WAAW,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAC5D,kBAAkB,EAAE,WAAW,CAAC,QAAQ,CAAC,gBAAgB,CAAC;QAC1D,oBAAoB,EAAE,kBAAkB;KACzC,CAAC;AACJ,CAAC","sourcesContent":["import { toBase64Url } from '../encoding.js';\nimport type { UkycClientMaterial } from './deriveClientMaterial.js';\nimport type { UkycStorageAccessToken } from './storageAccessToken.js';\n\n/**\n * Builds the `wrapped_relay_payload` — the only client-derived material that\n * leaves the device. It is sent to the idOS Relay (through the UKYC API) so the\n * Relay can encrypt/decrypt and store KYC payloads on the user's behalf. See the\n * architecture doc, section \"Client-Derived Material\".\n *\n * Neither `local_user_secret` nor the private `signing_key` are ever included.\n * The `data_encryption_key` *is* included: it is intentionally shared with the\n * Relay so it can encrypt/decrypt payloads transiently.\n */\n\n/**\n * The Relay-facing bundle. `data_encryption_key` is base64url-encoded secret\n * material shared with the Relay; the other fields are non-secret.\n */\nexport type UkycWrappedRelayPayload = {\n // Wire-shape fields are snake_case.\n /* eslint-disable @typescript-eslint/naming-convention */\n storage_id: string;\n data_encryption_key: string;\n signing_public_key: string;\n storage_access_token: UkycStorageAccessToken;\n /* eslint-enable @typescript-eslint/naming-convention */\n};\n\n/**\n * Assembles the `wrapped_relay_payload` from derived client material and a\n * Relay-presented `storage_access_token`.\n *\n * The token must be scoped for the Relay to present (`presenter: 'idos-relay'`)\n * and must not authorize `delete`, which is never delegated to the Relay.\n *\n * @param material - Client material derived from `local_user_secret`.\n * @param storageAccessToken - A `read`/`write`-scoped, Relay-presented token.\n * @returns The `wrapped_relay_payload` to send to the Relay via the UKYC API.\n */\nexport function buildWrappedRelayPayload(\n material: UkycClientMaterial,\n storageAccessToken: UkycStorageAccessToken,\n): UkycWrappedRelayPayload {\n const { presenter, operations } = storageAccessToken.payload;\n\n if (presenter !== 'idos-relay') {\n throw new Error(\n 'UKYC: wrapped_relay_payload requires a Relay-presented storage_access_token.',\n );\n }\n\n if (operations.includes('delete')) {\n throw new Error(\n 'UKYC: wrapped_relay_payload must not carry a delete-scoped storage_access_token.',\n );\n }\n\n return {\n storage_id: toBase64Url(material.storageId),\n data_encryption_key: toBase64Url(material.dataEncryptionKey),\n signing_public_key: toBase64Url(material.signingPublicKey),\n storage_access_token: storageAccessToken,\n };\n}\n"]}