improvements: address requested changes

This commit is contained in:
Scott Wilson
2024-10-03 12:55:53 -07:00
parent 7cd85cf84a
commit c66a711890
15 changed files with 111 additions and 119 deletions
@@ -1,5 +1,6 @@
import { Knex } from "knex";
import { dropConstraintIfExists } from "@app/db/migrations/utils/dropConstraintIfExists";
import { TableName } from "@app/db/schemas";
export async function up(knex: Knex): Promise<void> {
@@ -7,12 +8,14 @@ export async function up(knex: Knex): Promise<void> {
const hasOrgId = await knex.schema.hasColumn(TableName.KmsKey, "orgId");
const hasSlug = await knex.schema.hasColumn(TableName.KmsKey, "slug");
// drop constraint if exists (won't exist if rolled back, see below)
await dropConstraintIfExists(TableName.KmsKey, "kms_keys_orgid_slug_unique", knex);
// projectId for CMEK functionality
await knex.schema.alterTable(TableName.KmsKey, (table) => {
table.string("projectId").nullable().references("id").inTable(TableName.Project).onDelete("CASCADE");
if (hasOrgId) {
table.dropUnique(["orgId", "slug"]); // prevents using the same key name in different projects so swapping constraint
table.unique(["orgId", "projectId", "slug"]);
}
@@ -36,7 +39,6 @@ export async function down(knex: Knex): Promise<void> {
if (hasOrgId) {
table.dropUnique(["orgId", "projectId", "slug"]);
table.unique(["orgId", "slug"]);
}
table.dropColumn("projectId");
});
@@ -0,0 +1,6 @@
import { Knex } from "knex";
import { TableName } from "@app/db/schemas";
export const dropConstraintIfExists = (tableName: TableName, constraintName: string, knex: Knex) =>
knex.raw(`ALTER TABLE ${tableName} DROP CONSTRAINT IF EXISTS ${constraintName};`);
@@ -2,24 +2,13 @@ import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, K
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { randomUUID } from "crypto";
import { getConfig } from "@app/lib/config/env";
import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model";
const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
const appCfg = getConfig();
if (providerInputs.credential.type === KmsAwsCredentialType.AssumeRole) {
const awsCredential = providerInputs.credential.data;
const stsClient = new STSClient({
region: providerInputs.awsRegion,
credentials:
appCfg.CLIENT_ID_AWS_INTEGRATION && appCfg.CLIENT_SECRET_AWS_INTEGRATION
? {
accessKeyId: appCfg.CLIENT_ID_AWS_INTEGRATION,
secretAccessKey: appCfg.CLIENT_SECRET_AWS_INTEGRATION
}
: undefined
region: providerInputs.awsRegion
});
const command = new AssumeRoleCommand({
RoleArn: awsCredential.assumeRoleArn,
+1 -13
View File
@@ -24,17 +24,5 @@ export const isBase64 = (
};
export const getBase64SizeInBytes = (base64String: string) => {
// Remove data URI scheme if present
const base64 = base64String.replace(/^data:.*?;base64,/, "");
// Remove padding characters
const withoutPadding = base64.replace(/=+$/, "");
// Calculate bits: each base64 character represents 6 bits
const totalBits = withoutPadding.length * 6;
// Convert bits to bytes (8 bits = 1 byte)
const bytes = totalBits / 8;
return bytes;
return Buffer.from(base64String, "base64").length;
};
+8 -8
View File
@@ -31,10 +31,10 @@ const base64Schema = z.string().superRefine((val, ctx) => {
});
}
if (getBase64SizeInBytes(val) > 6144) {
if (getBase64SizeInBytes(val) > 4096) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
message: "data cannot exceed 6144 bytes"
message: "data cannot exceed 4096 bytes"
});
}
});
@@ -65,7 +65,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
body: { projectId, name, description, encryptionAlgorithm },
@@ -118,7 +118,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
params: { keyId },
@@ -162,7 +162,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
params: { keyId },
@@ -214,7 +214,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
query: { projectId, ...dto },
@@ -259,7 +259,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
params: { keyId },
@@ -304,7 +304,7 @@ export const registerCmekRouter = async (server: FastifyZodProvider) => {
})
}
},
onRequest: verifyAuth([AuthMode.JWT]),
onRequest: verifyAuth([AuthMode.JWT, AuthMode.IDENTITY_ACCESS_TOKEN]),
handler: async (req) => {
const {
params: { keyId },
+11 -28
View File
@@ -67,16 +67,6 @@ export const kmsServiceFactory = ({
}: TKmsServiceFactoryDep) => {
let ROOT_ENCRYPTION_KEY = Buffer.alloc(0);
const $getRootEncryptionKey = (encryptionAlgorithm: SymmetricEncryption) => {
switch (encryptionAlgorithm) {
case SymmetricEncryption.AES_GCM_128:
return ROOT_ENCRYPTION_KEY.subarray(0, 16); // taking first 128bits
case SymmetricEncryption.AES_GCM_256:
default:
return ROOT_ENCRYPTION_KEY;
}
};
/*
* Generate KMS Key
* This function is responsibile for generating the infisical internal KMS for various entities
@@ -91,10 +81,11 @@ export const kmsServiceFactory = ({
encryptionAlgorithm = SymmetricEncryption.AES_GCM_256,
description
}: TGenerateKMSDTO) => {
const cipher = symmetricCipherService(encryptionAlgorithm);
const cipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const kmsKeyMaterial = randomSecureBytes(getByteLengthForAlgorithm(encryptionAlgorithm));
const encryptedKeyMaterial = cipher.encrypt(kmsKeyMaterial, $getRootEncryptionKey(encryptionAlgorithm));
const encryptedKeyMaterial = cipher.encrypt(kmsKeyMaterial, ROOT_ENCRYPTION_KEY);
const sanitizedName = name ? slugify(name) : slugify(alphaNumericNanoId(8).toLowerCase());
const dbQuery = async (db: Knex) => {
const kmsDoc = await kmsDAL.create(
@@ -308,17 +299,13 @@ export const kmsServiceFactory = ({
}
// internal KMS
const encryptionAlgorithm =
(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256;
const cipher = symmetricCipherService(encryptionAlgorithm);
const kmsKey = cipher.decrypt(
kmsDoc.internalKms?.encryptedKey as Buffer,
$getRootEncryptionKey(encryptionAlgorithm)
);
const keyCipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const dataCipher = symmetricCipherService(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption);
const kmsKey = keyCipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY);
return ({ cipherTextBlob: versionedCipherTextBlob }: Pick<TDecryptWithKmsDTO, "cipherTextBlob">) => {
const cipherTextBlob = versionedCipherTextBlob.subarray(0, -KMS_VERSION_BLOB_LENGTH);
const decryptedBlob = cipher.decrypt(cipherTextBlob, kmsKey);
const decryptedBlob = dataCipher.decrypt(cipherTextBlob, kmsKey);
return Promise.resolve(decryptedBlob);
};
};
@@ -374,15 +361,11 @@ export const kmsServiceFactory = ({
}
// internal KMS
const encryptionAlgorithm =
(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption) ?? SymmetricEncryption.AES_GCM_256;
const cipher = symmetricCipherService(encryptionAlgorithm);
const keyCipher = symmetricCipherService(SymmetricEncryption.AES_GCM_256);
const dataCipher = symmetricCipherService(kmsDoc.internalKms?.encryptionAlgorithm as SymmetricEncryption);
return ({ plainText }: Pick<TEncryptWithKmsDTO, "plainText">) => {
const kmsKey = cipher.decrypt(
kmsDoc.internalKms?.encryptedKey as Buffer,
$getRootEncryptionKey(encryptionAlgorithm)
);
const encryptedPlainTextBlob = cipher.encrypt(plainText, kmsKey);
const kmsKey = keyCipher.decrypt(kmsDoc.internalKms?.encryptedKey as Buffer, ROOT_ENCRYPTION_KEY);
const encryptedPlainTextBlob = dataCipher.encrypt(plainText, kmsKey);
// Buffer#1 encrypted text + Buffer#2 version number
const versionBlob = Buffer.from(KMS_VERSION, "utf8"); // length is 3