diff --git a/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts index c8b56561e..917c55a0f 100644 --- a/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts +++ b/backend/src/ee/services/certificate-authority-crl/certificate-authority-crl-service.ts @@ -17,7 +17,7 @@ type TCertificateAuthorityCrlServiceFactoryDep = { certificateAuthorityDAL: Pick; certificateAuthorityCrlDAL: Pick; projectDAL: Pick; - kmsService: Pick; + kmsService: Pick; permissionService: Pick; licenseService: Pick; }; @@ -68,11 +68,11 @@ export const certificateAuthorityCrlServiceFactory = ({ kmsService }); - const decryptedCrl = await kmsService.decrypt({ - kmsId: keyId, - cipherTextBlob: caCrl.encryptedCrl + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: keyId }); + const decryptedCrl = kmsDecryptor({ cipherTextBlob: caCrl.encryptedCrl }); const crl = new x509.X509Crl(decryptedCrl); const base64crl = crl.toString("base64"); 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 4b3e693f9..168f6a1a7 100644 --- a/backend/src/ee/services/external-kms/external-kms-service.ts +++ b/backend/src/ee/services/external-kms/external-kms-service.ts @@ -22,7 +22,7 @@ import { ExternalKmsAwsSchema, KmsProviders } from "./providers/model"; type TExternalKmsServiceFactoryDep = { externalKmsDAL: TExternalKmsDALFactory; - kmsService: Pick; + kmsService: Pick; kmsDAL: Pick; permissionService: Pick; }; @@ -70,8 +70,10 @@ export const externalKmsServiceFactory = ({ } const orgKmsKeyId = await kmsService.getOrgKmsKeyId(actorOrgId); - const { cipherTextBlob: encryptedProviderInputs } = await kmsService.encrypt({ - kmsId: orgKmsKeyId, + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: orgKmsKeyId + }); + const { cipherTextBlob: encryptedProviderInputs } = kmsEncryptor({ plainText: Buffer.from(sanitizedProviderInput, "utf8") }); @@ -126,8 +128,10 @@ export const externalKmsServiceFactory = ({ const orgDefaultKmsId = await kmsService.getOrgKmsKeyId(kmsDoc.orgId); let sanitizedProviderInput = ""; if (provider) { - const decryptedProviderInputBlob = await kmsService.decrypt({ - kmsId: orgDefaultKmsId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: orgDefaultKmsId + }); + const decryptedProviderInputBlob = kmsDecryptor({ cipherTextBlob: externalKmsDoc.encryptedProviderInputs }); @@ -150,8 +154,10 @@ export const externalKmsServiceFactory = ({ let encryptedProviderInputs: Buffer | undefined; if (sanitizedProviderInput) { - const { cipherTextBlob } = await kmsService.encrypt({ - kmsId: orgDefaultKmsId, + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: orgDefaultKmsId + }); + const { cipherTextBlob } = kmsEncryptor({ plainText: Buffer.from(sanitizedProviderInput, "utf8") }); encryptedProviderInputs = cipherTextBlob; @@ -234,8 +240,10 @@ export const externalKmsServiceFactory = ({ if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" }); const orgDefaultKmsId = await kmsService.getOrgKmsKeyId(kmsDoc.orgId); - const decryptedProviderInputBlob = await kmsService.decrypt({ - kmsId: orgDefaultKmsId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: orgDefaultKmsId + }); + const decryptedProviderInputBlob = kmsDecryptor({ cipherTextBlob: externalKmsDoc.encryptedProviderInputs }); switch (externalKmsDoc.provider) { @@ -271,10 +279,13 @@ export const externalKmsServiceFactory = ({ if (!externalKmsDoc) throw new BadRequestError({ message: "External kms not found" }); const orgDefaultKmsId = await kmsService.getOrgKmsKeyId(kmsDoc.orgId); - const decryptedProviderInputBlob = await kmsService.decrypt({ - kmsId: orgDefaultKmsId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: orgDefaultKmsId + }); + const decryptedProviderInputBlob = kmsDecryptor({ cipherTextBlob: externalKmsDoc.encryptedProviderInputs }); + switch (externalKmsDoc.provider) { case KmsProviders.Aws: { const decryptedProviderInput = await ExternalKmsAwsSchema.parseAsync( diff --git a/backend/src/services/certificate-authority/certificate-authority-fns.ts b/backend/src/services/certificate-authority/certificate-authority-fns.ts index cf42a058e..9f98dcb83 100644 --- a/backend/src/services/certificate-authority/certificate-authority-fns.ts +++ b/backend/src/services/certificate-authority/certificate-authority-fns.ts @@ -75,8 +75,10 @@ export const getCaCredentials = async ({ kmsService }); - const decryptedPrivateKey = await kmsService.decrypt({ - kmsId: keyId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: keyId + }); + const decryptedPrivateKey = kmsDecryptor({ cipherTextBlob: caSecret.encryptedPrivateKey }); @@ -123,15 +125,17 @@ export const getCaCertChain = async ({ kmsService }); - const decryptedCaCert = await kmsService.decrypt({ - kmsId: keyId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: keyId + }); + + const decryptedCaCert = kmsDecryptor({ cipherTextBlob: caCert.encryptedCertificate }); const caCertObj = new x509.X509Certificate(decryptedCaCert); - const decryptedChain = await kmsService.decrypt({ - kmsId: keyId, + const decryptedChain = kmsDecryptor({ cipherTextBlob: caCert.encryptedCertificateChain }); @@ -168,8 +172,11 @@ export const rebuildCaCrl = async ({ kmsService }); - const privateKey = await kmsService.decrypt({ - kmsId: keyId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: keyId + }); + + const privateKey = kmsDecryptor({ cipherTextBlob: caSecret.encryptedPrivateKey }); @@ -200,8 +207,10 @@ export const rebuildCaCrl = async ({ signingKey: sk }); - const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({ - kmsId: keyId, + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: keyId + }); + const { cipherTextBlob: encryptedCrl } = kmsEncryptor({ plainText: Buffer.from(new Uint8Array(crl.rawData)) }); diff --git a/backend/src/services/certificate-authority/certificate-authority-queue.ts b/backend/src/services/certificate-authority/certificate-authority-queue.ts index 384f45c09..30da119d0 100644 --- a/backend/src/services/certificate-authority/certificate-authority-queue.ts +++ b/backend/src/services/certificate-authority/certificate-authority-queue.ts @@ -25,7 +25,7 @@ type TCertificateAuthorityQueueFactoryDep = { certificateAuthoritySecretDAL: TCertificateAuthoritySecretDALFactory; certificateDAL: TCertificateDALFactory; projectDAL: Pick; - kmsService: Pick; + kmsService: Pick; queueService: TQueueServiceFactory; }; export type TCertificateAuthorityQueueFactory = ReturnType; @@ -88,8 +88,10 @@ export const certificateAuthorityQueueFactory = ({ kmsService }); - const privateKey = await kmsService.decrypt({ - kmsId: keyId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: keyId + }); + const privateKey = kmsDecryptor({ cipherTextBlob: caSecret.encryptedPrivateKey }); @@ -120,8 +122,10 @@ export const certificateAuthorityQueueFactory = ({ signingKey: sk }); - const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({ - kmsId: keyId, + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: keyId + }); + const { cipherTextBlob: encryptedCrl } = kmsEncryptor({ plainText: Buffer.from(new Uint8Array(crl.rawData)) }); diff --git a/backend/src/services/certificate-authority/certificate-authority-service.ts b/backend/src/services/certificate-authority/certificate-authority-service.ts index 7d87545e2..afc8d7efb 100644 --- a/backend/src/services/certificate-authority/certificate-authority-service.ts +++ b/backend/src/services/certificate-authority/certificate-authority-service.ts @@ -53,7 +53,7 @@ type TCertificateAuthorityServiceFactoryDep = { certificateDAL: Pick; certificateBodyDAL: Pick; projectDAL: Pick; - kmsService: Pick; + kmsService: Pick; permissionService: Pick; }; @@ -154,11 +154,14 @@ export const certificateAuthorityServiceFactory = ({ tx ); - const keyId = await getProjectKmsCertificateKeyId({ + const certificateManagerKmsId = await getProjectKmsCertificateKeyId({ projectId: project.id, projectDAL, kmsService }); + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: certificateManagerKmsId + }); if (type === CaType.ROOT) { // note: create self-signed cert only applicable for root CA @@ -178,13 +181,11 @@ export const certificateAuthorityServiceFactory = ({ ] }); - const { cipherTextBlob: encryptedCertificate } = await kmsService.encrypt({ - kmsId: keyId, + const { cipherTextBlob: encryptedCertificate } = kmsEncryptor({ plainText: Buffer.from(new Uint8Array(cert.rawData)) }); - const { cipherTextBlob: encryptedCertificateChain } = await kmsService.encrypt({ - kmsId: keyId, + const { cipherTextBlob: encryptedCertificateChain } = kmsEncryptor({ plainText: Buffer.alloc(0) }); @@ -208,8 +209,7 @@ export const certificateAuthorityServiceFactory = ({ signingKey: keys.privateKey }); - const { cipherTextBlob: encryptedCrl } = await kmsService.encrypt({ - kmsId: keyId, + const { cipherTextBlob: encryptedCrl } = kmsEncryptor({ plainText: Buffer.from(new Uint8Array(crl.rawData)) }); @@ -224,8 +224,7 @@ export const certificateAuthorityServiceFactory = ({ // https://nodejs.org/api/crypto.html#static-method-keyobjectfromkey const skObj = KeyObject.from(keys.privateKey); - const { cipherTextBlob: encryptedPrivateKey } = await kmsService.encrypt({ - kmsId: keyId, + const { cipherTextBlob: encryptedPrivateKey } = kmsEncryptor({ plainText: skObj.export({ type: "pkcs8", format: "der" @@ -449,15 +448,17 @@ export const certificateAuthorityServiceFactory = ({ const alg = keyAlgorithmToAlgCfg(ca.keyAlgorithm as CertKeyAlgorithm); - const keyId = await getProjectKmsCertificateKeyId({ + const certificateManagerKmsId = await getProjectKmsCertificateKeyId({ projectId: ca.projectId, projectDAL, kmsService }); + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: certificateManagerKmsId + }); const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id }); - const decryptedCaCert = await kmsService.decrypt({ - kmsId: keyId, + const decryptedCaCert = kmsDecryptor({ cipherTextBlob: caCert.encryptedCertificate }); @@ -605,19 +606,20 @@ export const certificateAuthorityServiceFactory = ({ dn: parentCertSubject }); - const keyId = await getProjectKmsCertificateKeyId({ + const certificateManagerKmsId = await getProjectKmsCertificateKeyId({ projectId: ca.projectId, projectDAL, kmsService }); + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: certificateManagerKmsId + }); - const { cipherTextBlob: encryptedCertificate } = await kmsService.encrypt({ - kmsId: keyId, + const { cipherTextBlob: encryptedCertificate } = kmsEncryptor({ plainText: Buffer.from(new Uint8Array(certObj.rawData)) }); - const { cipherTextBlob: encryptedCertificateChain } = await kmsService.encrypt({ - kmsId: keyId, + const { cipherTextBlob: encryptedCertificateChain } = kmsEncryptor({ plainText: Buffer.from(certificateChain) }); @@ -682,14 +684,16 @@ export const certificateAuthorityServiceFactory = ({ const caCert = await certificateAuthorityCertDAL.findOne({ caId: ca.id }); if (!caCert) throw new BadRequestError({ message: "CA does not have a certificate installed" }); - const keyId = await getProjectKmsCertificateKeyId({ + const certificateManagerKmsId = await getProjectKmsCertificateKeyId({ projectId: ca.projectId, projectDAL, kmsService }); + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: certificateManagerKmsId + }); - const decryptedCaCert = await kmsService.decrypt({ - kmsId: keyId, + const decryptedCaCert = kmsDecryptor({ cipherTextBlob: caCert.encryptedCertificate }); @@ -796,8 +800,10 @@ export const certificateAuthorityServiceFactory = ({ const skLeafObj = KeyObject.from(leafKeys.privateKey); const skLeaf = skLeafObj.export({ format: "pem", type: "pkcs8" }) as string; - const { cipherTextBlob: encryptedCertificate } = await kmsService.encrypt({ - kmsId: keyId, + const kmsEncryptor = await kmsService.encryptWithKmsKey({ + kmsId: certificateManagerKmsId + }); + const { cipherTextBlob: encryptedCertificate } = kmsEncryptor({ plainText: Buffer.from(new Uint8Array(leafCert.rawData)) }); diff --git a/backend/src/services/certificate-authority/certificate-authority-types.ts b/backend/src/services/certificate-authority/certificate-authority-types.ts index 8af8b679c..7818c3a3e 100644 --- a/backend/src/services/certificate-authority/certificate-authority-types.ts +++ b/backend/src/services/certificate-authority/certificate-authority-types.ts @@ -95,7 +95,7 @@ export type TGetCaCredentialsDTO = { certificateAuthorityDAL: Pick; certificateAuthoritySecretDAL: Pick; projectDAL: Pick; - kmsService: Pick; + kmsService: Pick; }; export type TGetCaCertChainDTO = { @@ -103,7 +103,7 @@ export type TGetCaCertChainDTO = { certificateAuthorityDAL: Pick; certificateAuthorityCertDAL: Pick; projectDAL: Pick; - kmsService: Pick; + kmsService: Pick; }; export type TRebuildCaCrlDTO = { @@ -113,7 +113,7 @@ export type TRebuildCaCrlDTO = { certificateAuthoritySecretDAL: Pick; projectDAL: Pick; certificateDAL: Pick; - kmsService: Pick; + kmsService: Pick; }; export type TRotateCaCrlTriggerDTO = { diff --git a/backend/src/services/certificate/certificate-service.ts b/backend/src/services/certificate/certificate-service.ts index ba865caa1..401a55cc9 100644 --- a/backend/src/services/certificate/certificate-service.ts +++ b/backend/src/services/certificate/certificate-service.ts @@ -25,7 +25,7 @@ type TCertificateServiceFactoryDep = { certificateAuthorityCrlDAL: Pick; certificateAuthoritySecretDAL: Pick; projectDAL: Pick; - kmsService: Pick; + kmsService: Pick; permissionService: Pick; }; @@ -164,14 +164,16 @@ export const certificateServiceFactory = ({ const certBody = await certificateBodyDAL.findOne({ certId: cert.id }); - const keyId = await getProjectKmsCertificateKeyId({ + const certificateManagerKeyId = await getProjectKmsCertificateKeyId({ projectId: ca.projectId, projectDAL, kmsService }); - const decryptedCert = await kmsService.decrypt({ - kmsId: keyId, + const kmsDecryptor = await kmsService.decryptWithKmsKey({ + kmsId: certificateManagerKeyId + }); + const decryptedCert = kmsDecryptor({ cipherTextBlob: certBody.encryptedCertificate }); diff --git a/backend/src/services/kms/kms-service.ts b/backend/src/services/kms/kms-service.ts index 0c0f48f97..468666221 100644 --- a/backend/src/services/kms/kms-service.ts +++ b/backend/src/services/kms/kms-service.ts @@ -14,7 +14,13 @@ import { TProjectDALFactory } from "../project/project-dal"; import { TInternalKmsDALFactory } from "./internal-kms-dal"; import { TKmsKeyDALFactory } from "./kms-key-dal"; import { TKmsRootConfigDALFactory } from "./kms-root-config-dal"; -import { EncryptionMode, TGenerateKMSDTO, TKmsServiceDecryptionDTO, TKmsServiceEncryptionDTO } from "./kms-types"; +import { + TDecryptWithKeyDTO, + TDecryptWithKmsDTO, + TEncryptionWithKeyDTO, + TEncryptWithKmsDTO, + TGenerateKMSDTO +} from "./kms-types"; type TKmsServiceFactoryDep = { kmsDAL: TKmsKeyDALFactory; @@ -74,64 +80,55 @@ export const kmsServiceFactory = ({ return doc; }; - /* - * KMS encryption service - * Function to handle various kinds of encryption like - * Normal encryption - * Encrypt with KMS key - internal or external - */ - const encrypt = async (encryptionDetails: TKmsServiceEncryptionDTO) => { + const encryptWithKmsKey = async ({ kmsId }: Omit) => { + const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId); + if (!kmsDoc) throw new BadRequestError({ message: "KMS ID not found" }); // akhilmhdh: as more encryption are added do a check here on kmsDoc.encryptionAlgorithm const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); - // instead of using kms key encrypt with the provided key - if (encryptionDetails.type === EncryptionMode.EncryptionKey) { - const { plainText, encryptionKey } = encryptionDetails; + return ({ plainText }: Pick) => { + const kmsKey = cipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY); + const encryptedPlainTextBlob = cipher.encrypt(plainText, kmsKey); - const encryptedPlainTextBlob = cipher.encrypt(plainText, encryptionKey); // Buffer#1 encrypted text + Buffer#2 version number const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3 const cipherTextBlob = Buffer.concat([encryptedPlainTextBlob, versionBlob]); return { cipherTextBlob }; - } - - // this mean use kms to encrypt it - const { plainText, kmsId } = encryptionDetails; - const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId); - if (!kmsDoc) throw new BadRequestError({ message: "KMS ID not found" }); - - const kmsKey = cipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY); - const encryptedPlainTextBlob = cipher.encrypt(plainText, kmsKey); - - // Buffer#1 encrypted text + Buffer#2 version number - const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3 - const cipherTextBlob = Buffer.concat([encryptedPlainTextBlob, versionBlob]); - return { cipherTextBlob }; + }; }; - /* - * KMS decryption service - * Function to handle various kinds of decryptionlike - * Normal decryption with a key - * Encrypt with KMS key - internal or external - */ - const decrypt = async (encryptionDetails: TKmsServiceDecryptionDTO) => { + const encryptWithInputKey = async ({ key }: Omit) => { // akhilmhdh: as more encryption are added do a check here on kmsDoc.encryptionAlgorithm const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); - if (encryptionDetails.type === EncryptionMode.EncryptionKey) { - const { cipherTextBlob: versionedCipherTextBlob, encryptionKey } = encryptionDetails; - const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH); - const decryptedBlob = cipher.decrypt(cipherTextBlob, encryptionKey); - return decryptedBlob; - } + return ({ plainText }: Pick) => { + const encryptedPlainTextBlob = cipher.encrypt(plainText, key); + // Buffer#1 encrypted text + Buffer#2 version number + const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3 + const cipherTextBlob = Buffer.concat([encryptedPlainTextBlob, versionBlob]); + return { cipherTextBlob }; + }; + }; - const { cipherTextBlob: versionedCipherTextBlob, kmsId } = encryptionDetails; + const decryptWithKmsKey = async ({ kmsId }: Omit) => { const kmsDoc = await kmsDAL.findByIdWithAssociatedKms(kmsId); if (!kmsDoc) throw new BadRequestError({ message: "KMS ID not found" }); + const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); const kmsKey = cipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY); - const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH); - const decryptedBlob = cipher.decrypt(cipherTextBlob, kmsKey); - return decryptedBlob; + return ({ cipherTextBlob: versionedCipherTextBlob }: Pick) => { + const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH); + const decryptedBlob = cipher.decrypt(cipherTextBlob, kmsKey); + return decryptedBlob; + }; + }; + + const decryptWithInputKey = async ({ key }: Omit) => { + const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256); + + return ({ cipherTextBlob: versionedCipherTextBlob }: Pick) => { + const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH); + const decryptedBlob = cipher.decrypt(cipherTextBlob, key); + return decryptedBlob; + }; }; const getOrgKmsKeyId = async (orgId: string) => { @@ -246,8 +243,10 @@ export const kmsServiceFactory = ({ return { startService, generateKmsKey, - encrypt, - decrypt, + encryptWithKmsKey, + encryptWithInputKey, + decryptWithKmsKey, + decryptWithInputKey, getOrgKmsKeyId, getProjectSecretManagerKmsKeyId }; diff --git a/backend/src/services/kms/kms-types.ts b/backend/src/services/kms/kms-types.ts index e1a152f06..5ba6c1343 100644 --- a/backend/src/services/kms/kms-types.ts +++ b/backend/src/services/kms/kms-types.ts @@ -7,35 +7,22 @@ export type TGenerateKMSDTO = { tx?: Knex; }; -export enum EncryptionMode { - KMS = "kms", - EncryptionKey = "encryption-key" -} - export type TEncryptWithKmsDTO = { - type?: EncryptionMode.KMS; kmsId: string; plainText: Buffer; }; export type TEncryptionWithKeyDTO = { - type: EncryptionMode.EncryptionKey; - encryptionKey: Buffer; + key: Buffer; plainText: Buffer; }; -export type TKmsServiceEncryptionDTO = TEncryptWithKmsDTO | TEncryptionWithKeyDTO; - export type TDecryptWithKmsDTO = { - type?: EncryptionMode.KMS; kmsId: string; cipherTextBlob: Buffer; }; -export type TDecryptWithEncryptionKeyDTO = { - type: EncryptionMode.EncryptionKey; - encryptionKey: Buffer; +export type TDecryptWithKeyDTO = { + key: Buffer; cipherTextBlob: Buffer; }; - -export type TKmsServiceDecryptionDTO = TDecryptWithKmsDTO | TDecryptWithEncryptionKeyDTO;