@turnkey/crypto
Advanced tools
+1
-0
@@ -24,3 +24,4 @@ 'use strict'; | ||
| exports.encryptWalletToBundle = turnkey.encryptWalletToBundle; | ||
| exports.verifySessionJwtSignature = turnkey.verifySessionJwtSignature; | ||
| exports.verifyStampSignature = turnkey.verifyStampSignature; | ||
| //# sourceMappingURL=index.js.map |
+6
-0
| # @turnkey/crypto | ||
| ## 2.4.0 | ||
| ### Minor Changes | ||
| - [#662](https://github.com/tkhq/sdk/pull/662) [`10ee5c5`](https://github.com/tkhq/sdk/commit/10ee5c524b477ce998e4fc635152cd101ae5a9cc) Thanks [@moe-dev](https://github.com/moe-dev)! - Add function `verifySessionJwtSignature` to verify session tokens return from Turnkey and signed by the notarizer | ||
| ## 2.3.1 | ||
@@ -4,0 +10,0 @@ |
@@ -10,2 +10,3 @@ export declare const SUITE_ID_1: Uint8Array; | ||
| export declare const PRODUCTION_SIGNER_PUBLIC_KEY = "04cf288fe433cc4e1aa0ce1632feac4ea26bf2f5a09dcfe5a42c398e06898710330f0572882f4dbdf0f5304b8fc8703acd69adca9a4bbf7f5d00d20a5e364b2569"; | ||
| export declare const PRODUCTION_NOTARIZER_PUBLIC_KEY = "04d498aa87ac3bf982ac2b5dd9604d0074905cfbda5d62727c5a237b895e6749205e9f7cd566909c4387f6ca25c308445c60884b788560b785f4a96ac33702a469"; | ||
| //# sourceMappingURL=constants.d.ts.map |
@@ -27,2 +27,3 @@ 'use strict'; | ||
| const PRODUCTION_SIGNER_PUBLIC_KEY = "04cf288fe433cc4e1aa0ce1632feac4ea26bf2f5a09dcfe5a42c398e06898710330f0572882f4dbdf0f5304b8fc8703acd69adca9a4bbf7f5d00d20a5e364b2569"; | ||
| const PRODUCTION_NOTARIZER_PUBLIC_KEY = "04d498aa87ac3bf982ac2b5dd9604d0074905cfbda5d62727c5a237b895e6749205e9f7cd566909c4387f6ca25c308445c60884b788560b785f4a96ac33702a469"; | ||
@@ -35,2 +36,3 @@ exports.AES_KEY_INFO = AES_KEY_INFO; | ||
| exports.LABEL_SHARED_SECRET = LABEL_SHARED_SECRET; | ||
| exports.PRODUCTION_NOTARIZER_PUBLIC_KEY = PRODUCTION_NOTARIZER_PUBLIC_KEY; | ||
| exports.PRODUCTION_SIGNER_PUBLIC_KEY = PRODUCTION_SIGNER_PUBLIC_KEY; | ||
@@ -37,0 +39,0 @@ exports.SUITE_ID_1 = SUITE_ID_1; |
@@ -25,4 +25,5 @@ const SUITE_ID_1 = new Uint8Array([75, 69, 77, 0, 16]); //KEM suite ID | ||
| const PRODUCTION_SIGNER_PUBLIC_KEY = "04cf288fe433cc4e1aa0ce1632feac4ea26bf2f5a09dcfe5a42c398e06898710330f0572882f4dbdf0f5304b8fc8703acd69adca9a4bbf7f5d00d20a5e364b2569"; | ||
| const PRODUCTION_NOTARIZER_PUBLIC_KEY = "04d498aa87ac3bf982ac2b5dd9604d0074905cfbda5d62727c5a237b895e6749205e9f7cd566909c4387f6ca25c308445c60884b788560b785f4a96ac33702a469"; | ||
| export { AES_KEY_INFO, HPKE_VERSION, IV_INFO, LABEL_EAE_PRK, LABEL_SECRET, LABEL_SHARED_SECRET, PRODUCTION_SIGNER_PUBLIC_KEY, SUITE_ID_1, SUITE_ID_2 }; | ||
| export { AES_KEY_INFO, HPKE_VERSION, IV_INFO, LABEL_EAE_PRK, LABEL_SECRET, LABEL_SHARED_SECRET, PRODUCTION_NOTARIZER_PUBLIC_KEY, PRODUCTION_SIGNER_PUBLIC_KEY, SUITE_ID_1, SUITE_ID_2 }; | ||
| //# sourceMappingURL=constants.mjs.map |
+1
-0
@@ -24,3 +24,4 @@ 'use strict'; | ||
| exports.encryptWalletToBundle = turnkey.encryptWalletToBundle; | ||
| exports.verifySessionJwtSignature = turnkey.verifySessionJwtSignature; | ||
| exports.verifyStampSignature = turnkey.verifyStampSignature; | ||
| //# sourceMappingURL=index.js.map |
+1
-1
| export { buildAdditionalAssociatedData, compressRawPublicKey, extractPrivateKeyFromPKCS8Bytes, formatHpkeBuf, fromDerSignature, generateP256KeyPair, getPublicKey, hpkeAuthEncrypt, hpkeDecrypt, hpkeEncrypt, toDerSignature, uncompressRawPublicKey } from './crypto.mjs'; | ||
| export { decryptCredentialBundle, decryptExportBundle, encryptPrivateKeyToBundle, encryptWalletToBundle, verifyStampSignature } from './turnkey.mjs'; | ||
| export { decryptCredentialBundle, decryptExportBundle, encryptPrivateKeyToBundle, encryptWalletToBundle, verifySessionJwtSignature, verifyStampSignature } from './turnkey.mjs'; | ||
| //# sourceMappingURL=index.mjs.map |
+25
-0
@@ -87,3 +87,28 @@ /// <reference lib="dom" /> | ||
| export declare const encryptWalletToBundle: ({ mnemonic, importBundle, userId, organizationId, dangerouslyOverrideSignerPublicKey, }: EncryptWalletToBundleParams) => Promise<string>; | ||
| /** | ||
| * Verifies that a **session JWT** was signed by Turnkey’s | ||
| * notarizer key (P-256 / ES256, compact 64-byte r‖s signature). | ||
| * | ||
| * How it works | ||
| * ------------ | ||
| * 1. Split the JWT into `header.payload.signature`. | ||
| * 2. **Double-hash** the string `"header.payload"`: | ||
| * `h1 = sha256(header.payload)` | ||
| * `msg = sha256(h1)` | ||
| * (The Rust signer feeds `h1` into `SigningKey::sign`, which hashes once | ||
| * more internally, yielding `msg`.) | ||
| * 3. Base64-URL-decode the signature (`r||s`, 64 bytes). | ||
| * 4. Import the notarizer public key (hex `04‖X‖Y` → `Uint8Array`). | ||
| * 5. Call `p256.verify(signature, msg, publicKey)`; noble treats the 32-byte | ||
| * `msg` as a pre-hashed digest and performs ECDSA verification. | ||
| * | ||
| * @param jwt The session JWT to validate. | ||
| * @param dangerouslyOverrideNotarizerPublicKey *(optional)* Hex-encoded | ||
| * uncompressed P-256 public key to verify against (use only in | ||
| * tests). Defaults to the production notarizer key. | ||
| * @returns `true` if the signature is valid for the given key, else `false`. | ||
| * @throws If the JWT is malformed. | ||
| */ | ||
| export declare const verifySessionJwtSignature: (jwt: string, dangerouslyOverrideNotarizerPublicKey?: string) => Promise<boolean>; | ||
| export {}; | ||
| //# sourceMappingURL=turnkey.d.ts.map |
+45
-0
@@ -254,2 +254,46 @@ 'use strict'; | ||
| }; | ||
| /** | ||
| * Verifies that a **session JWT** was signed by Turnkey’s | ||
| * notarizer key (P-256 / ES256, compact 64-byte r‖s signature). | ||
| * | ||
| * How it works | ||
| * ------------ | ||
| * 1. Split the JWT into `header.payload.signature`. | ||
| * 2. **Double-hash** the string `"header.payload"`: | ||
| * `h1 = sha256(header.payload)` | ||
| * `msg = sha256(h1)` | ||
| * (The Rust signer feeds `h1` into `SigningKey::sign`, which hashes once | ||
| * more internally, yielding `msg`.) | ||
| * 3. Base64-URL-decode the signature (`r||s`, 64 bytes). | ||
| * 4. Import the notarizer public key (hex `04‖X‖Y` → `Uint8Array`). | ||
| * 5. Call `p256.verify(signature, msg, publicKey)`; noble treats the 32-byte | ||
| * `msg` as a pre-hashed digest and performs ECDSA verification. | ||
| * | ||
| * @param jwt The session JWT to validate. | ||
| * @param dangerouslyOverrideNotarizerPublicKey *(optional)* Hex-encoded | ||
| * uncompressed P-256 public key to verify against (use only in | ||
| * tests). Defaults to the production notarizer key. | ||
| * @returns `true` if the signature is valid for the given key, else `false`. | ||
| * @throws If the JWT is malformed. | ||
| */ | ||
| const verifySessionJwtSignature = async (jwt, dangerouslyOverrideNotarizerPublicKey) => { | ||
| const notarizerKeyHex = dangerouslyOverrideNotarizerPublicKey ?? constants.PRODUCTION_NOTARIZER_PUBLIC_KEY; | ||
| /* 1. split JWT -------------------------------------------------------- */ | ||
| const [headerB64, payloadB64, signatureB64] = jwt.split("."); | ||
| if (!signatureB64) | ||
| throw new Error("invalid JWT: need 3 parts"); | ||
| const signingInput = `${headerB64}.${payloadB64}`; | ||
| /* 2. sha256(sha256(header.payload)) ----------------------------------- */ | ||
| const h1 = sha256.sha256(new TextEncoder().encode(signingInput)); | ||
| const msgDigest = sha256.sha256(h1); // 32-byte Uint8Array | ||
| /* 3. base64-url decode signature -------------------------------------- */ | ||
| const toB64 = (u) => (u = u.replace(/-/g, "+").replace(/_/g, "/")).padEnd(u.length + ((4 - (u.length % 4)) % 4), "="); | ||
| const signature = Uint8Array.from(atob(toB64(signatureB64)) | ||
| .split("") | ||
| .map((c) => c.charCodeAt(0))); // 64 bytes | ||
| /* 4. load public key -------------------------------------------------- */ | ||
| const publicKey = encoding.uint8ArrayFromHexString(notarizerKeyHex); | ||
| /* 5. verify ----------------------------------------------------------- */ | ||
| return p256.p256.verify(signature, msgDigest, publicKey); | ||
| }; | ||
@@ -260,3 +304,4 @@ exports.decryptCredentialBundle = decryptCredentialBundle; | ||
| exports.encryptWalletToBundle = encryptWalletToBundle; | ||
| exports.verifySessionJwtSignature = verifySessionJwtSignature; | ||
| exports.verifyStampSignature = verifyStampSignature; | ||
| //# sourceMappingURL=turnkey.js.map |
+46
-2
| import bs58check from 'bs58check'; | ||
| import bs58 from 'bs58'; | ||
| import { uint8ArrayToHexString, uint8ArrayFromHexString, hexToAscii } from '@turnkey/encoding'; | ||
| import { PRODUCTION_SIGNER_PUBLIC_KEY } from './constants.mjs'; | ||
| import { PRODUCTION_SIGNER_PUBLIC_KEY, PRODUCTION_NOTARIZER_PUBLIC_KEY } from './constants.mjs'; | ||
| import { uncompressRawPublicKey, hpkeDecrypt, fromDerSignature, hpkeEncrypt, formatHpkeBuf } from './crypto.mjs'; | ||
@@ -252,4 +252,48 @@ import { p256 } from '@noble/curves/p256'; | ||
| }; | ||
| /** | ||
| * Verifies that a **session JWT** was signed by Turnkey’s | ||
| * notarizer key (P-256 / ES256, compact 64-byte r‖s signature). | ||
| * | ||
| * How it works | ||
| * ------------ | ||
| * 1. Split the JWT into `header.payload.signature`. | ||
| * 2. **Double-hash** the string `"header.payload"`: | ||
| * `h1 = sha256(header.payload)` | ||
| * `msg = sha256(h1)` | ||
| * (The Rust signer feeds `h1` into `SigningKey::sign`, which hashes once | ||
| * more internally, yielding `msg`.) | ||
| * 3. Base64-URL-decode the signature (`r||s`, 64 bytes). | ||
| * 4. Import the notarizer public key (hex `04‖X‖Y` → `Uint8Array`). | ||
| * 5. Call `p256.verify(signature, msg, publicKey)`; noble treats the 32-byte | ||
| * `msg` as a pre-hashed digest and performs ECDSA verification. | ||
| * | ||
| * @param jwt The session JWT to validate. | ||
| * @param dangerouslyOverrideNotarizerPublicKey *(optional)* Hex-encoded | ||
| * uncompressed P-256 public key to verify against (use only in | ||
| * tests). Defaults to the production notarizer key. | ||
| * @returns `true` if the signature is valid for the given key, else `false`. | ||
| * @throws If the JWT is malformed. | ||
| */ | ||
| const verifySessionJwtSignature = async (jwt, dangerouslyOverrideNotarizerPublicKey) => { | ||
| const notarizerKeyHex = dangerouslyOverrideNotarizerPublicKey ?? PRODUCTION_NOTARIZER_PUBLIC_KEY; | ||
| /* 1. split JWT -------------------------------------------------------- */ | ||
| const [headerB64, payloadB64, signatureB64] = jwt.split("."); | ||
| if (!signatureB64) | ||
| throw new Error("invalid JWT: need 3 parts"); | ||
| const signingInput = `${headerB64}.${payloadB64}`; | ||
| /* 2. sha256(sha256(header.payload)) ----------------------------------- */ | ||
| const h1 = sha256(new TextEncoder().encode(signingInput)); | ||
| const msgDigest = sha256(h1); // 32-byte Uint8Array | ||
| /* 3. base64-url decode signature -------------------------------------- */ | ||
| const toB64 = (u) => (u = u.replace(/-/g, "+").replace(/_/g, "/")).padEnd(u.length + ((4 - (u.length % 4)) % 4), "="); | ||
| const signature = Uint8Array.from(atob(toB64(signatureB64)) | ||
| .split("") | ||
| .map((c) => c.charCodeAt(0))); // 64 bytes | ||
| /* 4. load public key -------------------------------------------------- */ | ||
| const publicKey = uint8ArrayFromHexString(notarizerKeyHex); | ||
| /* 5. verify ----------------------------------------------------------- */ | ||
| return p256.verify(signature, msgDigest, publicKey); | ||
| }; | ||
| export { decryptCredentialBundle, decryptExportBundle, encryptPrivateKeyToBundle, encryptWalletToBundle, verifyStampSignature }; | ||
| export { decryptCredentialBundle, decryptExportBundle, encryptPrivateKeyToBundle, encryptWalletToBundle, verifySessionJwtSignature, verifyStampSignature }; | ||
| //# sourceMappingURL=turnkey.mjs.map |
+8
-8
| { | ||
| "name": "@turnkey/crypto", | ||
| "version": "2.3.1", | ||
| "version": "2.4.0", | ||
| "main": "./dist/index.js", | ||
@@ -23,3 +23,3 @@ "module": "./dist/index.mjs", | ||
| "type": "git", | ||
| "url": "https://github.com/tkhq/sdk.git", | ||
| "url": "git+https://github.com/tkhq/sdk.git", | ||
| "directory": "packages/crypto" | ||
@@ -29,3 +29,4 @@ }, | ||
| "dist/", | ||
| "CHANGELOG.md" | ||
| "CHANGELOG.md", | ||
| "README.md" | ||
| ], | ||
@@ -43,10 +44,9 @@ "publishConfig": { | ||
| "bs58check": "3.0.1", | ||
| "bs58": "^5.0.0", | ||
| "@turnkey/encoding": "0.4.0" | ||
| "bs58": "6.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "jest": "29.7.0", | ||
| "@turnkey/encoding": "0.4.0", | ||
| "@turnkey/http": "2.18.0", | ||
| "@turnkey/sdk-server": "2.0.1" | ||
| "@turnkey/http": "3.4.1", | ||
| "@turnkey/api-key-stamper": "0.4.6", | ||
| "@turnkey/encoding": "0.5.0" | ||
| }, | ||
@@ -53,0 +53,0 @@ "scripts": { |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
165741
5.96%5
-16.67%1925
6.65%+ Added
+ Added
- Removed
- Removed
Updated