@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.
- package/dist/KycController-method-action-types.cjs +7 -0
- package/dist/KycController-method-action-types.cjs.map +1 -0
- package/dist/{KycController-method-action-types.d.ts → KycController-method-action-types.d.cts} +2 -2
- package/dist/KycController-method-action-types.d.cts.map +1 -0
- package/dist/KycController-method-action-types.d.mts +237 -0
- package/dist/KycController-method-action-types.d.mts.map +1 -0
- package/dist/{KycController-method-action-types.js → KycController-method-action-types.mjs} +1 -1
- package/dist/KycController-method-action-types.mjs.map +1 -0
- package/dist/KycController.cjs +1843 -0
- package/dist/KycController.cjs.map +1 -0
- package/dist/{KycController.d.ts → KycController.d.cts} +9 -9
- package/dist/KycController.d.cts.map +1 -0
- package/dist/KycController.d.mts +393 -0
- package/dist/KycController.d.mts.map +1 -0
- package/dist/{KycController.js → KycController.mjs} +10 -10
- package/dist/KycController.mjs.map +1 -0
- package/dist/KycService-method-action-types.cjs +7 -0
- package/dist/KycService-method-action-types.cjs.map +1 -0
- package/dist/{KycService-method-action-types.d.ts → KycService-method-action-types.d.cts} +2 -2
- package/dist/KycService-method-action-types.d.cts.map +1 -0
- package/dist/KycService-method-action-types.d.mts +186 -0
- package/dist/KycService-method-action-types.d.mts.map +1 -0
- package/dist/{KycService-method-action-types.js → KycService-method-action-types.mjs} +1 -1
- package/dist/KycService-method-action-types.mjs.map +1 -0
- package/dist/KycService.cjs +628 -0
- package/dist/KycService.cjs.map +1 -0
- package/dist/{KycService.d.ts → KycService.d.cts} +12 -12
- package/dist/KycService.d.cts.map +1 -0
- package/dist/KycService.d.mts +494 -0
- package/dist/KycService.d.mts.map +1 -0
- package/dist/{KycService.js → KycService.mjs} +7 -7
- package/dist/{KycService.js.map → KycService.mjs.map} +1 -1
- package/dist/countryCodes.cjs +274 -0
- package/dist/countryCodes.cjs.map +1 -0
- package/dist/{countryCodes.d.ts → countryCodes.d.cts} +1 -1
- package/dist/countryCodes.d.cts.map +1 -0
- package/dist/countryCodes.d.mts +18 -0
- package/dist/countryCodes.d.mts.map +1 -0
- package/dist/{countryCodes.js → countryCodes.mjs} +1 -1
- package/dist/countryCodes.mjs.map +1 -0
- package/dist/crypto.cjs +154 -0
- package/dist/crypto.cjs.map +1 -0
- package/dist/{crypto.d.ts → crypto.d.cts} +1 -1
- package/dist/crypto.d.cts.map +1 -0
- package/dist/crypto.d.mts +84 -0
- package/dist/crypto.d.mts.map +1 -0
- package/dist/{crypto.js → crypto.mjs} +7 -7
- package/dist/crypto.mjs.map +1 -0
- package/dist/encoding.cjs +40 -0
- package/dist/encoding.cjs.map +1 -0
- package/dist/{encoding.d.ts → encoding.d.cts} +1 -1
- package/dist/encoding.d.cts.map +1 -0
- package/dist/encoding.d.mts +24 -0
- package/dist/encoding.d.mts.map +1 -0
- package/dist/{encoding.js → encoding.mjs} +2 -2
- package/dist/encoding.mjs.map +1 -0
- package/dist/index.cjs +37 -0
- package/dist/index.cjs.map +1 -0
- package/dist/{index.d.ts → index.d.cts} +19 -19
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +19 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +12 -0
- package/dist/index.mjs.map +1 -0
- package/dist/selectors.cjs +30 -0
- package/dist/selectors.cjs.map +1 -0
- package/dist/{selectors.d.ts → selectors.d.cts} +4 -4
- package/dist/selectors.d.cts.map +1 -0
- package/dist/selectors.d.mts +24 -0
- package/dist/selectors.d.mts.map +1 -0
- package/dist/{selectors.js → selectors.mjs} +2 -2
- package/dist/selectors.mjs.map +1 -0
- package/dist/types.cjs +10 -0
- package/dist/types.cjs.map +1 -0
- package/dist/{types.d.ts → types.d.cts} +1 -1
- package/dist/types.d.cts.map +1 -0
- package/dist/types.d.mts +208 -0
- package/dist/types.d.mts.map +1 -0
- package/dist/{types.js → types.mjs} +1 -1
- package/dist/types.mjs.map +1 -0
- package/dist/ukyc/constants.cjs +75 -0
- package/dist/ukyc/constants.cjs.map +1 -0
- package/dist/ukyc/{constants.d.ts → constants.d.cts} +9 -9
- package/dist/ukyc/constants.d.cts.map +1 -0
- package/dist/ukyc/constants.d.mts +69 -0
- package/dist/ukyc/constants.d.mts.map +1 -0
- package/dist/ukyc/{constants.js → constants.mjs} +1 -1
- package/dist/ukyc/constants.mjs.map +1 -0
- package/dist/ukyc/deriveClientMaterial.cjs +65 -0
- package/dist/ukyc/deriveClientMaterial.cjs.map +1 -0
- package/dist/ukyc/{deriveClientMaterial.d.ts → deriveClientMaterial.d.cts} +1 -1
- package/dist/ukyc/deriveClientMaterial.d.cts.map +1 -0
- package/dist/ukyc/deriveClientMaterial.d.mts +54 -0
- package/dist/ukyc/deriveClientMaterial.d.mts.map +1 -0
- package/dist/ukyc/{deriveClientMaterial.js → deriveClientMaterial.mjs} +7 -7
- package/dist/ukyc/deriveClientMaterial.mjs.map +1 -0
- package/dist/ukyc/jwtChain.cjs +54 -0
- package/dist/ukyc/jwtChain.cjs.map +1 -0
- package/dist/ukyc/{jwtChain.d.ts → jwtChain.d.cts} +1 -1
- package/dist/ukyc/jwtChain.d.cts.map +1 -0
- package/dist/ukyc/jwtChain.d.mts +40 -0
- package/dist/ukyc/jwtChain.d.mts.map +1 -0
- package/dist/ukyc/{jwtChain.js → jwtChain.mjs} +4 -4
- package/dist/ukyc/jwtChain.mjs.map +1 -0
- package/dist/ukyc/localUserSecret.cjs +98 -0
- package/dist/ukyc/localUserSecret.cjs.map +1 -0
- package/dist/ukyc/{localUserSecret.d.ts → localUserSecret.d.cts} +1 -1
- package/dist/ukyc/localUserSecret.d.cts.map +1 -0
- package/dist/ukyc/localUserSecret.d.mts +65 -0
- package/dist/ukyc/localUserSecret.d.mts.map +1 -0
- package/dist/ukyc/{localUserSecret.js → localUserSecret.mjs} +4 -4
- package/dist/ukyc/localUserSecret.mjs.map +1 -0
- package/dist/ukyc/storageAccessToken.cjs +139 -0
- package/dist/ukyc/storageAccessToken.cjs.map +1 -0
- package/dist/ukyc/{storageAccessToken.d.ts → storageAccessToken.d.cts} +2 -2
- package/dist/ukyc/storageAccessToken.d.cts.map +1 -0
- package/dist/ukyc/storageAccessToken.d.mts +99 -0
- package/dist/ukyc/storageAccessToken.d.mts.map +1 -0
- package/dist/ukyc/{storageAccessToken.js → storageAccessToken.mjs} +5 -5
- package/dist/ukyc/storageAccessToken.mjs.map +1 -0
- package/dist/ukyc/testToken.cjs +61 -0
- package/dist/ukyc/testToken.cjs.map +1 -0
- package/dist/ukyc/{testToken.d.ts → testToken.d.cts} +2 -2
- package/dist/ukyc/testToken.d.cts.map +1 -0
- package/dist/ukyc/testToken.d.mts +50 -0
- package/dist/ukyc/testToken.d.mts.map +1 -0
- package/dist/ukyc/{testToken.js → testToken.mjs} +5 -5
- package/dist/ukyc/testToken.mjs.map +1 -0
- package/dist/ukyc/wrapEncryptionKey.cjs +37 -0
- package/dist/ukyc/wrapEncryptionKey.cjs.map +1 -0
- package/dist/ukyc/{wrapEncryptionKey.d.ts → wrapEncryptionKey.d.cts} +1 -1
- package/dist/ukyc/wrapEncryptionKey.d.cts.map +1 -0
- package/dist/ukyc/wrapEncryptionKey.d.mts +41 -0
- package/dist/ukyc/wrapEncryptionKey.d.mts.map +1 -0
- package/dist/ukyc/{wrapEncryptionKey.js → wrapEncryptionKey.mjs} +4 -3
- package/dist/ukyc/wrapEncryptionKey.mjs.map +1 -0
- package/dist/ukyc/wrapUserKey.cjs +80 -0
- package/dist/ukyc/wrapUserKey.cjs.map +1 -0
- package/dist/ukyc/{wrapUserKey.d.ts → wrapUserKey.d.cts} +1 -1
- package/dist/ukyc/wrapUserKey.d.cts.map +1 -0
- package/dist/ukyc/wrapUserKey.d.mts +26 -0
- package/dist/ukyc/wrapUserKey.d.mts.map +1 -0
- package/dist/ukyc/{wrapUserKey.js → wrapUserKey.mjs} +8 -8
- package/dist/ukyc/wrapUserKey.mjs.map +1 -0
- package/dist/ukyc/wrappedRelayPayload.cjs +32 -0
- package/dist/ukyc/wrappedRelayPayload.cjs.map +1 -0
- package/dist/ukyc/{wrappedRelayPayload.d.ts → wrappedRelayPayload.d.cts} +3 -3
- package/dist/ukyc/wrappedRelayPayload.d.cts.map +1 -0
- package/dist/ukyc/wrappedRelayPayload.d.mts +35 -0
- package/dist/ukyc/wrappedRelayPayload.d.mts.map +1 -0
- package/dist/ukyc/{wrappedRelayPayload.js → wrappedRelayPayload.mjs} +2 -2
- package/dist/ukyc/wrappedRelayPayload.mjs.map +1 -0
- package/package.json +17 -13
- package/dist/KycController-method-action-types.d.ts.map +0 -1
- package/dist/KycController-method-action-types.js.map +0 -1
- package/dist/KycController.d.ts.map +0 -1
- package/dist/KycController.js.map +0 -1
- package/dist/KycService-method-action-types.d.ts.map +0 -1
- package/dist/KycService-method-action-types.js.map +0 -1
- package/dist/KycService.d.ts.map +0 -1
- package/dist/countryCodes.d.ts.map +0 -1
- package/dist/countryCodes.js.map +0 -1
- package/dist/crypto.d.ts.map +0 -1
- package/dist/crypto.js.map +0 -1
- package/dist/encoding.d.ts.map +0 -1
- package/dist/encoding.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -12
- package/dist/index.js.map +0 -1
- package/dist/selectors.d.ts.map +0 -1
- package/dist/selectors.js.map +0 -1
- package/dist/types.d.ts.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/ukyc/constants.d.ts.map +0 -1
- package/dist/ukyc/constants.js.map +0 -1
- package/dist/ukyc/deriveClientMaterial.d.ts.map +0 -1
- package/dist/ukyc/deriveClientMaterial.js.map +0 -1
- package/dist/ukyc/jwtChain.d.ts.map +0 -1
- package/dist/ukyc/jwtChain.js.map +0 -1
- package/dist/ukyc/localUserSecret.d.ts.map +0 -1
- package/dist/ukyc/localUserSecret.js.map +0 -1
- package/dist/ukyc/storageAccessToken.d.ts.map +0 -1
- package/dist/ukyc/storageAccessToken.js.map +0 -1
- package/dist/ukyc/testToken.d.ts.map +0 -1
- package/dist/ukyc/testToken.js.map +0 -1
- package/dist/ukyc/wrapEncryptionKey.d.ts.map +0 -1
- package/dist/ukyc/wrapEncryptionKey.js.map +0 -1
- package/dist/ukyc/wrapUserKey.d.ts.map +0 -1
- package/dist/ukyc/wrapUserKey.js.map +0 -1
- package/dist/ukyc/wrappedRelayPayload.d.ts.map +0 -1
- package/dist/ukyc/wrappedRelayPayload.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localUserSecret.d.mts","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,6 +1,6 @@
|
|
|
1
|
-
import { base64ToBytes, bytesToBase64 } from
|
|
2
|
-
import { randomBytes } from
|
|
3
|
-
import { UKYC_LOCAL_USER_SECRET_PATH, UKYC_LOCAL_USER_SECRET_SIZE_BYTES
|
|
1
|
+
import { base64ToBytes, bytesToBase64 } from "@metamask/utils";
|
|
2
|
+
import { randomBytes } from "@noble/hashes/utils";
|
|
3
|
+
import { UKYC_LOCAL_USER_SECRET_PATH, UKYC_LOCAL_USER_SECRET_SIZE_BYTES } from "./constants.mjs";
|
|
4
4
|
/**
|
|
5
5
|
* In-flight `getOrCreateLocalUserSecret` calls, keyed by entropy source.
|
|
6
6
|
* Deduplicates concurrent enrollments in a single client session so we never
|
|
@@ -89,4 +89,4 @@ export async function getOrCreateLocalUserSecret(store, entropySourceId) {
|
|
|
89
89
|
export async function hasLocalUserSecret(store, entropySourceId) {
|
|
90
90
|
return (await loadLocalUserSecret(store, entropySourceId)) !== null;
|
|
91
91
|
}
|
|
92
|
-
//# sourceMappingURL=localUserSecret.
|
|
92
|
+
//# sourceMappingURL=localUserSecret.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"localUserSecret.mjs","sourceRoot":"","sources":["../../src/ukyc/localUserSecret.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,wBAAwB;AAC/D,OAAO,EAAE,WAAW,EAAE,4BAA4B;AAElD,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EAClC,wBAAuB;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"]}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.encodeStorageAccessTokenForHeader = exports.signStorageAccessToken = exports.canonicalizeJson = void 0;
|
|
4
|
+
const utils_1 = require("@metamask/utils");
|
|
5
|
+
const ed25519_1 = require("@noble/curves/ed25519");
|
|
6
|
+
const encoding_js_1 = require("../encoding.cjs");
|
|
7
|
+
const constants_js_1 = require("./constants.cjs");
|
|
8
|
+
/**
|
|
9
|
+
* Serializes a JSON value to RFC 8785 (JCS) canonical form.
|
|
10
|
+
*
|
|
11
|
+
* Scope note: this implementation covers the JSON shapes used by UKYC storage
|
|
12
|
+
* payloads — objects, arrays, strings, integers, booleans, and null. Object
|
|
13
|
+
* members are sorted by their UTF-16 code units (matching JS default string
|
|
14
|
+
* ordering, which is what JCS requires) and `undefined` members are dropped.
|
|
15
|
+
* Non-finite and non-integer numbers are rejected, since the payloads never
|
|
16
|
+
* contain them and correct JCS number formatting for the general case is
|
|
17
|
+
* intentionally out of scope here.
|
|
18
|
+
*
|
|
19
|
+
* @param value - The value to canonicalize.
|
|
20
|
+
* @returns The canonical JSON string.
|
|
21
|
+
*/
|
|
22
|
+
function canonicalizeJson(value) {
|
|
23
|
+
if (value === null) {
|
|
24
|
+
return 'null';
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === 'boolean') {
|
|
27
|
+
return value ? 'true' : 'false';
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === 'number') {
|
|
30
|
+
if (!Number.isInteger(value)) {
|
|
31
|
+
throw new Error('UKYC: cannot canonicalize a non-integer number for JCS.');
|
|
32
|
+
}
|
|
33
|
+
return JSON.stringify(value);
|
|
34
|
+
}
|
|
35
|
+
if (typeof value === 'string') {
|
|
36
|
+
return JSON.stringify(value);
|
|
37
|
+
}
|
|
38
|
+
if (Array.isArray(value)) {
|
|
39
|
+
return `[${value.map((item) => canonicalizeJson(item)).join(',')}]`;
|
|
40
|
+
}
|
|
41
|
+
const entries = Object.keys(value)
|
|
42
|
+
.sort()
|
|
43
|
+
.reduce((acc, key) => {
|
|
44
|
+
const child = value[key];
|
|
45
|
+
if (child !== undefined) {
|
|
46
|
+
acc.push(`${JSON.stringify(key)}:${canonicalizeJson(child)}`);
|
|
47
|
+
}
|
|
48
|
+
return acc;
|
|
49
|
+
}, []);
|
|
50
|
+
return `{${entries.join(',')}}`;
|
|
51
|
+
}
|
|
52
|
+
exports.canonicalizeJson = canonicalizeJson;
|
|
53
|
+
/**
|
|
54
|
+
* Formats a date as RFC 3339 UTC with whole-second precision (e.g.
|
|
55
|
+
* `2026-07-07T00:00:00Z`). `Date.prototype.toISOString` always emits
|
|
56
|
+
* milliseconds (`...:00.000Z`); the `storage_access_token` wire format omits
|
|
57
|
+
* fractional seconds, so the sub-second component is truncated (not rounded).
|
|
58
|
+
*
|
|
59
|
+
* @param date - The date to format.
|
|
60
|
+
* @returns The RFC 3339 timestamp without fractional seconds.
|
|
61
|
+
*/
|
|
62
|
+
function toRfc3339Seconds(date) {
|
|
63
|
+
return `${date.toISOString().slice(0, 19)}Z`;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Builds and signs a `storage_access_token`.
|
|
67
|
+
*
|
|
68
|
+
* @param params - See {@link SignStorageAccessTokenParams}.
|
|
69
|
+
* @returns The signed token envelope.
|
|
70
|
+
*/
|
|
71
|
+
function signStorageAccessToken(params) {
|
|
72
|
+
const { material, operations, presenter = 'client', sessionId, issuedAt = new Date(), expiresAt, } = params;
|
|
73
|
+
assertValidOperations(operations);
|
|
74
|
+
if (expiresAt.getTime() <= issuedAt.getTime()) {
|
|
75
|
+
throw new Error('UKYC: storage_access_token expires_at must be after issued_at.');
|
|
76
|
+
}
|
|
77
|
+
const isDelete = operations.includes('delete');
|
|
78
|
+
if (presenter === 'idos-relay' && isDelete) {
|
|
79
|
+
throw new Error('UKYC: a delete-scoped storage_access_token cannot be delegated to the Relay.');
|
|
80
|
+
}
|
|
81
|
+
if (presenter === 'idos-relay' && !sessionId) {
|
|
82
|
+
throw new Error('UKYC: a Relay-presented storage_access_token requires a session_id.');
|
|
83
|
+
}
|
|
84
|
+
const payload = {
|
|
85
|
+
version: constants_js_1.UKYC_STORAGE_ACCESS_TOKEN_VERSION,
|
|
86
|
+
aud: [...constants_js_1.UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES],
|
|
87
|
+
storage_id: (0, encoding_js_1.toBase64Url)(material.storageId),
|
|
88
|
+
signing_public_key: (0, encoding_js_1.toBase64Url)(material.signingPublicKey),
|
|
89
|
+
operations,
|
|
90
|
+
presenter,
|
|
91
|
+
issued_at: toRfc3339Seconds(issuedAt),
|
|
92
|
+
expires_at: toRfc3339Seconds(expiresAt),
|
|
93
|
+
};
|
|
94
|
+
// Only bind session_id for Relay-presented tokens; omit the key entirely for
|
|
95
|
+
// client-presented tokens so it does not appear in the canonicalized payload.
|
|
96
|
+
if (presenter === 'idos-relay') {
|
|
97
|
+
payload.session_id = sessionId;
|
|
98
|
+
}
|
|
99
|
+
const message = (0, utils_1.stringToBytes)(canonicalizeJson(payload));
|
|
100
|
+
const signature = ed25519_1.ed25519.sign(message, material.signingKey);
|
|
101
|
+
return {
|
|
102
|
+
payload,
|
|
103
|
+
signature: (0, encoding_js_1.toBase64Url)(signature),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
exports.signStorageAccessToken = signStorageAccessToken;
|
|
107
|
+
/**
|
|
108
|
+
* Serializes a signed token to a compact string suitable for header transport
|
|
109
|
+
* (`Authorization: AccessToken <token>`, see `UKYC_CAPABILITY_AUTH_SCHEME`). The
|
|
110
|
+
* complete envelope is base64url-encoded; the private `signing_key` is never
|
|
111
|
+
* included.
|
|
112
|
+
*
|
|
113
|
+
* @param token - The signed token envelope.
|
|
114
|
+
* @returns The base64url-encoded envelope string.
|
|
115
|
+
*/
|
|
116
|
+
function encodeStorageAccessTokenForHeader(token) {
|
|
117
|
+
return (0, encoding_js_1.toBase64Url)((0, utils_1.stringToBytes)(JSON.stringify(token)));
|
|
118
|
+
}
|
|
119
|
+
exports.encodeStorageAccessTokenForHeader = encodeStorageAccessTokenForHeader;
|
|
120
|
+
/**
|
|
121
|
+
* Validates that an operations list is one storage understands: a non-empty set
|
|
122
|
+
* of `read`/`write`, or exactly `['delete']`. `delete` is never combined with
|
|
123
|
+
* other operations.
|
|
124
|
+
*
|
|
125
|
+
* @param operations - The requested operations.
|
|
126
|
+
*/
|
|
127
|
+
function assertValidOperations(operations) {
|
|
128
|
+
if (operations.length === 0) {
|
|
129
|
+
throw new Error('UKYC: storage_access_token requires at least one operation.');
|
|
130
|
+
}
|
|
131
|
+
const unique = new Set(operations);
|
|
132
|
+
if (unique.size !== operations.length) {
|
|
133
|
+
throw new Error('UKYC: storage_access_token operations must be unique.');
|
|
134
|
+
}
|
|
135
|
+
if (unique.has('delete') && operations.length > 1) {
|
|
136
|
+
throw new Error('UKYC: a delete-scoped storage_access_token must contain only "delete".');
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=storageAccessToken.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageAccessToken.cjs","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,4 +1,4 @@
|
|
|
1
|
-
import type { UkycClientMaterial } from
|
|
1
|
+
import type { UkycClientMaterial } from "./deriveClientMaterial.cjs";
|
|
2
2
|
/**
|
|
3
3
|
* Mints `storage_access_token` capabilities — the client-signed, scoped,
|
|
4
4
|
* session-bound proofs that authorize UKYC storage operations. See the
|
|
@@ -96,4 +96,4 @@ export declare function signStorageAccessToken(params: SignStorageAccessTokenPar
|
|
|
96
96
|
*/
|
|
97
97
|
export declare function encodeStorageAccessTokenForHeader(token: UkycStorageAccessToken): string;
|
|
98
98
|
export {};
|
|
99
|
-
//# sourceMappingURL=storageAccessToken.d.
|
|
99
|
+
//# sourceMappingURL=storageAccessToken.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageAccessToken.d.cts","sourceRoot":"","sources":["../../src/ukyc/storageAccessToken.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mCAAkC;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"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import type { UkycClientMaterial } from "./deriveClientMaterial.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Mints `storage_access_token` capabilities — the client-signed, scoped,
|
|
4
|
+
* session-bound proofs that authorize UKYC storage operations. See the
|
|
5
|
+
* architecture doc, section "Storage Authentication".
|
|
6
|
+
*
|
|
7
|
+
* The token is Ed25519 over RFC 8785 (JCS) canonical JSON of the payload. Only
|
|
8
|
+
* the client holds the private `signing_key`, so only the client can mint a
|
|
9
|
+
* token; a `read`/`write`-scoped token may then be handed to the Relay to
|
|
10
|
+
* present, but `delete` is never delegated.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Storage operations a `storage_access_token` can authorize.
|
|
14
|
+
*/
|
|
15
|
+
export type UkycStorageOperation = 'read' | 'write' | 'delete';
|
|
16
|
+
/**
|
|
17
|
+
* Who presents the token to UKYC storage. The Relay may only present
|
|
18
|
+
* `read`/`write` tokens; `delete` is always client-presented.
|
|
19
|
+
*/
|
|
20
|
+
export type UkycTokenPresenter = 'client' | 'idos-relay';
|
|
21
|
+
/**
|
|
22
|
+
* The signed `storage_access_token` payload. Field names are snake_case because
|
|
23
|
+
* they are canonicalized and hashed exactly as they appear on the wire.
|
|
24
|
+
*/
|
|
25
|
+
export type UkycStorageAccessTokenPayload = {
|
|
26
|
+
version: number;
|
|
27
|
+
/** Every verifier that may accept the token, e.g. UKYC Storage and idOS Kwil. */
|
|
28
|
+
aud: string[];
|
|
29
|
+
storage_id: string;
|
|
30
|
+
signing_public_key: string;
|
|
31
|
+
operations: UkycStorageOperation[];
|
|
32
|
+
presenter: UkycTokenPresenter;
|
|
33
|
+
/** UKYC session id. Required (and only present) when presenter is `idos-relay`. */
|
|
34
|
+
session_id?: string;
|
|
35
|
+
issued_at: string;
|
|
36
|
+
expires_at: string;
|
|
37
|
+
};
|
|
38
|
+
/**
|
|
39
|
+
* The on-the-wire envelope: the payload plus its detached Ed25519 signature
|
|
40
|
+
* (base64url) over the JCS canonicalization of the payload.
|
|
41
|
+
*/
|
|
42
|
+
export type UkycStorageAccessToken = {
|
|
43
|
+
payload: UkycStorageAccessTokenPayload;
|
|
44
|
+
signature: string;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Inputs for minting a `storage_access_token`.
|
|
48
|
+
*/
|
|
49
|
+
export type SignStorageAccessTokenParams = {
|
|
50
|
+
/** Client material derived from `local_user_secret`. */
|
|
51
|
+
material: UkycClientMaterial;
|
|
52
|
+
/** Operations the token authorizes. `delete` must be the sole operation. */
|
|
53
|
+
operations: UkycStorageOperation[];
|
|
54
|
+
/** Who will present the token. Defaults to `client`. */
|
|
55
|
+
presenter?: UkycTokenPresenter;
|
|
56
|
+
/** UKYC session id. Required when presenter is `idos-relay`. */
|
|
57
|
+
sessionId?: string;
|
|
58
|
+
/** Token issue time. Defaults to now. */
|
|
59
|
+
issuedAt?: Date;
|
|
60
|
+
/** Token expiry. Must be strictly after `issuedAt`. */
|
|
61
|
+
expiresAt: Date;
|
|
62
|
+
};
|
|
63
|
+
type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
64
|
+
[key: string]: JsonValue | undefined;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Serializes a JSON value to RFC 8785 (JCS) canonical form.
|
|
68
|
+
*
|
|
69
|
+
* Scope note: this implementation covers the JSON shapes used by UKYC storage
|
|
70
|
+
* payloads — objects, arrays, strings, integers, booleans, and null. Object
|
|
71
|
+
* members are sorted by their UTF-16 code units (matching JS default string
|
|
72
|
+
* ordering, which is what JCS requires) and `undefined` members are dropped.
|
|
73
|
+
* Non-finite and non-integer numbers are rejected, since the payloads never
|
|
74
|
+
* contain them and correct JCS number formatting for the general case is
|
|
75
|
+
* intentionally out of scope here.
|
|
76
|
+
*
|
|
77
|
+
* @param value - The value to canonicalize.
|
|
78
|
+
* @returns The canonical JSON string.
|
|
79
|
+
*/
|
|
80
|
+
export declare function canonicalizeJson(value: JsonValue): string;
|
|
81
|
+
/**
|
|
82
|
+
* Builds and signs a `storage_access_token`.
|
|
83
|
+
*
|
|
84
|
+
* @param params - See {@link SignStorageAccessTokenParams}.
|
|
85
|
+
* @returns The signed token envelope.
|
|
86
|
+
*/
|
|
87
|
+
export declare function signStorageAccessToken(params: SignStorageAccessTokenParams): UkycStorageAccessToken;
|
|
88
|
+
/**
|
|
89
|
+
* Serializes a signed token to a compact string suitable for header transport
|
|
90
|
+
* (`Authorization: AccessToken <token>`, see `UKYC_CAPABILITY_AUTH_SCHEME`). The
|
|
91
|
+
* complete envelope is base64url-encoded; the private `signing_key` is never
|
|
92
|
+
* included.
|
|
93
|
+
*
|
|
94
|
+
* @param token - The signed token envelope.
|
|
95
|
+
* @returns The base64url-encoded envelope string.
|
|
96
|
+
*/
|
|
97
|
+
export declare function encodeStorageAccessTokenForHeader(token: UkycStorageAccessToken): string;
|
|
98
|
+
export {};
|
|
99
|
+
//# sourceMappingURL=storageAccessToken.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageAccessToken.d.mts","sourceRoot":"","sources":["../../src/ukyc/storageAccessToken.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,kBAAkB,EAAE,mCAAkC;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,7 +1,7 @@
|
|
|
1
|
-
import { stringToBytes } from
|
|
2
|
-
import { ed25519 } from
|
|
3
|
-
import { toBase64Url } from
|
|
4
|
-
import { UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES, UKYC_STORAGE_ACCESS_TOKEN_VERSION
|
|
1
|
+
import { stringToBytes } from "@metamask/utils";
|
|
2
|
+
import { ed25519 } from "@noble/curves/ed25519";
|
|
3
|
+
import { toBase64Url } from "../encoding.mjs";
|
|
4
|
+
import { UKYC_STORAGE_ACCESS_TOKEN_AUDIENCES, UKYC_STORAGE_ACCESS_TOKEN_VERSION } from "./constants.mjs";
|
|
5
5
|
/**
|
|
6
6
|
* Serializes a JSON value to RFC 8785 (JCS) canonical form.
|
|
7
7
|
*
|
|
@@ -130,4 +130,4 @@ function assertValidOperations(operations) {
|
|
|
130
130
|
throw new Error('UKYC: a delete-scoped storage_access_token must contain only "delete".');
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
//# sourceMappingURL=storageAccessToken.
|
|
133
|
+
//# sourceMappingURL=storageAccessToken.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storageAccessToken.mjs","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"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.mintUkycTestToken = void 0;
|
|
4
|
+
const utils_1 = require("@noble/hashes/utils");
|
|
5
|
+
const constants_js_1 = require("./constants.cjs");
|
|
6
|
+
const deriveClientMaterial_js_1 = require("./deriveClientMaterial.cjs");
|
|
7
|
+
const storageAccessToken_js_1 = require("./storageAccessToken.cjs");
|
|
8
|
+
/**
|
|
9
|
+
* Mints ready-to-use UKYC `storage_access_token`s for testing UKYC Storage.
|
|
10
|
+
*
|
|
11
|
+
* This composes the same pure functions the MetaMask client uses in production
|
|
12
|
+
* (`deriveClientMaterial` + `signStorageAccessToken`), so a third party such as
|
|
13
|
+
* idOS can produce a valid, signed token without deriving one on-device. No
|
|
14
|
+
* server "fills in" the `signing_public_key`: it is derived from a
|
|
15
|
+
* `local_user_secret` the caller controls and registered on first write, so
|
|
16
|
+
* reusing the same secret yields a stable `storage_id` and controlling key.
|
|
17
|
+
*/
|
|
18
|
+
/** Default token lifetime when `expiresAt` is not supplied (4 hours). */
|
|
19
|
+
const DEFAULT_TOKEN_LIFETIME_MS = 4 * 60 * 60 * 1000;
|
|
20
|
+
/**
|
|
21
|
+
* Resolves the caller-supplied secret into raw bytes, generating a random one
|
|
22
|
+
* when none is provided.
|
|
23
|
+
*
|
|
24
|
+
* @param secret - Raw 32 bytes, a hex string, or undefined for a random secret.
|
|
25
|
+
* @returns The `local_user_secret` bytes.
|
|
26
|
+
*/
|
|
27
|
+
function resolveLocalUserSecret(secret) {
|
|
28
|
+
if (secret === undefined) {
|
|
29
|
+
return (0, utils_1.randomBytes)(constants_js_1.UKYC_LOCAL_USER_SECRET_SIZE_BYTES);
|
|
30
|
+
}
|
|
31
|
+
return typeof secret === 'string' ? (0, utils_1.hexToBytes)(secret) : secret;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Mints a signed UKYC `storage_access_token` for testing.
|
|
35
|
+
*
|
|
36
|
+
* @param params - See {@link MintUkycTestTokenParams}.
|
|
37
|
+
* @returns The token, its `Authorization` header, and the derived identifiers.
|
|
38
|
+
*/
|
|
39
|
+
function mintUkycTestToken(params = {}) {
|
|
40
|
+
const { operations = ['read'], presenter, sessionId, issuedAt = new Date(), expiresAt = new Date(issuedAt.getTime() + DEFAULT_TOKEN_LIFETIME_MS), } = params;
|
|
41
|
+
const localUserSecret = resolveLocalUserSecret(params.localUserSecret);
|
|
42
|
+
const material = (0, deriveClientMaterial_js_1.deriveClientMaterial)(localUserSecret);
|
|
43
|
+
const token = (0, storageAccessToken_js_1.signStorageAccessToken)({
|
|
44
|
+
material,
|
|
45
|
+
operations,
|
|
46
|
+
presenter,
|
|
47
|
+
sessionId,
|
|
48
|
+
issuedAt,
|
|
49
|
+
expiresAt,
|
|
50
|
+
});
|
|
51
|
+
const { storageId, signingPublicKey } = (0, deriveClientMaterial_js_1.encodeClientMaterial)(material);
|
|
52
|
+
return {
|
|
53
|
+
localUserSecret: (0, utils_1.bytesToHex)(localUserSecret),
|
|
54
|
+
storageId,
|
|
55
|
+
signingPublicKey,
|
|
56
|
+
token,
|
|
57
|
+
authorizationHeader: `${constants_js_1.UKYC_CAPABILITY_AUTH_SCHEME} ${(0, storageAccessToken_js_1.encodeStorageAccessTokenForHeader)(token)}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
exports.mintUkycTestToken = mintUkycTestToken;
|
|
61
|
+
//# sourceMappingURL=testToken.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testToken.cjs","sourceRoot":"","sources":["../../src/ukyc/testToken.ts"],"names":[],"mappings":";;;AAAA,+CAA0E;AAE1E,kDAGwB;AACxB,wEAGmC;AACnC,oEAGiC;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,IAAA,mBAAW,EAAC,gDAAiC,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAA,kBAAU,EAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAClE,CAAC;AAED;;;;;GAKG;AACH,SAAgB,iBAAiB,CAC/B,SAAkC,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,IAAA,8CAAoB,EAAC,eAAe,CAAC,CAAC;IAEvD,MAAM,KAAK,GAAG,IAAA,8CAAsB,EAAC;QACnC,QAAQ;QACR,UAAU;QACV,SAAS;QACT,SAAS;QACT,QAAQ;QACR,SAAS;KACV,CAAC,CAAC;IAEH,MAAM,EAAE,SAAS,EAAE,gBAAgB,EAAE,GAAG,IAAA,8CAAoB,EAAC,QAAQ,CAAC,CAAC;IAEvE,OAAO;QACL,eAAe,EAAE,IAAA,kBAAU,EAAC,eAAe,CAAC;QAC5C,SAAS;QACT,gBAAgB;QAChB,KAAK;QACL,mBAAmB,EAAE,GAAG,0CAA2B,IAAI,IAAA,yDAAiC,EACtF,KAAK,CACN,EAAE;KACJ,CAAC;AACJ,CAAC;AAlCD,8CAkCC","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,4 +1,4 @@
|
|
|
1
|
-
import type { UkycStorageAccessToken, UkycStorageOperation, UkycTokenPresenter } from
|
|
1
|
+
import type { UkycStorageAccessToken, UkycStorageOperation, UkycTokenPresenter } from "./storageAccessToken.cjs";
|
|
2
2
|
/**
|
|
3
3
|
* Inputs for {@link mintUkycTestToken}. All fields are optional; the only value
|
|
4
4
|
* a caller usually pins is `localUserSecret`, so `storage_id` and the signing
|
|
@@ -47,4 +47,4 @@ export type MintedUkycTestToken = {
|
|
|
47
47
|
* @returns The token, its `Authorization` header, and the derived identifiers.
|
|
48
48
|
*/
|
|
49
49
|
export declare function mintUkycTestToken(params?: MintUkycTestTokenParams): MintedUkycTestToken;
|
|
50
|
-
//# sourceMappingURL=testToken.d.
|
|
50
|
+
//# sourceMappingURL=testToken.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testToken.d.cts","sourceRoot":"","sources":["../../src/ukyc/testToken.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EACnB,iCAAgC;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"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { UkycStorageAccessToken, UkycStorageOperation, UkycTokenPresenter } from "./storageAccessToken.mjs";
|
|
2
|
+
/**
|
|
3
|
+
* Inputs for {@link mintUkycTestToken}. All fields are optional; the only value
|
|
4
|
+
* a caller usually pins is `localUserSecret`, so `storage_id` and the signing
|
|
5
|
+
* key stay stable across runs.
|
|
6
|
+
*/
|
|
7
|
+
export type MintUkycTestTokenParams = {
|
|
8
|
+
/**
|
|
9
|
+
* The root `local_user_secret`, as raw 32 bytes or a hex string. When omitted
|
|
10
|
+
* a fresh random secret is generated (returned in the result so it can be
|
|
11
|
+
* reused).
|
|
12
|
+
*/
|
|
13
|
+
localUserSecret?: Uint8Array | string;
|
|
14
|
+
/** Operations the token authorizes. Defaults to `['read']`. */
|
|
15
|
+
operations?: UkycStorageOperation[];
|
|
16
|
+
/** Who will present the token. Defaults to `client`. */
|
|
17
|
+
presenter?: UkycTokenPresenter;
|
|
18
|
+
/** UKYC session id. Required when `presenter` is `idos-relay`. */
|
|
19
|
+
sessionId?: string;
|
|
20
|
+
/** Token issue time. Defaults to now. */
|
|
21
|
+
issuedAt?: Date;
|
|
22
|
+
/** Token expiry. Defaults to `issuedAt` + 4 hours. */
|
|
23
|
+
expiresAt?: Date;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* The minted token plus everything needed to exercise UKYC Storage with it.
|
|
27
|
+
*/
|
|
28
|
+
export type MintedUkycTestToken = {
|
|
29
|
+
/** The `local_user_secret` used, hex-encoded, so the caller can reuse it. */
|
|
30
|
+
localUserSecret: string;
|
|
31
|
+
/** base64url `storage_id` — use it as the `{storage_id}` path segment. */
|
|
32
|
+
storageId: string;
|
|
33
|
+
/** base64url `signing_public_key` registered on first write. */
|
|
34
|
+
signingPublicKey: string;
|
|
35
|
+
/** The signed token envelope (payload + signature). */
|
|
36
|
+
token: UkycStorageAccessToken;
|
|
37
|
+
/**
|
|
38
|
+
* The full `Authorization` header value, e.g.
|
|
39
|
+
* `AccessToken <base64url(envelope)>`, ready to send to UKYC Storage.
|
|
40
|
+
*/
|
|
41
|
+
authorizationHeader: string;
|
|
42
|
+
};
|
|
43
|
+
/**
|
|
44
|
+
* Mints a signed UKYC `storage_access_token` for testing.
|
|
45
|
+
*
|
|
46
|
+
* @param params - See {@link MintUkycTestTokenParams}.
|
|
47
|
+
* @returns The token, its `Authorization` header, and the derived identifiers.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mintUkycTestToken(params?: MintUkycTestTokenParams): MintedUkycTestToken;
|
|
50
|
+
//# sourceMappingURL=testToken.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testToken.d.mts","sourceRoot":"","sources":["../../src/ukyc/testToken.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EACnB,iCAAgC;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,7 +1,7 @@
|
|
|
1
|
-
import { bytesToHex, hexToBytes, randomBytes } from
|
|
2
|
-
import { UKYC_CAPABILITY_AUTH_SCHEME, UKYC_LOCAL_USER_SECRET_SIZE_BYTES
|
|
3
|
-
import { deriveClientMaterial, encodeClientMaterial
|
|
4
|
-
import { encodeStorageAccessTokenForHeader, signStorageAccessToken
|
|
1
|
+
import { bytesToHex, hexToBytes, randomBytes } from "@noble/hashes/utils";
|
|
2
|
+
import { UKYC_CAPABILITY_AUTH_SCHEME, UKYC_LOCAL_USER_SECRET_SIZE_BYTES } from "./constants.mjs";
|
|
3
|
+
import { deriveClientMaterial, encodeClientMaterial } from "./deriveClientMaterial.mjs";
|
|
4
|
+
import { encodeStorageAccessTokenForHeader, signStorageAccessToken } from "./storageAccessToken.mjs";
|
|
5
5
|
/**
|
|
6
6
|
* Mints ready-to-use UKYC `storage_access_token`s for testing UKYC Storage.
|
|
7
7
|
*
|
|
@@ -54,4 +54,4 @@ export function mintUkycTestToken(params = {}) {
|
|
|
54
54
|
authorizationHeader: `${UKYC_CAPABILITY_AUTH_SCHEME} ${encodeStorageAccessTokenForHeader(token)}`,
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
|
-
//# sourceMappingURL=testToken.
|
|
57
|
+
//# sourceMappingURL=testToken.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testToken.mjs","sourceRoot":"","sources":["../../src/ukyc/testToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,WAAW,EAAE,4BAA4B;AAE1E,OAAO,EACL,2BAA2B,EAC3B,iCAAiC,EAClC,wBAAuB;AACxB,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACrB,mCAAkC;AACnC,OAAO,EACL,iCAAiC,EACjC,sBAAsB,EACvB,iCAAgC;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,SAAkC,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"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.wrapEncryptionKey = void 0;
|
|
4
|
+
const tweetnacl_1 = require("tweetnacl");
|
|
5
|
+
const encoding_js_1 = require("../encoding.cjs");
|
|
6
|
+
/**
|
|
7
|
+
* Wraps `plaintext` for the UKYC session server.
|
|
8
|
+
*
|
|
9
|
+
* The box is sealed with NaCl's `crypto_box`, keyed by the X25519 shared secret
|
|
10
|
+
* between our session client private key and the session server public key
|
|
11
|
+
* from an encryption schema (`encryptionDataKey` or `ukycCapabilityToken`)
|
|
12
|
+
* returned by `createUkycSession`. The 32-byte client public key is prefixed
|
|
13
|
+
* onto `data` so the server can open the box without a prior key-registration
|
|
14
|
+
* step.
|
|
15
|
+
*
|
|
16
|
+
* @param sessionClientPrivateKey - Our session's X25519 private key.
|
|
17
|
+
* @param sessionServerPublicKey - The server's X25519 public key (base64url).
|
|
18
|
+
* @param plaintext - The raw bytes to encrypt (e.g. the `data_encryption_key`
|
|
19
|
+
* or the encoded `ukyc_capability_token`).
|
|
20
|
+
* @returns The base64url `data` (`clientPublicKey || ciphertext+tag`) and
|
|
21
|
+
* `nonce`.
|
|
22
|
+
*/
|
|
23
|
+
function wrapEncryptionKey(sessionClientPrivateKey, sessionServerPublicKey, plaintext) {
|
|
24
|
+
const serverPublicKey = (0, encoding_js_1.base64UrlToBytes)(sessionServerPublicKey);
|
|
25
|
+
const { publicKey: clientPublicKey } = tweetnacl_1.box.keyPair.fromSecretKey(sessionClientPrivateKey);
|
|
26
|
+
const nonce = (0, tweetnacl_1.randomBytes)(tweetnacl_1.box.nonceLength);
|
|
27
|
+
const ciphertext = (0, tweetnacl_1.box)(plaintext, nonce, serverPublicKey, sessionClientPrivateKey);
|
|
28
|
+
const data = new Uint8Array(clientPublicKey.length + ciphertext.length);
|
|
29
|
+
data.set(clientPublicKey, 0);
|
|
30
|
+
data.set(ciphertext, clientPublicKey.length);
|
|
31
|
+
return {
|
|
32
|
+
data: (0, encoding_js_1.toBase64Url)(data),
|
|
33
|
+
nonce: (0, encoding_js_1.toBase64Url)(nonce),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
exports.wrapEncryptionKey = wrapEncryptionKey;
|
|
37
|
+
//# sourceMappingURL=wrapEncryptionKey.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapEncryptionKey.cjs","sourceRoot":"","sources":["../../src/ukyc/wrapEncryptionKey.ts"],"names":[],"mappings":";;;AAAA,yCAA6C;AAE7C,iDAA+D;AA0B/D;;;;;;;;;;;;;;;;GAgBG;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 * 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"]}
|
|
@@ -38,4 +38,4 @@ export type WrappedEncryptionKeyParts = {
|
|
|
38
38
|
* `nonce`.
|
|
39
39
|
*/
|
|
40
40
|
export declare function wrapEncryptionKey(sessionClientPrivateKey: Uint8Array, sessionServerPublicKey: string, plaintext: Uint8Array): WrappedEncryptionKeyParts;
|
|
41
|
-
//# sourceMappingURL=wrapEncryptionKey.d.
|
|
41
|
+
//# sourceMappingURL=wrapEncryptionKey.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapEncryptionKey.d.cts","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"}
|