@metamask-previews/kyc-controller 0.0.0-preview-eb263b0ee → 0.0.0-preview-c61613ed2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/dist/KycController-method-action-types.cjs.map +1 -1
- package/dist/KycController-method-action-types.d.cts +4 -2
- package/dist/KycController-method-action-types.d.cts.map +1 -1
- package/dist/KycController-method-action-types.d.mts +4 -2
- package/dist/KycController-method-action-types.d.mts.map +1 -1
- package/dist/KycController-method-action-types.mjs.map +1 -1
- package/dist/KycController.cjs +35 -10
- package/dist/KycController.cjs.map +1 -1
- package/dist/KycController.d.cts +4 -2
- package/dist/KycController.d.cts.map +1 -1
- package/dist/KycController.d.mts +4 -2
- package/dist/KycController.d.mts.map +1 -1
- package/dist/KycController.mjs +35 -10
- package/dist/KycController.mjs.map +1 -1
- package/dist/KycService-method-action-types.cjs.map +1 -1
- package/dist/KycService-method-action-types.d.cts +25 -8
- package/dist/KycService-method-action-types.d.cts.map +1 -1
- package/dist/KycService-method-action-types.d.mts +25 -8
- package/dist/KycService-method-action-types.d.mts.map +1 -1
- package/dist/KycService-method-action-types.mjs.map +1 -1
- package/dist/KycService.cjs +65 -29
- package/dist/KycService.cjs.map +1 -1
- package/dist/KycService.d.cts +48 -16
- package/dist/KycService.d.cts.map +1 -1
- package/dist/KycService.d.mts +48 -16
- package/dist/KycService.d.mts.map +1 -1
- package/dist/KycService.mjs +65 -29
- package/dist/KycService.mjs.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs.map +1 -1
- package/dist/ukyc/constants.cjs +2 -2
- package/dist/ukyc/constants.cjs.map +1 -1
- package/dist/ukyc/constants.d.cts +2 -2
- package/dist/ukyc/constants.d.mts +2 -2
- package/dist/ukyc/constants.mjs +2 -2
- package/dist/ukyc/constants.mjs.map +1 -1
- package/dist/ukyc/jwtChain.cjs +1 -1
- package/dist/ukyc/jwtChain.cjs.map +1 -1
- package/dist/ukyc/jwtChain.d.cts +4 -4
- package/dist/ukyc/jwtChain.d.mts +4 -4
- package/dist/ukyc/jwtChain.mjs +1 -1
- package/dist/ukyc/jwtChain.mjs.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.cjs +2 -3
- package/dist/ukyc/wrapEncryptionKey.cjs.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.d.cts +6 -9
- package/dist/ukyc/wrapEncryptionKey.d.cts.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.d.mts +6 -9
- package/dist/ukyc/wrapEncryptionKey.d.mts.map +1 -1
- package/dist/ukyc/wrapEncryptionKey.mjs +2 -3
- package/dist/ukyc/wrapEncryptionKey.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/ukyc/wrapUserKey.cjs +0 -80
- package/dist/ukyc/wrapUserKey.cjs.map +0 -1
- package/dist/ukyc/wrapUserKey.d.cts +0 -26
- package/dist/ukyc/wrapUserKey.d.cts.map +0 -1
- package/dist/ukyc/wrapUserKey.d.mts +0 -26
- package/dist/ukyc/wrapUserKey.d.mts.map +0 -1
- package/dist/ukyc/wrapUserKey.mjs +0 -76
- package/dist/ukyc/wrapUserKey.mjs.map +0 -1
package/dist/ukyc/constants.mjs
CHANGED
|
@@ -65,8 +65,8 @@ export const UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES = [
|
|
|
65
65
|
*/
|
|
66
66
|
export const UKYC_CAPABILITY_AUTH_SCHEME = 'AccessToken';
|
|
67
67
|
/**
|
|
68
|
-
* Standard well-known path where the
|
|
69
|
-
*
|
|
68
|
+
* Standard well-known path where the idOS enclave and the idOS relay publish their JWKS
|
|
69
|
+
* (the Ed25519 public keys used to sign encryption-schema `jwtChain`s).
|
|
70
70
|
*/
|
|
71
71
|
export const UKYC_JWKS_PATH = '/.well-known/jwks.json';
|
|
72
72
|
//# sourceMappingURL=constants.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../src/ukyc/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAiC,CAAC;AAE7E;;;;GAIG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,EAAE;IACb,iBAAiB,EAAE,EAAE;IACrB,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,EAAE;CACV,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS,EAAE,qCAAqC;IAChD,iBAAiB,EAAE,8CAA8C;IACjE,UAAU,EAAE,sCAAsC;IAClD,cAAc,EAAE,2CAA2C;CACnD,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAC7C,4BAAqC,CAAC;AAExC;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAoB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mCAAmC,GAAG;IACjD,kCAAkC;IAClC,kBAAkB;CACV,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,aAAsB,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC","sourcesContent":["/**\n * Constants for the UKYC client-derived key material and storage-authorization\n * layer. See the architecture doc, section \"Client-Derived Material\".\n */\n\n/**\n * Fully-qualified key path for the `local_user_secret` in Encrypted User\n * Storage.\n */\nexport const UKYC_LOCAL_USER_SECRET_PATH = `ukyc.local_user_secret` as const;\n\n/**\n * Size of the `local_user_secret` in bytes. 32 bytes (256 bits) provides high\n * entropy and matches the input length expected by the HKDF-SHA256 derivations\n * below.\n */\nexport const UKYC_LOCAL_USER_SECRET_SIZE_BYTES = 32;\n\n/**\n * Byte length of each value derived from `local_user_secret`.\n *\n * `signingKey` is 32 bytes because it is used directly as the Ed25519\n * private key (for Ed25519 the 32-byte seed *is* the private key).\n */\nexport const UKYC_DERIVED_KEY_SIZES = {\n storageId: 32,\n dataEncryptionKey: 32,\n signingKey: 32,\n relayTunnelKey: 32,\n} as const;\n\n/**\n * HKDF `info` labels providing domain separation between the values derived\n * from `local_user_secret`.\n */\nexport const UKYC_KDF_INFO = {\n storageId: 'metamask.ukyc.storage.v1.storage_id',\n dataEncryptionKey: 'metamask.ukyc.storage.v1.data_encryption_key',\n signingKey: 'metamask.ukyc.storage.v1.signing_key',\n relayTunnelKey: 'metamask.ukyc.storage.v1.relay_tunnel_key',\n} as const;\n\n/**\n * Version bound into every `storage_access_token` payload.\n */\nexport const UKYC_STORAGE_ACCESS_TOKEN_VERSION = 1;\n\n/**\n * Audience identifying the UKYC user-storage service. Required by UKYC Storage\n * when it verifies a `storage_access_token`.\n */\nexport const UKYC_STORAGE_ACCESS_TOKEN_AUDIENCE =\n 'metamask:user-storage:ukyc' as const;\n\n/**\n * Audience identifying the idOS Kwil credential-registry nodes. Required by\n * idOS Kwil when it verifies a `storage_access_token`.\n */\nexport const UKYC_KWIL_AUDIENCE = 'idos:kwil' as const;\n\n/**\n * Full audience list bound into every `storage_access_token` payload. `aud`\n * lists every verifier that may accept the token, so both UKYC Storage and\n * idOS Kwil can each find their own entry.\n */\nexport const UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES = [\n UKYC_STORAGE_ACCESS_TOKEN_AUDIENCE,\n UKYC_KWIL_AUDIENCE,\n] as const;\n\n/**\n * Authorization scheme under which a signed `storage_access_token` envelope is\n * carried to UKYC Storage: `Authorization: AccessToken <base64url(envelope)>`.\n * The credentials portion is what `encodeStorageAccessTokenForHeader` returns.\n */\nexport const UKYC_CAPABILITY_AUTH_SCHEME = 'AccessToken' as const;\n\n/**\n * Standard well-known path where the
|
|
1
|
+
{"version":3,"file":"constants.mjs","sourceRoot":"","sources":["../../src/ukyc/constants.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,wBAAiC,CAAC;AAE7E;;;;GAIG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,EAAE,CAAC;AAEpD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,SAAS,EAAE,EAAE;IACb,iBAAiB,EAAE,EAAE;IACrB,UAAU,EAAE,EAAE;IACd,cAAc,EAAE,EAAE;CACV,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG;IAC3B,SAAS,EAAE,qCAAqC;IAChD,iBAAiB,EAAE,8CAA8C;IACjE,UAAU,EAAE,sCAAsC;IAClD,cAAc,EAAE,2CAA2C;CACnD,CAAC;AAEX;;GAEG;AACH,MAAM,CAAC,MAAM,iCAAiC,GAAG,CAAC,CAAC;AAEnD;;;GAGG;AACH,MAAM,CAAC,MAAM,kCAAkC,GAC7C,4BAAqC,CAAC;AAExC;;;GAGG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,WAAoB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,CAAC,MAAM,mCAAmC,GAAG;IACjD,kCAAkC;IAClC,kBAAkB;CACV,CAAC;AAEX;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,aAAsB,CAAC;AAElE;;;GAGG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG,wBAAwB,CAAC","sourcesContent":["/**\n * Constants for the UKYC client-derived key material and storage-authorization\n * layer. See the architecture doc, section \"Client-Derived Material\".\n */\n\n/**\n * Fully-qualified key path for the `local_user_secret` in Encrypted User\n * Storage.\n */\nexport const UKYC_LOCAL_USER_SECRET_PATH = `ukyc.local_user_secret` as const;\n\n/**\n * Size of the `local_user_secret` in bytes. 32 bytes (256 bits) provides high\n * entropy and matches the input length expected by the HKDF-SHA256 derivations\n * below.\n */\nexport const UKYC_LOCAL_USER_SECRET_SIZE_BYTES = 32;\n\n/**\n * Byte length of each value derived from `local_user_secret`.\n *\n * `signingKey` is 32 bytes because it is used directly as the Ed25519\n * private key (for Ed25519 the 32-byte seed *is* the private key).\n */\nexport const UKYC_DERIVED_KEY_SIZES = {\n storageId: 32,\n dataEncryptionKey: 32,\n signingKey: 32,\n relayTunnelKey: 32,\n} as const;\n\n/**\n * HKDF `info` labels providing domain separation between the values derived\n * from `local_user_secret`.\n */\nexport const UKYC_KDF_INFO = {\n storageId: 'metamask.ukyc.storage.v1.storage_id',\n dataEncryptionKey: 'metamask.ukyc.storage.v1.data_encryption_key',\n signingKey: 'metamask.ukyc.storage.v1.signing_key',\n relayTunnelKey: 'metamask.ukyc.storage.v1.relay_tunnel_key',\n} as const;\n\n/**\n * Version bound into every `storage_access_token` payload.\n */\nexport const UKYC_STORAGE_ACCESS_TOKEN_VERSION = 1;\n\n/**\n * Audience identifying the UKYC user-storage service. Required by UKYC Storage\n * when it verifies a `storage_access_token`.\n */\nexport const UKYC_STORAGE_ACCESS_TOKEN_AUDIENCE =\n 'metamask:user-storage:ukyc' as const;\n\n/**\n * Audience identifying the idOS Kwil credential-registry nodes. Required by\n * idOS Kwil when it verifies a `storage_access_token`.\n */\nexport const UKYC_KWIL_AUDIENCE = 'idos:kwil' as const;\n\n/**\n * Full audience list bound into every `storage_access_token` payload. `aud`\n * lists every verifier that may accept the token, so both UKYC Storage and\n * idOS Kwil can each find their own entry.\n */\nexport const UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES = [\n UKYC_STORAGE_ACCESS_TOKEN_AUDIENCE,\n UKYC_KWIL_AUDIENCE,\n] as const;\n\n/**\n * Authorization scheme under which a signed `storage_access_token` envelope is\n * carried to UKYC Storage: `Authorization: AccessToken <base64url(envelope)>`.\n * The credentials portion is what `encodeStorageAccessTokenForHeader` returns.\n */\nexport const UKYC_CAPABILITY_AUTH_SCHEME = 'AccessToken' as const;\n\n/**\n * Standard well-known path where the idOS enclave and the idOS relay publish their JWKS\n * (the Ed25519 public keys used to sign encryption-schema `jwtChain`s).\n */\nexport const UKYC_JWKS_PATH = '/.well-known/jwks.json';\n"]}
|
package/dist/ukyc/jwtChain.cjs
CHANGED
|
@@ -24,7 +24,7 @@ function decodeJsonSegment(segment, label) {
|
|
|
24
24
|
* published Ed25519 signing key and checks the EdDSA signature over the
|
|
25
25
|
* `header.payload` input. Returns the decoded, verified payload.
|
|
26
26
|
*
|
|
27
|
-
* @param keys - The JWKS keys
|
|
27
|
+
* @param keys - The issuer JWKS keys used to verify the chain.
|
|
28
28
|
* @param jwtChain - The compact-serialized EdDSA JWT from an encryption schema.
|
|
29
29
|
* @returns The verified JWT payload.
|
|
30
30
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwtChain.cjs","sourceRoot":"","sources":["../../src/ukyc/jwtChain.ts"],"names":[],"mappings":";;;AAAA,2CAAgD;AAChD,mDAAgD;AAEhD,iDAAkD;AA0ClD;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAO,OAAe,EAAE,KAAa;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,qBAAa,EAAC,IAAA,8BAAgB,EAAC,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,SAAgB,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,iBAAO,CAAC,MAAM,CAC5B,IAAA,8BAAgB,EAAC,gBAAgB,CAAC,EAClC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,aAAa,IAAI,cAAc,EAAE,CAAC,EAC9D,IAAA,8BAAgB,EAAC,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;AAzCD,wCAyCC","sourcesContent":["import { bytesToString } from '@metamask/utils';\nimport { ed25519 } from '@noble/curves/ed25519';\n\nimport { base64UrlToBytes } from '../encoding.js';\n\n/**\n * Verifies
|
|
1
|
+
{"version":3,"file":"jwtChain.cjs","sourceRoot":"","sources":["../../src/ukyc/jwtChain.ts"],"names":[],"mappings":";;;AAAA,2CAAgD;AAChD,mDAAgD;AAEhD,iDAAkD;AA0ClD;;;;;;GAMG;AACH,SAAS,iBAAiB,CAAO,OAAe,EAAE,KAAa;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAA,qBAAa,EAAC,IAAA,8BAAgB,EAAC,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,SAAgB,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,iBAAO,CAAC,MAAM,CAC5B,IAAA,8BAAgB,EAAC,gBAAgB,CAAC,EAClC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,GAAG,aAAa,IAAI,cAAc,EAAE,CAAC,EAC9D,IAAA,8BAAgB,EAAC,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;AAzCD,wCAyCC","sourcesContent":["import { bytesToString } from '@metamask/utils';\nimport { ed25519 } from '@noble/curves/ed25519';\n\nimport { base64UrlToBytes } from '../encoding.js';\n\n/**\n * Verifies an encryption-schema `jwtChain` against the issuer's published JWKS\n * (idOS enclave for `encryptionDataKey`, idOS relay for `ukycCapabilityToken`).\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 an issuer 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 issuer JWKS keys used to verify the chain.\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"]}
|
package/dist/ukyc/jwtChain.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Verifies
|
|
3
|
-
*
|
|
2
|
+
* Verifies an encryption-schema `jwtChain` against the issuer's published JWKS
|
|
3
|
+
* (idOS enclave for `encryptionDataKey`, idOS relay for `ukycCapabilityToken`).
|
|
4
4
|
*
|
|
5
5
|
* The signature check is done with `@noble/curves` (rather than WebCrypto
|
|
6
6
|
* `subtle`) because not every MetaMask runtime exposes a `subtle`
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* no `jose` dependency is required.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* A single Ed25519 (OKP) JSON Web Key from
|
|
11
|
+
* A single Ed25519 (OKP) JSON Web Key from an issuer JWKS.
|
|
12
12
|
*/
|
|
13
13
|
export type Jwk = {
|
|
14
14
|
kty: string;
|
|
@@ -32,7 +32,7 @@ export type JwtChainPayload = {
|
|
|
32
32
|
* published Ed25519 signing key and checks the EdDSA signature over the
|
|
33
33
|
* `header.payload` input. Returns the decoded, verified payload.
|
|
34
34
|
*
|
|
35
|
-
* @param keys - The JWKS keys
|
|
35
|
+
* @param keys - The issuer JWKS keys used to verify the chain.
|
|
36
36
|
* @param jwtChain - The compact-serialized EdDSA JWT from an encryption schema.
|
|
37
37
|
* @returns The verified JWT payload.
|
|
38
38
|
*/
|
package/dist/ukyc/jwtChain.d.mts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Verifies
|
|
3
|
-
*
|
|
2
|
+
* Verifies an encryption-schema `jwtChain` against the issuer's published JWKS
|
|
3
|
+
* (idOS enclave for `encryptionDataKey`, idOS relay for `ukycCapabilityToken`).
|
|
4
4
|
*
|
|
5
5
|
* The signature check is done with `@noble/curves` (rather than WebCrypto
|
|
6
6
|
* `subtle`) because not every MetaMask runtime exposes a `subtle`
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* no `jose` dependency is required.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
|
-
* A single Ed25519 (OKP) JSON Web Key from
|
|
11
|
+
* A single Ed25519 (OKP) JSON Web Key from an issuer JWKS.
|
|
12
12
|
*/
|
|
13
13
|
export type Jwk = {
|
|
14
14
|
kty: string;
|
|
@@ -32,7 +32,7 @@ export type JwtChainPayload = {
|
|
|
32
32
|
* published Ed25519 signing key and checks the EdDSA signature over the
|
|
33
33
|
* `header.payload` input. Returns the decoded, verified payload.
|
|
34
34
|
*
|
|
35
|
-
* @param keys - The JWKS keys
|
|
35
|
+
* @param keys - The issuer JWKS keys used to verify the chain.
|
|
36
36
|
* @param jwtChain - The compact-serialized EdDSA JWT from an encryption schema.
|
|
37
37
|
* @returns The verified JWT payload.
|
|
38
38
|
*/
|
package/dist/ukyc/jwtChain.mjs
CHANGED
|
@@ -21,7 +21,7 @@ function decodeJsonSegment(segment, label) {
|
|
|
21
21
|
* published Ed25519 signing key and checks the EdDSA signature over the
|
|
22
22
|
* `header.payload` input. Returns the decoded, verified payload.
|
|
23
23
|
*
|
|
24
|
-
* @param keys - The JWKS keys
|
|
24
|
+
* @param keys - The issuer JWKS keys used to verify the chain.
|
|
25
25
|
* @param jwtChain - The compact-serialized EdDSA JWT from an encryption schema.
|
|
26
26
|
* @returns The verified JWT payload.
|
|
27
27
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jwtChain.mjs","sourceRoot":"","sources":["../../src/ukyc/jwtChain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,wBAAwB;AAChD,OAAO,EAAE,OAAO,EAAE,8BAA8B;AAEhD,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;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
|
|
1
|
+
{"version":3,"file":"jwtChain.mjs","sourceRoot":"","sources":["../../src/ukyc/jwtChain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,wBAAwB;AAChD,OAAO,EAAE,OAAO,EAAE,8BAA8B;AAEhD,OAAO,EAAE,gBAAgB,EAAE,wBAAuB;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 an encryption-schema `jwtChain` against the issuer's published JWKS\n * (idOS enclave for `encryptionDataKey`, idOS relay for `ukycCapabilityToken`).\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 an issuer 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 issuer JWKS keys used to verify the chain.\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"]}
|
|
@@ -9,9 +9,8 @@ const encoding_js_1 = require("../encoding.cjs");
|
|
|
9
9
|
* The box is sealed with NaCl's `crypto_box`, keyed by the X25519 shared secret
|
|
10
10
|
* between our session client private key and the session server public key
|
|
11
11
|
* from an encryption schema (`encryptionDataKey` or `ukycCapabilityToken`)
|
|
12
|
-
* returned by `createUkycSession`. The 32-byte client public key is
|
|
13
|
-
* onto `data` so the
|
|
14
|
-
* step.
|
|
12
|
+
* returned by `createUkycSession`. The 32-byte client public key is still
|
|
13
|
+
* prefixed onto `data` so the box is self-describing on the wire.
|
|
15
14
|
*
|
|
16
15
|
* @param sessionClientPrivateKey - Our session's X25519 private key.
|
|
17
16
|
* @param sessionServerPublicKey - The server's X25519 public key (base64url).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapEncryptionKey.cjs","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":";;;AAAA,yCAA6C;AAE7C,iDAA+D;
|
|
1
|
+
{"version":3,"file":"wrapEncryptionKey.cjs","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":";;;AAAA,yCAA6C;AAE7C,iDAA+D;AAwB/D;;;;;;;;;;;;;;;GAeG;AACH,SAAgB,iBAAiB,CAC/B,uBAAmC,EACnC,sBAA8B,EAC9B,SAAqB;IAErB,MAAM,eAAe,GAAG,IAAA,8BAAgB,EAAC,sBAAsB,CAAC,CAAC;IACjE,MAAM,EAAE,SAAS,EAAE,eAAe,EAAE,GAAG,eAAG,CAAC,OAAO,CAAC,aAAa,CAC9D,uBAAuB,CACxB,CAAC;IACF,MAAM,KAAK,GAAG,IAAA,uBAAW,EAAC,eAAG,CAAC,WAAW,CAAC,CAAC;IAC3C,MAAM,UAAU,GAAG,IAAA,eAAG,EACpB,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,IAAA,yBAAW,EAAC,IAAI,CAAC;QACvB,KAAK,EAAE,IAAA,yBAAW,EAAC,KAAK,CAAC;KAC1B,CAAC;AACJ,CAAC;AAvBD,8CAuBC","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 * Reuses a session client keypair whose public half is registered on\n * `createUkycSession`. `data` is still `clientPublicKey (32) || ciphertext+tag`\n * so the box can be opened from `{ data, nonce }` alone. Used for both the\n * `data_encryption_key` and the `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 still\n * prefixed onto `data` so the box is self-describing on the wire.\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"]}
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
* (X25519 + XSalsa20-Poly1305) established with a per-secret wrapping key
|
|
4
4
|
* returned inside an encryption schema from `createUkycSession`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
* transmitted. Used for both the `data_encryption_key` and the
|
|
11
|
-
* `ukyc_capability_token`.
|
|
6
|
+
* Reuses a session client keypair whose public half is registered on
|
|
7
|
+
* `createUkycSession`. `data` is still `clientPublicKey (32) || ciphertext+tag`
|
|
8
|
+
* so the box can be opened from `{ data, nonce }` alone. Used for both the
|
|
9
|
+
* `data_encryption_key` and the `ukyc_capability_token`.
|
|
12
10
|
*/
|
|
13
11
|
/**
|
|
14
12
|
* The transmitted portion of a wrapped secret: the sender public key prefixed
|
|
@@ -26,9 +24,8 @@ export type WrappedEncryptionKeyParts = {
|
|
|
26
24
|
* The box is sealed with NaCl's `crypto_box`, keyed by the X25519 shared secret
|
|
27
25
|
* between our session client private key and the session server public key
|
|
28
26
|
* from an encryption schema (`encryptionDataKey` or `ukycCapabilityToken`)
|
|
29
|
-
* returned by `createUkycSession`. The 32-byte client public key is
|
|
30
|
-
* onto `data` so the
|
|
31
|
-
* step.
|
|
27
|
+
* returned by `createUkycSession`. The 32-byte client public key is still
|
|
28
|
+
* prefixed onto `data` so the box is self-describing on the wire.
|
|
32
29
|
*
|
|
33
30
|
* @param sessionClientPrivateKey - Our session's X25519 private key.
|
|
34
31
|
* @param sessionServerPublicKey - The server's X25519 public key (base64url).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapEncryptionKey.d.cts","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"wrapEncryptionKey.d.cts","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,uBAAuB,EAAE,UAAU,EACnC,sBAAsB,EAAE,MAAM,EAC9B,SAAS,EAAE,UAAU,GACpB,yBAAyB,CAmB3B"}
|
|
@@ -3,12 +3,10 @@
|
|
|
3
3
|
* (X25519 + XSalsa20-Poly1305) established with a per-secret wrapping key
|
|
4
4
|
* returned inside an encryption schema from `createUkycSession`.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
10
|
-
* transmitted. Used for both the `data_encryption_key` and the
|
|
11
|
-
* `ukyc_capability_token`.
|
|
6
|
+
* Reuses a session client keypair whose public half is registered on
|
|
7
|
+
* `createUkycSession`. `data` is still `clientPublicKey (32) || ciphertext+tag`
|
|
8
|
+
* so the box can be opened from `{ data, nonce }` alone. Used for both the
|
|
9
|
+
* `data_encryption_key` and the `ukyc_capability_token`.
|
|
12
10
|
*/
|
|
13
11
|
/**
|
|
14
12
|
* The transmitted portion of a wrapped secret: the sender public key prefixed
|
|
@@ -26,9 +24,8 @@ export type WrappedEncryptionKeyParts = {
|
|
|
26
24
|
* The box is sealed with NaCl's `crypto_box`, keyed by the X25519 shared secret
|
|
27
25
|
* between our session client private key and the session server public key
|
|
28
26
|
* from an encryption schema (`encryptionDataKey` or `ukycCapabilityToken`)
|
|
29
|
-
* returned by `createUkycSession`. The 32-byte client public key is
|
|
30
|
-
* onto `data` so the
|
|
31
|
-
* step.
|
|
27
|
+
* returned by `createUkycSession`. The 32-byte client public key is still
|
|
28
|
+
* prefixed onto `data` so the box is self-describing on the wire.
|
|
32
29
|
*
|
|
33
30
|
* @param sessionClientPrivateKey - Our session's X25519 private key.
|
|
34
31
|
* @param sessionServerPublicKey - The server's X25519 public key (base64url).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapEncryptionKey.d.mts","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":"AAIA
|
|
1
|
+
{"version":3,"file":"wrapEncryptionKey.d.mts","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AAEH;;;;;GAKG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,iBAAiB,CAC/B,uBAAuB,EAAE,UAAU,EACnC,sBAAsB,EAAE,MAAM,EAC9B,SAAS,EAAE,UAAU,GACpB,yBAAyB,CAmB3B"}
|
|
@@ -7,9 +7,8 @@ import { base64UrlToBytes, toBase64Url } from "../encoding.mjs";
|
|
|
7
7
|
* The box is sealed with NaCl's `crypto_box`, keyed by the X25519 shared secret
|
|
8
8
|
* between our session client private key and the session server public key
|
|
9
9
|
* from an encryption schema (`encryptionDataKey` or `ukycCapabilityToken`)
|
|
10
|
-
* returned by `createUkycSession`. The 32-byte client public key is
|
|
11
|
-
* onto `data` so the
|
|
12
|
-
* step.
|
|
10
|
+
* returned by `createUkycSession`. The 32-byte client public key is still
|
|
11
|
+
* prefixed onto `data` so the box is self-describing on the wire.
|
|
13
12
|
*
|
|
14
13
|
* @param sessionClientPrivateKey - Our session's X25519 private key.
|
|
15
14
|
* @param sessionServerPublicKey - The server's X25519 public key (base64url).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wrapEncryptionKey.mjs","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":";;AAEA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,wBAAuB;
|
|
1
|
+
{"version":3,"file":"wrapEncryptionKey.mjs","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":";;AAEA,OAAO,EAAE,gBAAgB,EAAE,WAAW,EAAE,wBAAuB;AAwB/D;;;;;;;;;;;;;;;GAeG;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 * Reuses a session client keypair whose public half is registered on\n * `createUkycSession`. `data` is still `clientPublicKey (32) || ciphertext+tag`\n * so the box can be opened from `{ data, nonce }` alone. Used for both the\n * `data_encryption_key` and the `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 still\n * prefixed onto `data` so the box is self-describing on the wire.\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"]}
|
package/package.json
CHANGED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.wrapUserKey = void 0;
|
|
4
|
-
const utils_1 = require("@metamask/utils");
|
|
5
|
-
const aes_1 = require("@noble/ciphers/aes");
|
|
6
|
-
const ed25519_1 = require("@noble/curves/ed25519");
|
|
7
|
-
const hkdf_1 = require("@noble/hashes/hkdf");
|
|
8
|
-
const sha2_1 = require("@noble/hashes/sha2");
|
|
9
|
-
const utils_2 = require("@noble/hashes/utils");
|
|
10
|
-
const encoding_js_1 = require("../encoding.cjs");
|
|
11
|
-
/**
|
|
12
|
-
* Wraps (encrypts) a symmetric key so that only the holder of a given X25519
|
|
13
|
-
* private key can recover it — used to seal the `data_encryption_key` for the
|
|
14
|
-
* idOS Relay before it is handed to the UKYC API as `wrappedUserKey`.
|
|
15
|
-
*/
|
|
16
|
-
/** X25519 public/private keys and AES-256-GCM keys are all 32 bytes. */
|
|
17
|
-
const X25519_KEY_SIZE_BYTES = 32;
|
|
18
|
-
/** 96-bit IV, the AES-GCM standard nonce size. */
|
|
19
|
-
const IV_SIZE_BYTES = 12;
|
|
20
|
-
/**
|
|
21
|
-
* Decodes an X25519 public key presented as either hex or base64/base64url.
|
|
22
|
-
*
|
|
23
|
-
* @param recipientPublicKey - The recipient's 32-byte X25519 public key.
|
|
24
|
-
* @returns The raw 32-byte public key.
|
|
25
|
-
*/
|
|
26
|
-
function decodePublicKey(recipientPublicKey) {
|
|
27
|
-
const isHex = recipientPublicKey.length === X25519_KEY_SIZE_BYTES * 2 &&
|
|
28
|
-
/^[0-9a-fA-F]+$/u.test(recipientPublicKey);
|
|
29
|
-
const bytes = isHex
|
|
30
|
-
? (0, utils_2.hexToBytes)(recipientPublicKey)
|
|
31
|
-
: (0, utils_1.base64ToBytes)(recipientPublicKey
|
|
32
|
-
.replace(/-/gu, '+')
|
|
33
|
-
.replace(/_/gu, '/')
|
|
34
|
-
.padEnd(recipientPublicKey.length +
|
|
35
|
-
((4 - (recipientPublicKey.length % 4)) % 4), '='));
|
|
36
|
-
if (bytes.length !== X25519_KEY_SIZE_BYTES) {
|
|
37
|
-
throw new Error(`UKYC: wrappingPublicKey has unexpected length ${bytes.length}, expected ${X25519_KEY_SIZE_BYTES}.`);
|
|
38
|
-
}
|
|
39
|
-
return bytes;
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Wraps `keyToWrap` for the holder of `recipientPublicKey` using an
|
|
43
|
-
* ephemeral-static ECDH + AES-256-GCM sealed-box scheme:
|
|
44
|
-
*
|
|
45
|
-
* ephemeral = fresh X25519 keypair (one per call)
|
|
46
|
-
* shared = X25519(ephemeralPrivate, recipientPublic)
|
|
47
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
48
|
-
* iv = 12 random bytes
|
|
49
|
-
* ct = AES-256-GCM(aeadKey, iv).encrypt(keyToWrap) // ct includes tag
|
|
50
|
-
*
|
|
51
|
-
* The recipient reverses it with their private key:
|
|
52
|
-
*
|
|
53
|
-
* shared = X25519(recipientPrivate, ephemeralPublic)
|
|
54
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
55
|
-
* key = AES-256-GCM(aeadKey, iv).decrypt(ct)
|
|
56
|
-
*
|
|
57
|
-
* This mirrors the X25519 + AES-256-GCM/HKDF decryption used for MoonPay
|
|
58
|
-
* Check/Auth-frame credentials, so both directions share one primitive.
|
|
59
|
-
*
|
|
60
|
-
* @param recipientPublicKey - The recipient's X25519 public key (hex or base64).
|
|
61
|
-
* @param keyToWrap - The raw symmetric key bytes to encrypt (e.g. the
|
|
62
|
-
* `data_encryption_key`).
|
|
63
|
-
* @returns Base64url of `ephemeralPublicKey(32) || iv(12) || ciphertext+tag`.
|
|
64
|
-
*/
|
|
65
|
-
function wrapUserKey(recipientPublicKey, keyToWrap) {
|
|
66
|
-
const recipient = decodePublicKey(recipientPublicKey);
|
|
67
|
-
const ephemeralPrivateKey = ed25519_1.x25519.utils.randomSecretKey();
|
|
68
|
-
const ephemeralPublicKey = ed25519_1.x25519.getPublicKey(ephemeralPrivateKey);
|
|
69
|
-
const shared = ed25519_1.x25519.getSharedSecret(ephemeralPrivateKey, recipient);
|
|
70
|
-
const aeadKey = (0, hkdf_1.hkdf)(sha2_1.sha256, shared, undefined, undefined, 32);
|
|
71
|
-
const iv = (0, utils_2.randomBytes)(IV_SIZE_BYTES);
|
|
72
|
-
const ciphertext = (0, aes_1.gcm)(aeadKey, iv).encrypt(keyToWrap);
|
|
73
|
-
const blob = new Uint8Array(ephemeralPublicKey.length + iv.length + ciphertext.length);
|
|
74
|
-
blob.set(ephemeralPublicKey, 0);
|
|
75
|
-
blob.set(iv, ephemeralPublicKey.length);
|
|
76
|
-
blob.set(ciphertext, ephemeralPublicKey.length + iv.length);
|
|
77
|
-
return (0, encoding_js_1.toBase64Url)(blob);
|
|
78
|
-
}
|
|
79
|
-
exports.wrapUserKey = wrapUserKey;
|
|
80
|
-
//# sourceMappingURL=wrapUserKey.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wrapUserKey.cjs","sourceRoot":"","sources":["../../src/ukyc/wrapUserKey.ts"],"names":[],"mappings":";;;AAAA,2CAAgD;AAChD,4CAAyC;AACzC,mDAA+C;AAC/C,6CAA0C;AAC1C,6CAA4C;AAC5C,+CAA8D;AAE9D,iDAA6C;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,IAAA,kBAAU,EAAC,kBAAkB,CAAC;QAChC,CAAC,CAAC,IAAA,qBAAa,EACX,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,SAAgB,WAAW,CACzB,kBAA0B,EAC1B,SAAqB;IAErB,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,CAAC,CAAC;IAEtD,MAAM,mBAAmB,GAAG,gBAAM,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC;IAC3D,MAAM,kBAAkB,GAAG,gBAAM,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC;IAEpE,MAAM,MAAM,GAAG,gBAAM,CAAC,eAAe,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC;IACtE,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,aAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC;IAE/D,MAAM,EAAE,GAAG,IAAA,mBAAW,EAAC,aAAa,CAAC,CAAC;IACtC,MAAM,UAAU,GAAG,IAAA,SAAG,EAAC,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,IAAA,yBAAW,EAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAvBD,kCAuBC","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,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wraps `keyToWrap` for the holder of `recipientPublicKey` using an
|
|
3
|
-
* ephemeral-static ECDH + AES-256-GCM sealed-box scheme:
|
|
4
|
-
*
|
|
5
|
-
* ephemeral = fresh X25519 keypair (one per call)
|
|
6
|
-
* shared = X25519(ephemeralPrivate, recipientPublic)
|
|
7
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
8
|
-
* iv = 12 random bytes
|
|
9
|
-
* ct = AES-256-GCM(aeadKey, iv).encrypt(keyToWrap) // ct includes tag
|
|
10
|
-
*
|
|
11
|
-
* The recipient reverses it with their private key:
|
|
12
|
-
*
|
|
13
|
-
* shared = X25519(recipientPrivate, ephemeralPublic)
|
|
14
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
15
|
-
* key = AES-256-GCM(aeadKey, iv).decrypt(ct)
|
|
16
|
-
*
|
|
17
|
-
* This mirrors the X25519 + AES-256-GCM/HKDF decryption used for MoonPay
|
|
18
|
-
* Check/Auth-frame credentials, so both directions share one primitive.
|
|
19
|
-
*
|
|
20
|
-
* @param recipientPublicKey - The recipient's X25519 public key (hex or base64).
|
|
21
|
-
* @param keyToWrap - The raw symmetric key bytes to encrypt (e.g. the
|
|
22
|
-
* `data_encryption_key`).
|
|
23
|
-
* @returns Base64url of `ephemeralPublicKey(32) || iv(12) || ciphertext+tag`.
|
|
24
|
-
*/
|
|
25
|
-
export declare function wrapUserKey(recipientPublicKey: string, keyToWrap: Uint8Array): string;
|
|
26
|
-
//# sourceMappingURL=wrapUserKey.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wrapUserKey.d.cts","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,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Wraps `keyToWrap` for the holder of `recipientPublicKey` using an
|
|
3
|
-
* ephemeral-static ECDH + AES-256-GCM sealed-box scheme:
|
|
4
|
-
*
|
|
5
|
-
* ephemeral = fresh X25519 keypair (one per call)
|
|
6
|
-
* shared = X25519(ephemeralPrivate, recipientPublic)
|
|
7
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
8
|
-
* iv = 12 random bytes
|
|
9
|
-
* ct = AES-256-GCM(aeadKey, iv).encrypt(keyToWrap) // ct includes tag
|
|
10
|
-
*
|
|
11
|
-
* The recipient reverses it with their private key:
|
|
12
|
-
*
|
|
13
|
-
* shared = X25519(recipientPrivate, ephemeralPublic)
|
|
14
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
15
|
-
* key = AES-256-GCM(aeadKey, iv).decrypt(ct)
|
|
16
|
-
*
|
|
17
|
-
* This mirrors the X25519 + AES-256-GCM/HKDF decryption used for MoonPay
|
|
18
|
-
* Check/Auth-frame credentials, so both directions share one primitive.
|
|
19
|
-
*
|
|
20
|
-
* @param recipientPublicKey - The recipient's X25519 public key (hex or base64).
|
|
21
|
-
* @param keyToWrap - The raw symmetric key bytes to encrypt (e.g. the
|
|
22
|
-
* `data_encryption_key`).
|
|
23
|
-
* @returns Base64url of `ephemeralPublicKey(32) || iv(12) || ciphertext+tag`.
|
|
24
|
-
*/
|
|
25
|
-
export declare function wrapUserKey(recipientPublicKey: string, keyToWrap: Uint8Array): string;
|
|
26
|
-
//# sourceMappingURL=wrapUserKey.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wrapUserKey.d.mts","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,76 +0,0 @@
|
|
|
1
|
-
import { base64ToBytes } from "@metamask/utils";
|
|
2
|
-
import { gcm } from "@noble/ciphers/aes";
|
|
3
|
-
import { x25519 } from "@noble/curves/ed25519";
|
|
4
|
-
import { hkdf } from "@noble/hashes/hkdf";
|
|
5
|
-
import { sha256 } from "@noble/hashes/sha2";
|
|
6
|
-
import { hexToBytes, randomBytes } from "@noble/hashes/utils";
|
|
7
|
-
import { toBase64Url } from "../encoding.mjs";
|
|
8
|
-
/**
|
|
9
|
-
* Wraps (encrypts) a symmetric key so that only the holder of a given X25519
|
|
10
|
-
* private key can recover it — used to seal the `data_encryption_key` for the
|
|
11
|
-
* idOS Relay before it is handed to the UKYC API as `wrappedUserKey`.
|
|
12
|
-
*/
|
|
13
|
-
/** X25519 public/private keys and AES-256-GCM keys are all 32 bytes. */
|
|
14
|
-
const X25519_KEY_SIZE_BYTES = 32;
|
|
15
|
-
/** 96-bit IV, the AES-GCM standard nonce size. */
|
|
16
|
-
const IV_SIZE_BYTES = 12;
|
|
17
|
-
/**
|
|
18
|
-
* Decodes an X25519 public key presented as either hex or base64/base64url.
|
|
19
|
-
*
|
|
20
|
-
* @param recipientPublicKey - The recipient's 32-byte X25519 public key.
|
|
21
|
-
* @returns The raw 32-byte public key.
|
|
22
|
-
*/
|
|
23
|
-
function decodePublicKey(recipientPublicKey) {
|
|
24
|
-
const isHex = recipientPublicKey.length === X25519_KEY_SIZE_BYTES * 2 &&
|
|
25
|
-
/^[0-9a-fA-F]+$/u.test(recipientPublicKey);
|
|
26
|
-
const bytes = isHex
|
|
27
|
-
? hexToBytes(recipientPublicKey)
|
|
28
|
-
: base64ToBytes(recipientPublicKey
|
|
29
|
-
.replace(/-/gu, '+')
|
|
30
|
-
.replace(/_/gu, '/')
|
|
31
|
-
.padEnd(recipientPublicKey.length +
|
|
32
|
-
((4 - (recipientPublicKey.length % 4)) % 4), '='));
|
|
33
|
-
if (bytes.length !== X25519_KEY_SIZE_BYTES) {
|
|
34
|
-
throw new Error(`UKYC: wrappingPublicKey has unexpected length ${bytes.length}, expected ${X25519_KEY_SIZE_BYTES}.`);
|
|
35
|
-
}
|
|
36
|
-
return bytes;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Wraps `keyToWrap` for the holder of `recipientPublicKey` using an
|
|
40
|
-
* ephemeral-static ECDH + AES-256-GCM sealed-box scheme:
|
|
41
|
-
*
|
|
42
|
-
* ephemeral = fresh X25519 keypair (one per call)
|
|
43
|
-
* shared = X25519(ephemeralPrivate, recipientPublic)
|
|
44
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
45
|
-
* iv = 12 random bytes
|
|
46
|
-
* ct = AES-256-GCM(aeadKey, iv).encrypt(keyToWrap) // ct includes tag
|
|
47
|
-
*
|
|
48
|
-
* The recipient reverses it with their private key:
|
|
49
|
-
*
|
|
50
|
-
* shared = X25519(recipientPrivate, ephemeralPublic)
|
|
51
|
-
* aeadKey = HKDF-SHA256(shared, 32 bytes)
|
|
52
|
-
* key = AES-256-GCM(aeadKey, iv).decrypt(ct)
|
|
53
|
-
*
|
|
54
|
-
* This mirrors the X25519 + AES-256-GCM/HKDF decryption used for MoonPay
|
|
55
|
-
* Check/Auth-frame credentials, so both directions share one primitive.
|
|
56
|
-
*
|
|
57
|
-
* @param recipientPublicKey - The recipient's X25519 public key (hex or base64).
|
|
58
|
-
* @param keyToWrap - The raw symmetric key bytes to encrypt (e.g. the
|
|
59
|
-
* `data_encryption_key`).
|
|
60
|
-
* @returns Base64url of `ephemeralPublicKey(32) || iv(12) || ciphertext+tag`.
|
|
61
|
-
*/
|
|
62
|
-
export function wrapUserKey(recipientPublicKey, keyToWrap) {
|
|
63
|
-
const recipient = decodePublicKey(recipientPublicKey);
|
|
64
|
-
const ephemeralPrivateKey = x25519.utils.randomSecretKey();
|
|
65
|
-
const ephemeralPublicKey = x25519.getPublicKey(ephemeralPrivateKey);
|
|
66
|
-
const shared = x25519.getSharedSecret(ephemeralPrivateKey, recipient);
|
|
67
|
-
const aeadKey = hkdf(sha256, shared, undefined, undefined, 32);
|
|
68
|
-
const iv = randomBytes(IV_SIZE_BYTES);
|
|
69
|
-
const ciphertext = gcm(aeadKey, iv).encrypt(keyToWrap);
|
|
70
|
-
const blob = new Uint8Array(ephemeralPublicKey.length + iv.length + ciphertext.length);
|
|
71
|
-
blob.set(ephemeralPublicKey, 0);
|
|
72
|
-
blob.set(iv, ephemeralPublicKey.length);
|
|
73
|
-
blob.set(ciphertext, ephemeralPublicKey.length + iv.length);
|
|
74
|
-
return toBase64Url(blob);
|
|
75
|
-
}
|
|
76
|
-
//# sourceMappingURL=wrapUserKey.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"wrapUserKey.mjs","sourceRoot":"","sources":["../../src/ukyc/wrapUserKey.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,wBAAwB;AAChD,OAAO,EAAE,GAAG,EAAE,2BAA2B;AACzC,OAAO,EAAE,MAAM,EAAE,8BAA8B;AAC/C,OAAO,EAAE,IAAI,EAAE,2BAA2B;AAC1C,OAAO,EAAE,MAAM,EAAE,2BAA2B;AAC5C,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,4BAA4B;AAE9D,OAAO,EAAE,WAAW,EAAE,wBAAuB;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"]}
|