🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

@turnkey/crypto

Package Overview
Dependencies
Maintainers
3
Versions
40
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@turnkey/crypto - npm Package Compare versions

Comparing version
2.9.0
to
2.10.0
+2
dist/__tests__/crypto-test.d.ts
export {};
//# sourceMappingURL=crypto-test.d.ts.map
{"version":3,"file":"crypto-test.d.ts","sourceRoot":"","sources":["../../src/__tests__/crypto-test.ts"],"names":[],"mappings":""}
/** @jest-environment node */
export {};
//# sourceMappingURL=proof-tests.d.ts.map
{"version":3,"file":"proof-tests.d.ts","sourceRoot":"","sources":["../../src/__tests__/proof-tests.ts"],"names":[],"mappings":"AAAA,6BAA6B"}
export {};
//# sourceMappingURL=webhooks-test.d.ts.map
{"version":3,"file":"webhooks-test.d.ts","sourceRoot":"","sources":["../../src/__tests__/webhooks-test.ts"],"names":[],"mappings":""}
export type TurnkeyWebhookVerificationKey = {
keyId: string;
publicKey: string;
algorithm?: "ed25519";
};
export type TurnkeyWebhookHeaders = Headers | Record<string, string | string[] | undefined>;
export type TurnkeyWebhookBody = string | Uint8Array | ArrayBuffer;
export type VerifyTurnkeyWebhookSignatureParams = {
headers: TurnkeyWebhookHeaders;
body: TurnkeyWebhookBody;
verificationKeys: TurnkeyWebhookVerificationKey[];
maxTimestampAgeMs: number;
nowMs?: number;
};
export declare const TurnkeyWebhookVerificationFailureReasons: {
readonly InvalidMaxTimestampAge: "invalid_max_timestamp_age";
readonly InvalidNow: "invalid_now";
readonly MissingHeader: "missing_header";
readonly InvalidTimestamp: "invalid_timestamp";
readonly StaleTimestamp: "stale_timestamp";
readonly UnsupportedSignatureVersion: "unsupported_signature_version";
readonly UnsupportedSignatureAlgorithm: "unsupported_signature_algorithm";
readonly MissingKey: "missing_key";
readonly InvalidVerificationKeyAlgorithm: "invalid_verification_key_algorithm";
readonly InvalidVerificationKey: "invalid_verification_key";
readonly InvalidSignature: "invalid_signature";
};
export type TurnkeyWebhookVerificationFailureReason = (typeof TurnkeyWebhookVerificationFailureReasons)[keyof typeof TurnkeyWebhookVerificationFailureReasons];
export type TurnkeyWebhookVerificationSuccess = {
ok: true;
eventId: string;
keyId: string;
timestampMs: number;
};
export type TurnkeyWebhookVerificationFailure = {
ok: false;
reason: TurnkeyWebhookVerificationFailureReason;
headerName?: string;
};
export type TurnkeyWebhookVerificationResult = TurnkeyWebhookVerificationSuccess | TurnkeyWebhookVerificationFailure;
/**
* Verifies a Turnkey webhook signature with caller-provided Ed25519
* verification keys.
*
* Pass the exact raw request body bytes Turnkey sent, the request headers, and
* a bounded `maxTimestampAgeMs` replay window. The function returns a typed
* failure result for invalid webhook input instead of throwing, so handlers can
* reject expected verification failures without exception-driven control flow.
*/
export declare function verifyTurnkeyWebhookSignature({ headers, body, verificationKeys, maxTimestampAgeMs, nowMs, }: VerifyTurnkeyWebhookSignatureParams): TurnkeyWebhookVerificationResult;
export declare function buildSignedInput({ version, algorithm, keyId, timestampMs, eventId, body, }: {
version: string;
algorithm: string;
keyId: string;
timestampMs: string;
eventId: string;
body: TurnkeyWebhookBody;
}): Uint8Array;
//# sourceMappingURL=webhooks.d.ts.map
{"version":3,"file":"webhooks.d.ts","sourceRoot":"","sources":["../src/webhooks.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,6BAA6B,GAAG;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAC7B,OAAO,GACP,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;AAElD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,UAAU,GAAG,WAAW,CAAC;AAEnE,MAAM,MAAM,mCAAmC,GAAG;IAChD,OAAO,EAAE,qBAAqB,CAAC;IAC/B,IAAI,EAAE,kBAAkB,CAAC;IACzB,gBAAgB,EAAE,6BAA6B,EAAE,CAAC;IAClD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,eAAO,MAAM,wCAAwC;;;;;;;;;;;;CAY3C,CAAC;AAEX,MAAM,MAAM,uCAAuC,GACjD,CAAC,OAAO,wCAAwC,CAAC,CAAC,MAAM,OAAO,wCAAwC,CAAC,CAAC;AAE3G,MAAM,MAAM,iCAAiC,GAAG;IAC9C,EAAE,EAAE,IAAI,CAAC;IACT,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,iCAAiC,GAAG;IAC9C,EAAE,EAAE,KAAK,CAAC;IACV,MAAM,EAAE,uCAAuC,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,gCAAgC,GACxC,iCAAiC,GACjC,iCAAiC,CAAC;AActC;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAAC,EAC5C,OAAO,EACP,IAAI,EACJ,gBAAgB,EAChB,iBAAiB,EACjB,KAAkB,GACnB,EAAE,mCAAmC,GAAG,gCAAgC,CAwGxE;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,OAAO,EACP,SAAS,EACT,KAAK,EACL,WAAW,EACX,OAAO,EACP,IAAI,GACL,EAAE;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,kBAAkB,CAAC;CAC1B,GAAG,UAAU,CASb"}
'use strict';
var ed25519 = require('@noble/curves/ed25519');
var encoding = require('@turnkey/encoding');
const TurnkeyWebhookVerificationFailureReasons = {
InvalidMaxTimestampAge: "invalid_max_timestamp_age",
InvalidNow: "invalid_now",
MissingHeader: "missing_header",
InvalidTimestamp: "invalid_timestamp",
StaleTimestamp: "stale_timestamp",
UnsupportedSignatureVersion: "unsupported_signature_version",
UnsupportedSignatureAlgorithm: "unsupported_signature_algorithm",
MissingKey: "missing_key",
InvalidVerificationKeyAlgorithm: "invalid_verification_key_algorithm",
InvalidVerificationKey: "invalid_verification_key",
InvalidSignature: "invalid_signature",
};
const HEADER_TIMESTAMP = "x-turnkey-timestamp";
const HEADER_EVENT_ID = "x-turnkey-event-id";
const HEADER_KEY_ID = "x-turnkey-signature-key-id";
const HEADER_ALGORITHM = "x-turnkey-signature-algorithm";
const HEADER_VERSION = "x-turnkey-signature-version";
const HEADER_SIGNATURE = "x-turnkey-signature";
const SUPPORTED_VERSION = "v1";
const SUPPORTED_ALGORITHM = "ed25519";
const PUBLIC_KEY_BYTE_LENGTH = 32;
const ED25519_SIGNATURE_BYTE_LENGTH = 64;
/**
* Verifies a Turnkey webhook signature with caller-provided Ed25519
* verification keys.
*
* Pass the exact raw request body bytes Turnkey sent, the request headers, and
* a bounded `maxTimestampAgeMs` replay window. The function returns a typed
* failure result for invalid webhook input instead of throwing, so handlers can
* reject expected verification failures without exception-driven control flow.
*/
function verifyTurnkeyWebhookSignature({ headers, body, verificationKeys, maxTimestampAgeMs, nowMs = Date.now(), }) {
if (!Number.isFinite(maxTimestampAgeMs) || maxTimestampAgeMs < 0) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidMaxTimestampAge);
}
if (!Number.isFinite(nowMs)) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidNow);
}
const timestampHeader = getRequiredHeader(headers, HEADER_TIMESTAMP);
if (!timestampHeader.ok) {
return timestampHeader;
}
const eventIdHeader = getRequiredHeader(headers, HEADER_EVENT_ID);
if (!eventIdHeader.ok) {
return eventIdHeader;
}
const keyIdHeader = getRequiredHeader(headers, HEADER_KEY_ID);
if (!keyIdHeader.ok) {
return keyIdHeader;
}
const algorithmHeader = getRequiredHeader(headers, HEADER_ALGORITHM);
if (!algorithmHeader.ok) {
return algorithmHeader;
}
const versionHeader = getRequiredHeader(headers, HEADER_VERSION);
if (!versionHeader.ok) {
return versionHeader;
}
const signatureHeader = getRequiredHeader(headers, HEADER_SIGNATURE);
if (!signatureHeader.ok) {
return signatureHeader;
}
const timestampMs = parseTimestampMs(timestampHeader.value);
if (timestampMs === undefined) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidTimestamp);
}
if (Math.abs(nowMs - timestampMs) > maxTimestampAgeMs) {
return invalid(TurnkeyWebhookVerificationFailureReasons.StaleTimestamp);
}
if (versionHeader.value !== SUPPORTED_VERSION) {
return invalid(TurnkeyWebhookVerificationFailureReasons.UnsupportedSignatureVersion);
}
if (algorithmHeader.value !== SUPPORTED_ALGORITHM) {
return invalid(TurnkeyWebhookVerificationFailureReasons.UnsupportedSignatureAlgorithm);
}
const key = verificationKeys.find((candidate) => candidate.keyId === keyIdHeader.value);
if (!key) {
return invalid(TurnkeyWebhookVerificationFailureReasons.MissingKey);
}
const publicKeyResult = getPublicKeyBytes(key);
if (!publicKeyResult.ok) {
return publicKeyResult;
}
const signatureResult = hexToBytes(signatureHeader.value, ED25519_SIGNATURE_BYTE_LENGTH, TurnkeyWebhookVerificationFailureReasons.InvalidSignature);
if (!signatureResult.ok) {
return signatureResult;
}
const signedInput = buildSignedInput({
version: versionHeader.value,
algorithm: algorithmHeader.value,
keyId: keyIdHeader.value,
timestampMs: timestampHeader.value,
eventId: eventIdHeader.value,
body,
});
let verified = false;
try {
verified = ed25519.ed25519.verify(signatureResult.value, signedInput, publicKeyResult.value);
}
catch {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidSignature);
}
if (!verified) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidSignature);
}
return {
ok: true,
eventId: eventIdHeader.value,
keyId: keyIdHeader.value,
timestampMs,
};
}
function buildSignedInput({ version, algorithm, keyId, timestampMs, eventId, body, }) {
const prefix = new TextEncoder().encode(`${version}.${algorithm}.${keyId}.${timestampMs}.${eventId}.`);
const bodyBytes = bodyToBytes(body);
const signedInput = new Uint8Array(prefix.length + bodyBytes.length);
signedInput.set(prefix, 0);
signedInput.set(bodyBytes, prefix.length);
return signedInput;
}
function bodyToBytes(body) {
if (typeof body === "string") {
return new TextEncoder().encode(body);
}
if (body instanceof Uint8Array) {
return body;
}
return new Uint8Array(body);
}
function getRequiredHeader(headers, name) {
const value = getHeader(headers, name);
if (value === undefined || value === "") {
return invalid(TurnkeyWebhookVerificationFailureReasons.MissingHeader, {
headerName: name,
});
}
return { ok: true, value };
}
function getHeader(headers, name) {
if (isHeaders(headers)) {
return headers.get(name) ?? undefined;
}
for (const [headerName, headerValue] of Object.entries(headers)) {
if (headerName.toLowerCase() !== name) {
continue;
}
if (Array.isArray(headerValue)) {
return headerValue[0];
}
return headerValue;
}
return undefined;
}
function isHeaders(headers) {
return typeof Headers !== "undefined" && headers instanceof Headers;
}
function parseTimestampMs(timestamp) {
if (!/^[0-9]+$/.test(timestamp)) {
return undefined;
}
const parsed = Number(timestamp);
if (!Number.isSafeInteger(parsed)) {
return undefined;
}
return parsed;
}
function getPublicKeyBytes(key) {
if (key.algorithm !== undefined && key.algorithm !== SUPPORTED_ALGORITHM) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidVerificationKeyAlgorithm);
}
return hexToBytes(key.publicKey, PUBLIC_KEY_BYTE_LENGTH, TurnkeyWebhookVerificationFailureReasons.InvalidVerificationKey);
}
function hexToBytes(input, expectedLength, reason) {
if (input.length !== expectedLength * 2) {
return invalid(reason);
}
try {
return {
ok: true,
value: encoding.uint8ArrayFromHexString(input),
};
}
catch {
return invalid(reason);
}
}
function invalid(reason, details = {}) {
return { ok: false, reason, ...details };
}
exports.TurnkeyWebhookVerificationFailureReasons = TurnkeyWebhookVerificationFailureReasons;
exports.buildSignedInput = buildSignedInput;
exports.verifyTurnkeyWebhookSignature = verifyTurnkeyWebhookSignature;
//# sourceMappingURL=webhooks.js.map
{"version":3,"file":"webhooks.js","sources":["../src/webhooks.ts"],"sourcesContent":[null],"names":["ed25519","uint8ArrayFromHexString"],"mappings":"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"}
import { ed25519 } from '@noble/curves/ed25519';
import { uint8ArrayFromHexString } from '@turnkey/encoding';
const TurnkeyWebhookVerificationFailureReasons = {
InvalidMaxTimestampAge: "invalid_max_timestamp_age",
InvalidNow: "invalid_now",
MissingHeader: "missing_header",
InvalidTimestamp: "invalid_timestamp",
StaleTimestamp: "stale_timestamp",
UnsupportedSignatureVersion: "unsupported_signature_version",
UnsupportedSignatureAlgorithm: "unsupported_signature_algorithm",
MissingKey: "missing_key",
InvalidVerificationKeyAlgorithm: "invalid_verification_key_algorithm",
InvalidVerificationKey: "invalid_verification_key",
InvalidSignature: "invalid_signature",
};
const HEADER_TIMESTAMP = "x-turnkey-timestamp";
const HEADER_EVENT_ID = "x-turnkey-event-id";
const HEADER_KEY_ID = "x-turnkey-signature-key-id";
const HEADER_ALGORITHM = "x-turnkey-signature-algorithm";
const HEADER_VERSION = "x-turnkey-signature-version";
const HEADER_SIGNATURE = "x-turnkey-signature";
const SUPPORTED_VERSION = "v1";
const SUPPORTED_ALGORITHM = "ed25519";
const PUBLIC_KEY_BYTE_LENGTH = 32;
const ED25519_SIGNATURE_BYTE_LENGTH = 64;
/**
* Verifies a Turnkey webhook signature with caller-provided Ed25519
* verification keys.
*
* Pass the exact raw request body bytes Turnkey sent, the request headers, and
* a bounded `maxTimestampAgeMs` replay window. The function returns a typed
* failure result for invalid webhook input instead of throwing, so handlers can
* reject expected verification failures without exception-driven control flow.
*/
function verifyTurnkeyWebhookSignature({ headers, body, verificationKeys, maxTimestampAgeMs, nowMs = Date.now(), }) {
if (!Number.isFinite(maxTimestampAgeMs) || maxTimestampAgeMs < 0) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidMaxTimestampAge);
}
if (!Number.isFinite(nowMs)) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidNow);
}
const timestampHeader = getRequiredHeader(headers, HEADER_TIMESTAMP);
if (!timestampHeader.ok) {
return timestampHeader;
}
const eventIdHeader = getRequiredHeader(headers, HEADER_EVENT_ID);
if (!eventIdHeader.ok) {
return eventIdHeader;
}
const keyIdHeader = getRequiredHeader(headers, HEADER_KEY_ID);
if (!keyIdHeader.ok) {
return keyIdHeader;
}
const algorithmHeader = getRequiredHeader(headers, HEADER_ALGORITHM);
if (!algorithmHeader.ok) {
return algorithmHeader;
}
const versionHeader = getRequiredHeader(headers, HEADER_VERSION);
if (!versionHeader.ok) {
return versionHeader;
}
const signatureHeader = getRequiredHeader(headers, HEADER_SIGNATURE);
if (!signatureHeader.ok) {
return signatureHeader;
}
const timestampMs = parseTimestampMs(timestampHeader.value);
if (timestampMs === undefined) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidTimestamp);
}
if (Math.abs(nowMs - timestampMs) > maxTimestampAgeMs) {
return invalid(TurnkeyWebhookVerificationFailureReasons.StaleTimestamp);
}
if (versionHeader.value !== SUPPORTED_VERSION) {
return invalid(TurnkeyWebhookVerificationFailureReasons.UnsupportedSignatureVersion);
}
if (algorithmHeader.value !== SUPPORTED_ALGORITHM) {
return invalid(TurnkeyWebhookVerificationFailureReasons.UnsupportedSignatureAlgorithm);
}
const key = verificationKeys.find((candidate) => candidate.keyId === keyIdHeader.value);
if (!key) {
return invalid(TurnkeyWebhookVerificationFailureReasons.MissingKey);
}
const publicKeyResult = getPublicKeyBytes(key);
if (!publicKeyResult.ok) {
return publicKeyResult;
}
const signatureResult = hexToBytes(signatureHeader.value, ED25519_SIGNATURE_BYTE_LENGTH, TurnkeyWebhookVerificationFailureReasons.InvalidSignature);
if (!signatureResult.ok) {
return signatureResult;
}
const signedInput = buildSignedInput({
version: versionHeader.value,
algorithm: algorithmHeader.value,
keyId: keyIdHeader.value,
timestampMs: timestampHeader.value,
eventId: eventIdHeader.value,
body,
});
let verified = false;
try {
verified = ed25519.verify(signatureResult.value, signedInput, publicKeyResult.value);
}
catch {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidSignature);
}
if (!verified) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidSignature);
}
return {
ok: true,
eventId: eventIdHeader.value,
keyId: keyIdHeader.value,
timestampMs,
};
}
function buildSignedInput({ version, algorithm, keyId, timestampMs, eventId, body, }) {
const prefix = new TextEncoder().encode(`${version}.${algorithm}.${keyId}.${timestampMs}.${eventId}.`);
const bodyBytes = bodyToBytes(body);
const signedInput = new Uint8Array(prefix.length + bodyBytes.length);
signedInput.set(prefix, 0);
signedInput.set(bodyBytes, prefix.length);
return signedInput;
}
function bodyToBytes(body) {
if (typeof body === "string") {
return new TextEncoder().encode(body);
}
if (body instanceof Uint8Array) {
return body;
}
return new Uint8Array(body);
}
function getRequiredHeader(headers, name) {
const value = getHeader(headers, name);
if (value === undefined || value === "") {
return invalid(TurnkeyWebhookVerificationFailureReasons.MissingHeader, {
headerName: name,
});
}
return { ok: true, value };
}
function getHeader(headers, name) {
if (isHeaders(headers)) {
return headers.get(name) ?? undefined;
}
for (const [headerName, headerValue] of Object.entries(headers)) {
if (headerName.toLowerCase() !== name) {
continue;
}
if (Array.isArray(headerValue)) {
return headerValue[0];
}
return headerValue;
}
return undefined;
}
function isHeaders(headers) {
return typeof Headers !== "undefined" && headers instanceof Headers;
}
function parseTimestampMs(timestamp) {
if (!/^[0-9]+$/.test(timestamp)) {
return undefined;
}
const parsed = Number(timestamp);
if (!Number.isSafeInteger(parsed)) {
return undefined;
}
return parsed;
}
function getPublicKeyBytes(key) {
if (key.algorithm !== undefined && key.algorithm !== SUPPORTED_ALGORITHM) {
return invalid(TurnkeyWebhookVerificationFailureReasons.InvalidVerificationKeyAlgorithm);
}
return hexToBytes(key.publicKey, PUBLIC_KEY_BYTE_LENGTH, TurnkeyWebhookVerificationFailureReasons.InvalidVerificationKey);
}
function hexToBytes(input, expectedLength, reason) {
if (input.length !== expectedLength * 2) {
return invalid(reason);
}
try {
return {
ok: true,
value: uint8ArrayFromHexString(input),
};
}
catch {
return invalid(reason);
}
}
function invalid(reason, details = {}) {
return { ok: false, reason, ...details };
}
export { TurnkeyWebhookVerificationFailureReasons, buildSignedInput, verifyTurnkeyWebhookSignature };
//# sourceMappingURL=webhooks.mjs.map
{"version":3,"file":"webhooks.mjs","sources":["../src/webhooks.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
+11
-0
# @turnkey/crypto
## 2.10.0
### Minor Changes
- [#1314](https://github.com/tkhq/sdk/pull/1314) [`ad044cd`](https://github.com/tkhq/sdk/commit/ad044cdcd41690c6479a81c56ebcd6d5108581fb) Author [@taylorjdawson](https://github.com/taylorjdawson) - Add a helper for verifying Turnkey webhook signatures.
### Patch Changes
- Updated dependencies [[`bf2c28f`](https://github.com/tkhq/sdk/commit/bf2c28fec690ec31254125405bf15a482139109b), [`f505818`](https://github.com/tkhq/sdk/commit/f505818e121c137d290156a32918a884ba80e5f5)]:
- @turnkey/sdk-types@1.1.0
## 2.9.0

@@ -4,0 +15,0 @@

+2
-0
export * from "./crypto";
export * from "./turnkey";
export * from "./proof";
export { TurnkeyWebhookVerificationFailureReasons, verifyTurnkeyWebhookSignature, } from "./webhooks";
export type { TurnkeyWebhookBody, TurnkeyWebhookHeaders, TurnkeyWebhookVerificationFailure, TurnkeyWebhookVerificationFailureReason, TurnkeyWebhookVerificationKey, TurnkeyWebhookVerificationResult, TurnkeyWebhookVerificationSuccess, VerifyTurnkeyWebhookSignatureParams, } from "./webhooks";
export { PRODUCTION_SIGNER_SIGN_PUBLIC_KEY, PRODUCTION_NOTARIZER_SIGN_PUBLIC_KEY, PRODUCTION_TLS_FETCHER_ENCRYPT_PUBLIC_KEY, PRODUCTION_TLS_FETCHER_SIGN_PUBLIC_KEY, PRODUCTION_ON_RAMP_CREDENTIALS_ENCRYPTION_PUBLIC_KEY, } from "./constants";
//# sourceMappingURL=index.d.ts.map
+1
-1

@@ -1,1 +0,1 @@

{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,iCAAiC,EACjC,oCAAoC,EACpC,yCAAyC,EACzC,sCAAsC,EACtC,oDAAoD,GACrD,MAAM,aAAa,CAAC"}
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,OAAO,EACL,wCAAwC,EACxC,6BAA6B,GAC9B,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,iCAAiC,EACjC,uCAAuC,EACvC,6BAA6B,EAC7B,gCAAgC,EAChC,iCAAiC,EACjC,mCAAmC,GACpC,MAAM,YAAY,CAAC;AACpB,OAAO,EACL,iCAAiC,EACjC,oCAAoC,EACpC,yCAAyC,EACzC,sCAAsC,EACtC,oDAAoD,GACrD,MAAM,aAAa,CAAC"}

@@ -6,2 +6,3 @@ 'use strict';

var proof = require('./proof.js');
var webhooks = require('./webhooks.js');
var constants = require('./constants.js');

@@ -43,2 +44,4 @@

exports.verifyCoseSign1Sig = proof.verifyCoseSign1Sig;
exports.TurnkeyWebhookVerificationFailureReasons = webhooks.TurnkeyWebhookVerificationFailureReasons;
exports.verifyTurnkeyWebhookSignature = webhooks.verifyTurnkeyWebhookSignature;
exports.PRODUCTION_NOTARIZER_SIGN_PUBLIC_KEY = constants.PRODUCTION_NOTARIZER_SIGN_PUBLIC_KEY;

@@ -45,0 +48,0 @@ exports.PRODUCTION_ON_RAMP_CREDENTIALS_ENCRYPTION_PUBLIC_KEY = constants.PRODUCTION_ON_RAMP_CREDENTIALS_ENCRYPTION_PUBLIC_KEY;

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
export { buildAdditionalAssociatedData, compressRawPublicKey, extractPrivateKeyFromPKCS8Bytes, formatHpkeBuf, fromDerSignature, generateP256KeyPair, getPublicKey, hpkeAuthEncrypt, hpkeDecrypt, hpkeEncrypt, quorumKeyEncrypt, toDerSignature, uncompressRawPublicKey } from './crypto.mjs';
export { Enclave, decryptCredentialBundle, decryptExportBundle, encryptOauth2ClientSecret, encryptOnRampSecret, encryptOtpCodeToBundle, encryptPrivateKeyToBundle, encryptToEnclave, encryptWalletToBundle, verifySessionJwtSignature, verifyStampSignature } from './turnkey.mjs';
export { getCryptoInstance, verify, verifyAppProofSignature, verifyCertificateChain, verifyCoseSign1Sig } from './proof.mjs';
export { TurnkeyWebhookVerificationFailureReasons, verifyTurnkeyWebhookSignature } from './webhooks.mjs';
export { PRODUCTION_NOTARIZER_SIGN_PUBLIC_KEY, PRODUCTION_ON_RAMP_CREDENTIALS_ENCRYPTION_PUBLIC_KEY, PRODUCTION_SIGNER_SIGN_PUBLIC_KEY, PRODUCTION_TLS_FETCHER_ENCRYPT_PUBLIC_KEY, PRODUCTION_TLS_FETCHER_SIGN_PUBLIC_KEY } from './constants.mjs';
//# sourceMappingURL=index.mjs.map

@@ -1,1 +0,1 @@

{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;"}
{"version":3,"file":"index.mjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;"}
{
"name": "@turnkey/crypto",
"version": "2.9.0",
"version": "2.10.0",
"main": "./dist/index.js",

@@ -48,4 +48,4 @@ "module": "./dist/index.mjs",

"cbor-js": "0.1.0",
"@turnkey/sdk-types": "1.0.0",
"@turnkey/encoding": "0.6.0"
"@turnkey/encoding": "0.6.0",
"@turnkey/sdk-types": "1.1.0"
},

@@ -61,4 +61,4 @@ "devDependencies": {

"test": "jest",
"typecheck": "tsc -p tsconfig.typecheck.json"
"typecheck": "tsc --noEmit"
}
}

@@ -36,1 +36,37 @@ # @turnkey/crypto

```
## Verifying Turnkey webhooks
Use `@turnkey/crypto` directly when you want to manage verification-key fetching and caching yourself. Verification must use the exact raw request body bytes that Turnkey sent, the Turnkey signature headers, Turnkey webhook verification keys, and an explicit `maxTimestampAgeMs` replay window.
Turnkey sends these signature headers with the body: `x-turnkey-timestamp`, `x-turnkey-event-id`, `x-turnkey-signature-key-id`, `x-turnkey-signature-algorithm`, `x-turnkey-signature-version`, and `x-turnkey-signature`. Pass the complete headers object through as received.
`x-turnkey-event-id` is stable across retry attempts for the same webhook event. Use it as the deduplication or idempotency key after signature verification succeeds.
```ts
import { verifyTurnkeyWebhookSignature } from "@turnkey/crypto";
const body = req.body; // Buffer from express.raw(), not parsed JSON
const verificationKeys = [
{
keyId: process.env.TURNKEY_WEBHOOK_KEY_ID!,
publicKey: process.env.TURNKEY_WEBHOOK_PUBLIC_KEY!, // Hex-encoded Ed25519 public key
algorithm: "ed25519",
},
];
const result = verifyTurnkeyWebhookSignature({
headers: req.headers,
body,
verificationKeys,
maxTimestampAgeMs: 5 * 60 * 1000,
});
if (!result.ok) {
throw new Error(`Invalid Turnkey webhook: ${result.reason}`);
}
const event = JSON.parse(body.toString("utf8"));
```
Do not verify a parsed and re-stringified JSON object. Even harmless-looking changes to whitespace or key ordering will change the signed payload.