From 5aa4864ec4b951898bd30039c9149a0abe8e90a7 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 12 Dec 2025 19:43:14 +0530 Subject: [PATCH 1/2] fix: credentials hash calculation in external kms --- .../external-kms-endpoints.ts | 19 +++++++++++++++---- .../external-kms/external-kms-service.ts | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts b/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts index 47b4947f2..871523713 100644 --- a/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts +++ b/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts @@ -88,9 +88,11 @@ export const registerExternalKmsEndpoints = < ...rest } = externalKms; + const credentialsToHash = JSON.stringify(configuration.credential); + const credentialsHash = crypto.nativeCrypto .createHash("sha256") - .update(externalKmsData.encryptedProviderInputs) + .update(Buffer.from(credentialsToHash)) .digest("hex"); return { ...rest, externalKms: { ...externalKmsData, configuration, credentialsHash } }; } @@ -153,9 +155,12 @@ export const registerExternalKmsEndpoints = < external: { providerInput: externalKmsConfiguration, ...externalKmsData }, ...rest } = externalKms; + + const credentialsToHash = JSON.stringify(externalKmsConfiguration.credential); + const credentialsHash = crypto.nativeCrypto .createHash("sha256") - .update(externalKmsData.encryptedProviderInputs) + .update(Buffer.from(credentialsToHash)) .digest("hex"); return { ...rest, externalKms: { ...externalKmsData, configuration: externalKmsConfiguration, credentialsHash } }; } @@ -222,9 +227,12 @@ export const registerExternalKmsEndpoints = < external: { providerInput: externalKmsConfiguration, ...externalKmsData }, ...rest } = externalKms; + + const credentialsToHash = JSON.stringify(externalKmsConfiguration.credential); + const credentialsHash = crypto.nativeCrypto .createHash("sha256") - .update(externalKmsData.encryptedProviderInputs) + .update(Buffer.from(credentialsToHash)) .digest("hex"); return { ...rest, externalKms: { ...externalKmsData, configuration: externalKmsConfiguration, credentialsHash } }; } @@ -277,9 +285,12 @@ export const registerExternalKmsEndpoints = < external: { providerInput: configuration, ...externalKmsData }, ...rest } = externalKms; + + const credentialsToHash = JSON.stringify(configuration.credential); + const credentialsHash = crypto.nativeCrypto .createHash("sha256") - .update(externalKmsData.encryptedProviderInputs) + .update(Buffer.from(credentialsToHash)) .digest("hex"); return { ...rest, externalKms: { ...externalKmsData, configuration, credentialsHash } }; diff --git a/backend/src/ee/services/external-kms/external-kms-service.ts b/backend/src/ee/services/external-kms/external-kms-service.ts index eb595ee02..af246eacd 100644 --- a/backend/src/ee/services/external-kms/external-kms-service.ts +++ b/backend/src/ee/services/external-kms/external-kms-service.ts @@ -380,6 +380,7 @@ export const externalKmsServiceFactory = ({ const findById = async ({ actor, actorId, actorOrgId, actorAuthMethod, id: kmsId }: TGetExternalKmsByIdDTO) => { const kmsDoc = await kmsDAL.findById(kmsId); + if (!kmsDoc) throw new NotFoundError({ message: `Could not find KMS with ID '${kmsId}'` }); const { permission } = await permissionService.getOrgPermission({ scope: OrganizationActionScope.Any, actor, From 9a475ac8974ade44318703f1ae86ba07dd7d6fb9 Mon Sep 17 00:00:00 2001 From: Piyush Gupta Date: Fri, 12 Dec 2025 20:06:57 +0530 Subject: [PATCH 2/2] fix: review comments --- .../external-kms-endpoints.ts | 9 +++--- backend/src/lib/fn/object.ts | 31 +++++++++++++++++++ 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts b/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts index 871523713..2ae94155b 100644 --- a/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts +++ b/backend/src/ee/routes/v1/external-kms-routers/external-kms-endpoints.ts @@ -11,6 +11,7 @@ import { } from "@app/ee/services/external-kms/providers/model"; import { crypto } from "@app/lib/crypto/cryptography"; import { BadRequestError } from "@app/lib/errors"; +import { deterministicStringify } from "@app/lib/fn/object"; import { readLimit, writeLimit } from "@app/server/config/rateLimiter"; import { verifyAuth } from "@app/server/plugins/auth/verify-auth"; import { AuthMode } from "@app/services/auth/auth-type"; @@ -88,7 +89,7 @@ export const registerExternalKmsEndpoints = < ...rest } = externalKms; - const credentialsToHash = JSON.stringify(configuration.credential); + const credentialsToHash = deterministicStringify(configuration.credential); const credentialsHash = crypto.nativeCrypto .createHash("sha256") @@ -156,7 +157,7 @@ export const registerExternalKmsEndpoints = < ...rest } = externalKms; - const credentialsToHash = JSON.stringify(externalKmsConfiguration.credential); + const credentialsToHash = deterministicStringify(externalKmsConfiguration.credential); const credentialsHash = crypto.nativeCrypto .createHash("sha256") @@ -228,7 +229,7 @@ export const registerExternalKmsEndpoints = < ...rest } = externalKms; - const credentialsToHash = JSON.stringify(externalKmsConfiguration.credential); + const credentialsToHash = deterministicStringify(externalKmsConfiguration.credential); const credentialsHash = crypto.nativeCrypto .createHash("sha256") @@ -286,7 +287,7 @@ export const registerExternalKmsEndpoints = < ...rest } = externalKms; - const credentialsToHash = JSON.stringify(configuration.credential); + const credentialsToHash = deterministicStringify(configuration.credential); const credentialsHash = crypto.nativeCrypto .createHash("sha256") diff --git a/backend/src/lib/fn/object.ts b/backend/src/lib/fn/object.ts index 6ff7278aa..c437e484a 100644 --- a/backend/src/lib/fn/object.ts +++ b/backend/src/lib/fn/object.ts @@ -103,3 +103,34 @@ export const deepEqualSkipFields = (obj1: unknown, obj2: unknown, skipFields: st return deepEqual(filtered1, filtered2); }; + +export const deterministicStringify = (value: unknown): string => { + if (value === null || value === undefined) { + return JSON.stringify(value); + } + + if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") { + return JSON.stringify(value); + } + + if (Array.isArray(value)) { + const items = value.map((item) => deterministicStringify(item)); + return `[${items.join(",")}]`; + } + + if (typeof value === "object") { + const sortedKeys = Object.keys(value).sort(); + const sortedObj: Record = {}; + for (const key of sortedKeys) { + const val = (value as Record)[key]; + if (typeof val === "object" && val !== null) { + sortedObj[key] = JSON.parse(deterministicStringify(val)); + } else { + sortedObj[key] = val; + } + } + return JSON.stringify(sortedObj); + } + + return JSON.stringify(value); +};