feat: fips inside support (checkpoint)

This commit is contained in:
Daniel Hougaard
2025-07-06 15:44:07 +04:00
parent 83f0a500bd
commit 9aa3c14bf2
121 changed files with 1766 additions and 1277 deletions

View File

@@ -1,11 +1,6 @@
FROM node:20-slim
# ? Setup a test SoftHSM module. In production a real HSM is used.
ARG SOFTHSM2_VERSION=2.5.0
ENV SOFTHSM2_VERSION=${SOFTHSM2_VERSION} \
SOFTHSM2_SOURCES=/tmp/softhsm2
RUN echo "RUNNING FIPS BUILD"
# Install build dependencies including python3 (required for pkcs11js and partially TDS driver)
RUN apt-get update && apt-get install -y \
@@ -34,24 +29,10 @@ RUN apt-get install -y \
RUN printf "[FreeTDS]\nDescription = FreeTDS Driver\nDriver = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nSetup = /usr/lib/x86_64-linux-gnu/odbc/libtdsodbc.so\nFileUsage = 1\n" > /etc/odbcinst.ini
# Build and install SoftHSM2
RUN git clone https://github.com/opendnssec/SoftHSMv2.git ${SOFTHSM2_SOURCES}
WORKDIR ${SOFTHSM2_SOURCES}
RUN git checkout ${SOFTHSM2_VERSION} -b ${SOFTHSM2_VERSION} \
&& sh autogen.sh \
&& ./configure --prefix=/usr/local --disable-gost \
&& make \
&& make install
WORKDIR /root
RUN rm -fr ${SOFTHSM2_SOURCES}
# Install pkcs11-tool
RUN apt-get install -y opensc
RUN mkdir -p /etc/softhsm2/tokens && \
softhsm2-util --init-token --slot 0 --label "auth-app" --pin 1234 --so-pin 0000
WORKDIR /openssl-build
RUN wget https://www.openssl.org/source/openssl-3.1.2.tar.gz \
@@ -81,5 +62,6 @@ ENV HOST=0.0.0.0
ENV OPENSSL_CONF=/app/nodejs.cnf
ENV OPENSSL_MODULES=/usr/local/lib/ossl-modules
ENV NODE_OPTIONS=--force-fips
ENV FIPS_ENABLED=true
CMD ["npm", "run", "dev:docker"]

View File

@@ -1,8 +1,6 @@
import crypto from "node:crypto";
import { SecretType, TSecrets } from "@app/db/schemas";
import { decryptSecret, encryptSecret, getUserPrivateKey, seedData1 } from "@app/db/seed-data";
import { decryptAsymmetric, decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { SymmetricKeySize } from "@app/lib/crypto";
const createServiceToken = async (
scopes: { environment: string; secretPath: string }[],
@@ -26,15 +24,21 @@ const createServiceToken = async (
});
const { user: userInfo } = JSON.parse(userInfoRes.payload);
const privateKey = await getUserPrivateKey(seedData1.password, userInfo);
const projectKey = decryptAsymmetric({
const projectKey = testCryptoProvider.encryption().asymmetric().decrypt({
ciphertext: projectKeyEnc.encryptedKey,
nonce: projectKeyEnc.nonce,
publicKey: projectKeyEnc.sender.publicKey,
privateKey
});
const randomBytes = crypto.randomBytes(16).toString("hex");
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8(projectKey, randomBytes);
const randomBytes = testCryptoProvider.randomBytes(16).toString("hex");
const { ciphertext, iv, tag } = testCryptoProvider.encryption().encryptSymmetric({
plaintext: projectKey,
key: randomBytes,
keySize: SymmetricKeySize.Bits128
});
const serviceTokenRes = await testServer.inject({
method: "POST",
url: "/api/v2/service-token",
@@ -153,11 +157,13 @@ describe("Service token secret ops", async () => {
expect(serviceTokenInfoRes.statusCode).toBe(200);
const serviceTokenInfo = serviceTokenInfoRes.json();
const serviceTokenParts = serviceToken.split(".");
projectKey = decryptSymmetric128BitHexKeyUTF8({
projectKey = testCryptoProvider.encryption().decryptSymmetric({
key: serviceTokenParts[3],
tag: serviceTokenInfo.tag,
ciphertext: serviceTokenInfo.encryptedKey,
iv: serviceTokenInfo.iv
iv: serviceTokenInfo.iv,
keySize: SymmetricKeySize.Bits128
});
// create a deep folder
@@ -551,7 +557,7 @@ describe("Service token fail cases", async () => {
type: SecretType.Shared,
secretPath: "/",
// doesn't matter project key because this will fail before that due to read only access
...encryptSecret(crypto.randomBytes(16).toString("hex"), "NEW", "value", "")
...encryptSecret(testCryptoProvider.randomBytes(16).toString("hex"), "NEW", "value", "")
},
headers: {
authorization: `Bearer ${serviceToken}`

View File

@@ -1,6 +1,5 @@
import { SecretType, TSecrets } from "@app/db/schemas";
import { decryptSecret, encryptSecret, getUserPrivateKey, seedData1 } from "@app/db/seed-data";
import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
import { AuthMode } from "@app/services/auth/auth-type";
const createSecret = async (dto: {
@@ -173,7 +172,7 @@ describe("Secret V3 Router", async () => {
});
const { user: userInfo } = JSON.parse(userInfoRes.payload);
const privateKey = await getUserPrivateKey(seedData1.password, userInfo);
projectKey = decryptAsymmetric({
projectKey = testCryptoProvider.encryption().asymmetric().decrypt({
ciphertext: projectKeyEncryptionDetails.encryptedKey,
nonce: projectKeyEncryptionDetails.nonce,
publicKey: projectKeyEncryptionDetails.sender.publicKey,
@@ -669,7 +668,7 @@ describe.each([{ auth: AuthMode.JWT }, { auth: AuthMode.IDENTITY_ACCESS_TOKEN }]
const { user: userInfo } = JSON.parse(userInfoRes.payload);
const privateKey = await getUserPrivateKey(seedData1.password, userInfo);
const projectKey = decryptAsymmetric({
const projectKey = testCryptoProvider.encryption().asymmetric().decrypt({
ciphertext: projectKeyEnc.encryptedKey,
nonce: projectKeyEnc.nonce,
publicKey: projectKeyEnc.sender.publicKey,
@@ -685,7 +684,7 @@ describe.each([{ auth: AuthMode.JWT }, { auth: AuthMode.IDENTITY_ACCESS_TOKEN }]
});
expect(projectBotRes.statusCode).toEqual(200);
const projectBot = JSON.parse(projectBotRes.payload).bot;
const botKey = encryptAsymmetric(projectKey, projectBot.publicKey, privateKey);
const botKey = testCryptoProvider.encryption().asymmetric().encrypt(projectKey, projectBot.publicKey, privateKey);
// set bot as active
const setBotActive = await testServer.inject({

View File

@@ -17,6 +17,8 @@ import { queueServiceFactory } from "@app/queue";
import { keyStoreFactory } from "@app/keystore/keystore";
import { initializeHsmModule } from "@app/ee/services/hsm/hsm-fns";
import { buildRedisFromConfig } from "@app/lib/config/redis";
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
import { crypto } from "@app/lib/crypto/cryptography";
dotenv.config({ path: path.join(__dirname, "../../.env.test"), debug: true });
export default {
@@ -29,6 +31,8 @@ export default {
dbConnectionUri: envConfig.DB_CONNECTION_URI,
dbRootCert: envConfig.DB_ROOT_CERT
});
const superAdminDAL = superAdminDALFactory(db);
await crypto.initialize(superAdminDAL);
const redis = buildRedisFromConfig(envConfig);
await redis.flushdb("SYNC");
@@ -68,6 +72,7 @@ export default {
queue,
keyStore,
hsmModule: hsmModule.getModule(),
superAdminDAL,
redis,
envConfig
});
@@ -75,6 +80,8 @@ export default {
// @ts-expect-error type
globalThis.testServer = server;
// @ts-expect-error type
globalThis.testCryptoProvider = crypto;
// @ts-expect-error type
globalThis.jwtAuthToken = jwt.sign(
{
authTokenType: AuthTokenType.ACCESS_TOKEN,

View File

@@ -69,6 +69,7 @@
"cassandra-driver": "^4.7.2",
"connect-redis": "^7.1.1",
"cron": "^3.1.7",
"crypto-js": "^4.2.0",
"dd-trace": "^5.40.0",
"dotenv": "^16.4.1",
"fastify": "^4.28.1",
@@ -138,6 +139,7 @@
"@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.24.7",
"@types/bcrypt": "^5.0.2",
"@types/crypto-js": "^4.2.2",
"@types/jmespath": "^0.15.2",
"@types/jsonwebtoken": "^9.0.5",
"@types/jsrp": "^0.2.6",
@@ -12492,6 +12494,13 @@
"@types/node": "*"
}
},
"node_modules/@types/crypto-js": {
"version": "4.2.2",
"resolved": "https://registry.npmjs.org/@types/crypto-js/-/crypto-js-4.2.2.tgz",
"integrity": "sha512-sDOLlVbHhXpAUAL0YHDUUwDZf3iN4Bwi4W6a0W0b+QcAezUbRtH4FVb+9J4h+XFPW7l/gQ9F8qC7P+Ec4k8QVQ==",
"dev": true,
"license": "MIT"
},
"node_modules/@types/debug": {
"version": "4.1.12",
"resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
@@ -15772,6 +15781,12 @@
"node": ">= 8"
}
},
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/crypto-randomuuid": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/crypto-randomuuid/-/crypto-randomuuid-1.0.0.tgz",

View File

@@ -85,6 +85,7 @@
"@babel/preset-env": "^7.18.10",
"@babel/preset-react": "^7.24.7",
"@types/bcrypt": "^5.0.2",
"@types/crypto-js": "^4.2.2",
"@types/jmespath": "^0.15.2",
"@types/jsonwebtoken": "^9.0.5",
"@types/jsrp": "^0.2.6",
@@ -188,6 +189,7 @@
"cassandra-driver": "^4.7.2",
"connect-redis": "^7.1.1",
"cron": "^3.1.7",
"crypto-js": "^4.2.0",
"dd-trace": "^5.40.0",
"dotenv": "^16.4.1",
"fastify": "^4.28.1",

View File

@@ -1,5 +1,6 @@
import { FastifyInstance, RawReplyDefaultExpression, RawRequestDefaultExpression, RawServerDefault } from "fastify";
import { TCryptographyFactory } from "@app/lib/crypto/cryptography";
import { CustomLogger } from "@app/lib/logger/logger";
import { ZodTypeProvider } from "@app/server/plugins/fastify-zod";
@@ -14,5 +15,6 @@ declare global {
// used only for testing
const testServer: FastifyZodProvider;
const testCryptoProvider: TCryptographyFactory;
const jwtAuthToken: string;
}

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
@@ -65,7 +65,7 @@ export async function up(knex: Knex): Promise<void> {
let encryptedSecretKey = null;
if (el.encryptedSecretKey && el.iv && el.tag && el.keyEncoding) {
const decyptedSecretKey = infisicalSymmetricDecrypt({
const decyptedSecretKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: el.keyEncoding as SecretKeyEncoding,
iv: el.iv,
tag: el.tag,
@@ -78,7 +78,7 @@ export async function up(knex: Knex): Promise<void> {
const decryptedUrl =
el.urlIV && el.urlTag && el.urlCipherText && el.keyEncoding
? infisicalSymmetricDecrypt({
? crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: el.keyEncoding as SecretKeyEncoding,
iv: el.urlIV,
tag: el.urlTag,

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
@@ -60,7 +60,7 @@ export async function up(knex: Knex): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.inputIV && el.inputTag && el.inputCiphertext && el.keyEncoding
? infisicalSymmetricDecrypt({
? crypto.encryption().decryptWithRootEncryptionKey({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
keyEncoding: el.keyEncoding as SecretKeyEncoding,

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
@@ -53,7 +53,7 @@ export async function up(knex: Knex): Promise<void> {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedDataTag && el.encryptedDataIV && el.encryptedData && el.keyEncoding
? infisicalSymmetricDecrypt({
? crypto.encryption().decryptWithRootEncryptionKey({
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
keyEncoding: el.keyEncoding as SecretKeyEncoding,

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { decryptSymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
@@ -99,7 +99,7 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => {
orgEncryptionRingBuffer.push(orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
const key = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
@@ -110,8 +110,9 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedTokenReviewerJwt && el.tokenReviewerJwtIV && el.tokenReviewerJwtTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.tokenReviewerJwtIV,
@@ -128,8 +129,9 @@ const reencryptIdentityK8sAuth = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCaCert && el.caCertIV && el.caCertTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.caCertIV,

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { decryptSymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
@@ -71,7 +71,8 @@ const reencryptIdentityOidcAuth = async (knex: Knex) => {
);
orgEncryptionRingBuffer.push(orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
const key = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
@@ -82,8 +83,9 @@ const reencryptIdentityOidcAuth = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCaCert && el.caCertIV && el.caCertTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.caCertIV,

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { inMemoryKeyStore } from "@app/keystore/memory";
import { decryptSymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { selectAllTableCols } from "@app/lib/knex";
import { initLogger } from "@app/lib/logger";
import { KmsDataKey } from "@app/services/kms/kms-types";
@@ -58,7 +58,8 @@ const reencryptSamlConfig = async (knex: Knex) => {
);
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
const key = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
@@ -69,8 +70,9 @@ const reencryptSamlConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedEntryPoint && el.entryPointIV && el.entryPointTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.entryPointIV,
@@ -87,8 +89,9 @@ const reencryptSamlConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedIssuer && el.issuerIV && el.issuerTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.issuerIV,
@@ -105,8 +108,9 @@ const reencryptSamlConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCert && el.certIV && el.certTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.certIV,
@@ -216,7 +220,8 @@ const reencryptLdapConfig = async (knex: Knex) => {
);
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
const key = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
@@ -227,8 +232,9 @@ const reencryptLdapConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedBindDN && el.bindDNIV && el.bindDNTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.bindDNIV,
@@ -245,8 +251,9 @@ const reencryptLdapConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedBindPass && el.bindPassIV && el.bindPassTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.bindPassIV,
@@ -263,8 +270,9 @@ const reencryptLdapConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedCACert && el.caCertIV && el.caCertTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.caCertIV,
@@ -368,7 +376,8 @@ const reencryptOidcConfig = async (knex: Knex) => {
);
orgEncryptionRingBuffer.push(el.orgId, orgKmsService);
}
const key = infisicalSymmetricDecrypt({
const key = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
@@ -379,8 +388,9 @@ const reencryptOidcConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedClientId && el.clientIdIV && el.clientIdTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.clientIdIV,
@@ -397,8 +407,9 @@ const reencryptOidcConfig = async (knex: Knex) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
el.encryptedClientSecret && el.clientSecretIV && el.clientSecretTag
? decryptSymmetric({
? crypto.encryption().decryptSymmetric({
key,
keySize: SymmetricKeySize.Bits256,
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore This will be removed in next cycle so ignore the ts missing error
iv: el.clientSecretIV,

View File

@@ -0,0 +1,23 @@
import { Knex } from "knex";
import { TableName } from "../schemas";
export async function up(knex: Knex): Promise<void> {
const hasFipsModeColumn = await knex.schema.hasColumn(TableName.SuperAdmin, "fipsEnabled");
if (!hasFipsModeColumn) {
await knex.schema.alterTable(TableName.SuperAdmin, (table) => {
table.boolean("fipsEnabled").notNullable().defaultTo(false);
});
}
}
export async function down(knex: Knex): Promise<void> {
const hasFipsModeColumn = await knex.schema.hasColumn(TableName.SuperAdmin, "fipsEnabled");
if (hasFipsModeColumn) {
await knex.schema.alterTable(TableName.SuperAdmin, (table) => {
table.dropColumn("fipsEnabled");
});
}
}

View File

@@ -34,7 +34,9 @@ export const SuperAdminSchema = z.object({
encryptedGitHubAppConnectionClientSecret: zodBuffer.nullable().optional(),
encryptedGitHubAppConnectionSlug: zodBuffer.nullable().optional(),
encryptedGitHubAppConnectionId: zodBuffer.nullable().optional(),
encryptedGitHubAppConnectionPrivateKey: zodBuffer.nullable().optional()
encryptedGitHubAppConnectionPrivateKey: zodBuffer.nullable().optional(),
encryptedEnvOverrides: zodBuffer.nullable().optional(),
fipsEnabled: z.boolean().default(false)
});
export type TSuperAdmin = z.infer<typeof SuperAdminSchema>;

View File

@@ -1,18 +1,8 @@
/* eslint-disable import/no-mutable-exports */
import crypto from "node:crypto";
import argon2, { argon2id } from "argon2";
import jsrp from "jsrp";
import nacl from "tweetnacl";
import { encodeBase64 } from "tweetnacl-util";
import {
decryptAsymmetric,
// decryptAsymmetric,
decryptSymmetric128BitHexKeyUTF8,
encryptAsymmetric,
encryptSymmetric128BitHexKeyUTF8
} from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { TSecrets, TUserEncryptionKeys } from "./schemas";
@@ -62,11 +52,7 @@ export const seedData1 = {
};
export const generateUserSrpKeys = async (password: string) => {
const pair = nacl.box.keyPair();
const secretKeyUint8Array = pair.secretKey;
const publicKeyUint8Array = pair.publicKey;
const privateKey = encodeBase64(secretKeyUint8Array);
const publicKey = encodeBase64(publicKeyUint8Array);
const { publicKey, privateKey } = crypto.encryption().asymmetric().generateKeyPair();
// eslint-disable-next-line
const client = new jsrp.client();
@@ -98,7 +84,11 @@ export const generateUserSrpKeys = async (password: string) => {
ciphertext: encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
} = encryptSymmetric128BitHexKeyUTF8(privateKey, key);
} = crypto.encryption().encryptSymmetric({
plaintext: privateKey,
key,
keySize: SymmetricKeySize.Bits128
});
// create the protected key by encrypting the symmetric key
// [key] with the derived key
@@ -106,7 +96,9 @@ export const generateUserSrpKeys = async (password: string) => {
ciphertext: protectedKey,
iv: protectedKeyIV,
tag: protectedKeyTag
} = encryptSymmetric128BitHexKeyUTF8(key.toString("hex"), derivedKey);
} = crypto
.encryption()
.encryptSymmetric({ plaintext: key.toString("hex"), key: derivedKey, keySize: SymmetricKeySize.Bits128 });
return {
protectedKey,
@@ -133,30 +125,32 @@ export const getUserPrivateKey = async (password: string, user: TUserEncryptionK
});
if (!derivedKey) throw new Error("Failed to derive key from password");
const key = decryptSymmetric128BitHexKeyUTF8({
const key = crypto.encryption().decryptSymmetric({
ciphertext: user.protectedKey as string,
iv: user.protectedKeyIV as string,
tag: user.protectedKeyTag as string,
key: derivedKey
key: derivedKey,
keySize: SymmetricKeySize.Bits128
});
const privateKey = decryptSymmetric128BitHexKeyUTF8({
const privateKey = crypto.encryption().decryptSymmetric({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key: Buffer.from(key, "hex")
key: Buffer.from(key, "hex"),
keySize: SymmetricKeySize.Bits128
});
return privateKey;
};
export const buildUserProjectKey = (privateKey: string, publickey: string) => {
const randomBytes = crypto.randomBytes(16).toString("hex");
const { nonce, ciphertext } = encryptAsymmetric(randomBytes, publickey, privateKey);
const { nonce, ciphertext } = crypto.encryption().asymmetric().encrypt(randomBytes, publickey, privateKey);
return { nonce, ciphertext };
};
export const getUserProjectKey = async (privateKey: string, ciphertext: string, nonce: string, publicKey: string) => {
return decryptAsymmetric({
return crypto.encryption().asymmetric().decrypt({
ciphertext,
nonce,
publicKey,
@@ -170,21 +164,33 @@ export const encryptSecret = (encKey: string, key: string, value?: string, comme
ciphertext: secretKeyCiphertext,
iv: secretKeyIV,
tag: secretKeyTag
} = encryptSymmetric128BitHexKeyUTF8(key, encKey);
} = crypto.encryption().encryptSymmetric({
plaintext: key,
key: encKey,
keySize: SymmetricKeySize.Bits128
});
// encrypt value
const {
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag
} = encryptSymmetric128BitHexKeyUTF8(value ?? "", encKey);
} = crypto.encryption().encryptSymmetric({
plaintext: value ?? "",
key: encKey,
keySize: SymmetricKeySize.Bits128
});
// encrypt comment
const {
ciphertext: secretCommentCiphertext,
iv: secretCommentIV,
tag: secretCommentTag
} = encryptSymmetric128BitHexKeyUTF8(comment ?? "", encKey);
} = crypto.encryption().encryptSymmetric({
plaintext: comment ?? "",
key: encKey,
keySize: SymmetricKeySize.Bits128
});
return {
secretKeyCiphertext,
@@ -200,27 +206,30 @@ export const encryptSecret = (encKey: string, key: string, value?: string, comme
};
export const decryptSecret = (decryptKey: string, encSecret: TSecrets) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
key: decryptKey,
ciphertext: encSecret.secretKeyCiphertext,
tag: encSecret.secretKeyTag,
iv: encSecret.secretKeyIV
iv: encSecret.secretKeyIV,
keySize: SymmetricKeySize.Bits128
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
const secretValue = crypto.encryption().decryptSymmetric({
key: decryptKey,
ciphertext: encSecret.secretValueCiphertext,
tag: encSecret.secretValueTag,
iv: encSecret.secretValueIV
iv: encSecret.secretValueIV,
keySize: SymmetricKeySize.Bits128
});
const secretComment =
encSecret.secretCommentIV && encSecret.secretCommentTag && encSecret.secretCommentCiphertext
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
key: decryptKey,
ciphertext: encSecret.secretCommentCiphertext,
tag: encSecret.secretCommentTag,
iv: encSecret.secretCommentIV
iv: encSecret.secretCommentIV,
keySize: SymmetricKeySize.Bits128
})
: "";

View File

@@ -1,8 +1,6 @@
import crypto from "node:crypto";
import { Knex } from "knex";
import { encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { ProjectMembershipRole, ProjectType, SecretEncryptionAlgo, SecretKeyEncoding, TableName } from "../schemas";
import { buildUserProjectKey, getUserPrivateKey, seedData1 } from "../seed-data";
@@ -72,7 +70,11 @@ export async function seed(knex: Knex): Promise<void> {
const encKey = process.env.ENCRYPTION_KEY;
if (!encKey) throw new Error("Missing ENCRYPTION_KEY");
const salt = crypto.randomBytes(16).toString("base64");
const secretBlindIndex = encryptSymmetric128BitHexKeyUTF8(salt, encKey);
const secretBlindIndex = crypto.encryption().encryptSymmetric({
plaintext: salt,
key: encKey,
keySize: SymmetricKeySize.Bits128
});
// insert secret blind index for project
await knex(TableName.SecretBlindIndex).insert({
projectId: project.id,

View File

@@ -1,6 +1,7 @@
import bcrypt from "bcrypt";
import { Knex } from "knex";
import { crypto } from "@app/lib/crypto/cryptography";
import { IdentityAuthMethod, OrgMembershipRole, ProjectMembershipRole, TableName } from "../schemas";
import { seedData1 } from "../seed-data";
@@ -54,7 +55,9 @@ export async function seed(knex: Knex): Promise<void> {
}
])
.returning("*");
const clientSecretHash = await bcrypt.hash(seedData1.machineIdentity.clientCredentials.secret, 10);
const clientSecretHash = await crypto.hashing().createHash(seedData1.machineIdentity.clientCredentials.secret, 10);
await knex(TableName.IdentityUaClientSecret).insert([
{
identityUAId: identityUa[0].id,

View File

@@ -1,7 +1,7 @@
import bcrypt from "bcrypt";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, UnauthorizedError } from "@app/lib/errors";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
@@ -85,7 +85,7 @@ export const registerCertificateEstRouter = async (server: FastifyZodProvider) =
});
}
const isPasswordValid = await bcrypt.compare(password, estConfig.hashedPassphrase);
const isPasswordValid = await crypto.hashing().compareHash(password, estConfig.hashedPassphrase);
if (!isPasswordValid) {
throw new UnauthorizedError({
message: "Invalid credentials"

View File

@@ -4,7 +4,7 @@ import { RawAxiosRequestHeaders } from "axios";
import { SecretKeyEncoding } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
@@ -86,7 +86,10 @@ export const auditLogStreamServiceFactory = ({
.catch((err) => {
throw new BadRequestError({ message: `Failed to connect with upstream source: ${(err as Error)?.message}` });
});
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
const encryptedHeaders = headers
? crypto.encryption().encryptWithRootEncryptionKey(JSON.stringify(headers))
: undefined;
const logStream = await auditLogStreamDAL.create({
orgId: actorOrgId,
url,
@@ -152,7 +155,9 @@ export const auditLogStreamServiceFactory = ({
throw new Error(`Failed to connect with the source ${(err as Error)?.message}`);
});
const encryptedHeaders = headers ? infisicalSymmetricEncypt(JSON.stringify(headers)) : undefined;
const encryptedHeaders = headers
? crypto.encryption().encryptWithRootEncryptionKey(JSON.stringify(headers))
: undefined;
const updatedLogStream = await auditLogStreamDAL.updateById(id, {
url,
...(encryptedHeaders
@@ -205,7 +210,7 @@ export const auditLogStreamServiceFactory = ({
const headers =
logStream?.encryptedHeadersCiphertext && logStream?.encryptedHeadersIV && logStream?.encryptedHeadersTag
? (JSON.parse(
infisicalSymmetricDecrypt({
crypto.encryption().decryptWithRootEncryptionKey({
tag: logStream.encryptedHeadersTag,
iv: logStream.encryptedHeadersIV,
ciphertext: logStream.encryptedHeadersCiphertext,

View File

@@ -3,7 +3,7 @@ import { AxiosError, RawAxiosRequestHeaders } from "axios";
import { SecretKeyEncoding } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -114,7 +114,7 @@ export const auditLogQueueServiceFactory = async ({
const streamHeaders =
encryptedHeadersIV && encryptedHeadersCiphertext && encryptedHeadersTag
? (JSON.parse(
infisicalSymmetricDecrypt({
crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: encryptedHeadersKeyEncoding as SecretKeyEncoding,
iv: encryptedHeadersIV,
tag: encryptedHeadersTag,
@@ -216,7 +216,7 @@ export const auditLogQueueServiceFactory = async ({
const streamHeaders =
encryptedHeadersIV && encryptedHeadersCiphertext && encryptedHeadersTag
? (JSON.parse(
infisicalSymmetricDecrypt({
crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: encryptedHeadersKeyEncoding as SecretKeyEncoding,
iv: encryptedHeadersIV,
tag: encryptedHeadersTag,

View File

@@ -17,10 +17,10 @@ import {
RemoveUserFromGroupCommand
} from "@aws-sdk/client-iam";
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { randomUUID } from "crypto";
import { z } from "zod";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -60,7 +60,7 @@ export const AwsIamProvider = (): TDynamicProviderFns => {
const command = new AssumeRoleCommand({
RoleArn: providerInputs.roleArn,
RoleSessionName: `infisical-dynamic-secret-${randomUUID()}`,
RoleSessionName: `infisical-dynamic-secret-${crypto.rawCrypto.randomUUID()}`,
DurationSeconds: 900, // 15 mins
ExternalId: projectId
});

View File

@@ -1,8 +1,8 @@
import { randomInt } from "crypto";
import handlebars from "handlebars";
import knex from "knex";
import { z } from "zod";
import { crypto } from "@app/lib/crypto/cryptography";
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { validateHandlebarTemplate } from "@app/lib/template/validate-handlebars";
@@ -50,7 +50,7 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
parts.push(
...Array(required.lowercase)
.fill(0)
.map(() => chars.lowercase[randomInt(chars.lowercase.length)])
.map(() => chars.lowercase[crypto.randomInt(chars.lowercase.length)])
);
}
@@ -58,7 +58,7 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
parts.push(
...Array(required.uppercase)
.fill(0)
.map(() => chars.uppercase[randomInt(chars.uppercase.length)])
.map(() => chars.uppercase[crypto.randomInt(chars.uppercase.length)])
);
}
@@ -66,7 +66,7 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
parts.push(
...Array(required.digits)
.fill(0)
.map(() => chars.digits[randomInt(chars.digits.length)])
.map(() => chars.digits[crypto.randomInt(chars.digits.length)])
);
}
@@ -74,7 +74,7 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
parts.push(
...Array(required.symbols)
.fill(0)
.map(() => chars.symbols[randomInt(chars.symbols.length)])
.map(() => chars.symbols[crypto.randomInt(chars.symbols.length)])
);
}
@@ -89,12 +89,12 @@ const generatePassword = (provider: SqlProviders, requirements?: PasswordRequire
parts.push(
...Array(remainingLength)
.fill(0)
.map(() => allowedChars[randomInt(allowedChars.length)])
.map(() => allowedChars[crypto.randomInt(allowedChars.length)])
);
// shuffle the array to mix up the characters
for (let i = parts.length - 1; i > 0; i -= 1) {
const j = randomInt(i + 1);
const j = crypto.randomInt(i + 1);
[parts[i], parts[j]] = [parts[j], parts[i]];
}

View File

@@ -1,8 +1,8 @@
import { randomInt } from "crypto";
import handlebars from "handlebars";
import knex, { Knex } from "knex";
import { z } from "zod";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { GatewayProxyProtocol, withGatewayProxy } from "@app/lib/gateway";
import { logger } from "@app/lib/logger";
@@ -64,7 +64,7 @@ const generatePassword = (requirements?: PasswordRequirements) => {
parts.push(
...Array(required.lowercase)
.fill(0)
.map(() => chars.lowercase[randomInt(chars.lowercase.length)])
.map(() => chars.lowercase[crypto.randomInt(chars.lowercase.length)])
);
}
@@ -72,7 +72,7 @@ const generatePassword = (requirements?: PasswordRequirements) => {
parts.push(
...Array(required.uppercase)
.fill(0)
.map(() => chars.uppercase[randomInt(chars.uppercase.length)])
.map(() => chars.uppercase[crypto.randomInt(chars.uppercase.length)])
);
}
@@ -80,7 +80,7 @@ const generatePassword = (requirements?: PasswordRequirements) => {
parts.push(
...Array(required.digits)
.fill(0)
.map(() => chars.digits[randomInt(chars.digits.length)])
.map(() => chars.digits[crypto.randomInt(chars.digits.length)])
);
}
@@ -88,7 +88,7 @@ const generatePassword = (requirements?: PasswordRequirements) => {
parts.push(
...Array(required.symbols)
.fill(0)
.map(() => chars.symbols[randomInt(chars.symbols.length)])
.map(() => chars.symbols[crypto.randomInt(chars.symbols.length)])
);
}
@@ -103,12 +103,12 @@ const generatePassword = (requirements?: PasswordRequirements) => {
parts.push(
...Array(remainingLength)
.fill(0)
.map(() => allowedChars[randomInt(allowedChars.length)])
.map(() => allowedChars[crypto.randomInt(allowedChars.length)])
);
// shuffle the array to mix up the characters
for (let i = parts.length - 1; i > 0; i -= 1) {
const j = randomInt(i + 1);
const j = crypto.randomInt(i + 1);
[parts[i], parts[j]] = [parts[j], parts[i]];
}

View File

@@ -1,6 +1,7 @@
import { CreateKeyCommand, DecryptCommand, DescribeKeyCommand, EncryptCommand, KMSClient } from "@aws-sdk/client-kms";
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import { randomUUID } from "crypto";
import { crypto } from "@app/lib/crypto/cryptography";
import { ExternalKmsAwsSchema, KmsAwsCredentialType, TExternalKmsAwsSchema, TExternalKmsProviderFns } from "./model";
@@ -12,7 +13,7 @@ const getAwsKmsClient = async (providerInputs: TExternalKmsAwsSchema) => {
});
const command = new AssumeRoleCommand({
RoleArn: awsCredential.assumeRoleArn,
RoleSessionName: `infisical-kms-${randomUUID()}`,
RoleSessionName: `infisical-kms-${crypto.rawCrypto.randomUUID()}`,
DurationSeconds: 900, // 15mins
ExternalId: awsCredential.externalId
});

View File

@@ -1,11 +1,10 @@
import crypto from "node:crypto";
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { z } from "zod";
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { pingGatewayAndVerify } from "@app/lib/gateway";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -149,9 +148,9 @@ export const gatewayServiceFactory = ({
const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048);
// generate root CA
const rootCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const rootCaKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const rootCaSerialNumber = createSerialNumber();
const rootCaSkObj = crypto.KeyObject.from(rootCaKeys.privateKey);
const rootCaSkObj = crypto.rawCrypto.KeyObject.from(rootCaKeys.privateKey);
const rootCaIssuedAt = new Date();
const rootCaKeyAlgorithm = CertKeyAlgorithm.RSA_2048;
const rootCaExpiration = new Date(new Date().setFullYear(2045));
@@ -173,8 +172,8 @@ export const gatewayServiceFactory = ({
const clientCaSerialNumber = createSerialNumber();
const clientCaIssuedAt = new Date();
const clientCaExpiration = new Date(new Date().setFullYear(2045));
const clientCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const clientCaSkObj = crypto.KeyObject.from(clientCaKeys.privateKey);
const clientCaKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const clientCaSkObj = crypto.rawCrypto.KeyObject.from(clientCaKeys.privateKey);
const clientCaCert = await x509.X509CertificateGenerator.create({
serialNumber: clientCaSerialNumber,
@@ -200,7 +199,7 @@ export const gatewayServiceFactory = ({
]
});
const clientKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const clientKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const clientCertSerialNumber = createSerialNumber();
const clientCert = await x509.X509CertificateGenerator.create({
serialNumber: clientCertSerialNumber,
@@ -226,14 +225,14 @@ export const gatewayServiceFactory = ({
new x509.ExtendedKeyUsageExtension([x509.ExtendedKeyUsage[CertExtendedKeyUsage.CLIENT_AUTH]], true)
]
});
const clientSkObj = crypto.KeyObject.from(clientKeys.privateKey);
const clientSkObj = crypto.rawCrypto.KeyObject.from(clientKeys.privateKey);
// generate gateway ca
const gatewayCaSerialNumber = createSerialNumber();
const gatewayCaIssuedAt = new Date();
const gatewayCaExpiration = new Date(new Date().setFullYear(2045));
const gatewayCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const gatewayCaSkObj = crypto.KeyObject.from(gatewayCaKeys.privateKey);
const gatewayCaKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const gatewayCaSkObj = crypto.rawCrypto.KeyObject.from(gatewayCaKeys.privateKey);
const gatewayCaCert = await x509.X509CertificateGenerator.create({
serialNumber: gatewayCaSerialNumber,
subject: `O=${identityOrg},CN=Gateway CA`,
@@ -326,7 +325,7 @@ export const gatewayServiceFactory = ({
);
const gatewayCaAlg = keyAlgorithmToAlgCfg(orgGatewayConfig.rootCaKeyAlgorithm as CertKeyAlgorithm);
const gatewayCaSkObj = crypto.createPrivateKey({
const gatewayCaSkObj = crypto.rawCrypto.createPrivateKey({
key: orgKmsDecryptor({ cipherTextBlob: orgGatewayConfig.encryptedGatewayCaPrivateKey }),
format: "der",
type: "pkcs8"
@@ -337,7 +336,7 @@ export const gatewayServiceFactory = ({
})
);
const gatewayCaPrivateKey = await crypto.subtle.importKey(
const gatewayCaPrivateKey = await crypto.rawCrypto.subtle.importKey(
"pkcs8",
gatewayCaSkObj.export({ format: "der", type: "pkcs8" }),
gatewayCaAlg,
@@ -346,7 +345,7 @@ export const gatewayServiceFactory = ({
);
const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048);
const gatewayKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const gatewayKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const certIssuedAt = new Date();
// then need to periodically init
const certExpireAt = new Date(new Date().setMonth(new Date().getMonth() + 1));
@@ -367,7 +366,7 @@ export const gatewayServiceFactory = ({
];
const serialNumber = createSerialNumber();
const privateKey = crypto.KeyObject.from(gatewayKeys.privateKey);
const privateKey = crypto.rawCrypto.KeyObject.from(gatewayKeys.privateKey);
const gatewayCertificate = await x509.X509CertificateGenerator.create({
serialNumber,
subject: `CN=${identityId},O=${identityOrg},OU=Gateway`,
@@ -454,7 +453,7 @@ export const gatewayServiceFactory = ({
})
);
const privateKey = crypto
const privateKey = crypto.rawCrypto
.createPrivateKey({
key: orgKmsDecryptor({ cipherTextBlob: orgGatewayConfig.encryptedClientPrivateKey }),
format: "der",
@@ -588,7 +587,7 @@ export const gatewayServiceFactory = ({
})
);
const clientSkObj = crypto.createPrivateKey({
const clientSkObj = crypto.rawCrypto.createPrivateKey({
key: orgKmsDecryptor({ cipherTextBlob: orgGatewayConfig.encryptedClientPrivateKey }),
format: "der",
type: "pkcs8"

View File

@@ -1,7 +1,7 @@
import { Knex } from "knex";
import { SecretKeyEncoding, TableName, TUsers } from "@app/db/schemas";
import { decryptAsymmetric, encryptAsymmetric, infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError, ScimRequestError } from "@app/lib/errors";
import {
@@ -94,14 +94,14 @@ const addAcceptedUsersToGroup = async ({
});
}
const botPrivateKey = infisicalSymmetricDecrypt({
const botPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
iv: bot.iv,
tag: bot.tag,
ciphertext: bot.encryptedPrivateKey
});
const plaintextProjectKey = decryptAsymmetric({
const plaintextProjectKey = crypto.encryption().asymmetric().decrypt({
ciphertext: ghostUserLatestKey.encryptedKey,
nonce: ghostUserLatestKey.nonce,
publicKey: ghostUserLatestKey.sender.publicKey,
@@ -109,11 +109,10 @@ const addAcceptedUsersToGroup = async ({
});
const projectKeysToAdd = usersToAddProjectKeyFor.map((user) => {
const { ciphertext: encryptedKey, nonce } = encryptAsymmetric(
plaintextProjectKey,
user.publicKey,
botPrivateKey
);
const { ciphertext: encryptedKey, nonce } = crypto
.encryption()
.asymmetric()
.encrypt(plaintextProjectKey, user.publicKey, botPrivateKey);
return {
encryptedKey,
nonce,

View File

@@ -1,8 +1,8 @@
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import crypto, { KeyObject } from "crypto";
import { ActionProjectType } from "@app/db/schemas";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
import { isValidIp } from "@app/lib/ip";
import { ms } from "@app/lib/ms";
@@ -299,7 +299,7 @@ export const kmipServiceFactory = ({
}
const alg = keyAlgorithmToAlgCfg(keyAlgorithm);
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const leafKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false),
@@ -318,13 +318,13 @@ export const kmipServiceFactory = ({
const caAlg = keyAlgorithmToAlgCfg(kmipConfig.caKeyAlgorithm as CertKeyAlgorithm);
const caSkObj = crypto.createPrivateKey({
const caSkObj = crypto.rawCrypto.createPrivateKey({
key: decryptor({ cipherTextBlob: kmipConfig.encryptedClientIntermediateCaPrivateKey }),
format: "der",
type: "pkcs8"
});
const caPrivateKey = await crypto.subtle.importKey(
const caPrivateKey = await crypto.rawCrypto.subtle.importKey(
"pkcs8",
caSkObj.export({ format: "der", type: "pkcs8" }),
caAlg,
@@ -345,7 +345,7 @@ export const kmipServiceFactory = ({
extensions
});
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const skLeafObj = crypto.rawCrypto.KeyObject.from(leafKeys.privateKey);
const rootCaCert = new x509.X509Certificate(decryptor({ cipherTextBlob: kmipConfig.encryptedRootCaCertificate }));
const serverIntermediateCaCert = new x509.X509Certificate(
@@ -424,8 +424,8 @@ export const kmipServiceFactory = ({
// generate root CA
const rootCaSerialNumber = createSerialNumber();
const rootCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const rootCaSkObj = KeyObject.from(rootCaKeys.privateKey);
const rootCaKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const rootCaSkObj = crypto.rawCrypto.KeyObject.from(rootCaKeys.privateKey);
const rootCaIssuedAt = new Date();
const rootCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 20));
@@ -447,8 +447,8 @@ export const kmipServiceFactory = ({
const serverIntermediateCaSerialNumber = createSerialNumber();
const serverIntermediateCaIssuedAt = new Date();
const serverIntermediateCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
const serverIntermediateCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const serverIntermediateCaSkObj = KeyObject.from(serverIntermediateCaKeys.privateKey);
const serverIntermediateCaKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const serverIntermediateCaSkObj = crypto.rawCrypto.KeyObject.from(serverIntermediateCaKeys.privateKey);
const serverIntermediateCaCert = await x509.X509CertificateGenerator.create({
serialNumber: serverIntermediateCaSerialNumber,
@@ -478,8 +478,8 @@ export const kmipServiceFactory = ({
const clientIntermediateCaSerialNumber = createSerialNumber();
const clientIntermediateCaIssuedAt = new Date();
const clientIntermediateCaExpiration = new Date(new Date().setFullYear(new Date().getFullYear() + 10));
const clientIntermediateCaKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const clientIntermediateCaSkObj = KeyObject.from(clientIntermediateCaKeys.privateKey);
const clientIntermediateCaKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const clientIntermediateCaSkObj = crypto.rawCrypto.KeyObject.from(clientIntermediateCaKeys.privateKey);
const clientIntermediateCaCert = await x509.X509CertificateGenerator.create({
serialNumber: clientIntermediateCaSerialNumber,
@@ -644,7 +644,8 @@ export const kmipServiceFactory = ({
}
const alg = keyAlgorithmToAlgCfg(keyAlgorithm);
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const leafKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const extensions: x509.Extension[] = [
new x509.BasicConstraintsExtension(false),
@@ -692,13 +693,13 @@ export const kmipServiceFactory = ({
cipherTextBlob: kmipOrgConfig.encryptedServerIntermediateCaChain
}).toString("utf-8");
const caSkObj = crypto.createPrivateKey({
const caSkObj = crypto.rawCrypto.createPrivateKey({
key: decryptor({ cipherTextBlob: kmipOrgConfig.encryptedServerIntermediateCaPrivateKey }),
format: "der",
type: "pkcs8"
});
const caPrivateKey = await crypto.subtle.importKey(
const caPrivateKey = await crypto.rawCrypto.subtle.importKey(
"pkcs8",
caSkObj.export({ format: "der", type: "pkcs8" }),
caAlg,
@@ -719,7 +720,7 @@ export const kmipServiceFactory = ({
extensions
});
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const skLeafObj = crypto.rawCrypto.KeyObject.from(leafKeys.privateKey);
const certificateChain = `${caCertObj.toString("pem")}\n${decryptedCaCertChain}`.trim();
await kmipOrgServerCertificateDAL.create({

View File

@@ -12,7 +12,7 @@ import {
TSecretApprovalRequestsSecretsV2Insert
} from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy, pick, unique } from "@app/lib/fn";
import { setKnexStringValue } from "@app/lib/knex";
@@ -819,11 +819,12 @@ export const secretApprovalRequestServiceFactory = ({
type: SecretType.Shared,
references: botKey
? getAllNestedSecretReferences(
decryptSymmetric128BitHexKeyUTF8({
crypto.encryption().decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
)
: undefined
@@ -864,11 +865,12 @@ export const secretApprovalRequestServiceFactory = ({
]),
references: botKey
? getAllNestedSecretReferences(
decryptSymmetric128BitHexKeyUTF8({
crypto.encryption().decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
)
: undefined

View File

@@ -3,7 +3,7 @@ import { TSecretApprovalPolicyServiceFactory } from "@app/ee/services/secret-app
import { TSecretApprovalRequestDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-dal";
import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-secret-dal";
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { groupBy, unique } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
@@ -100,18 +100,20 @@ const getReplicationKeyLockPrefix = (projectId: string, environmentSlug: string,
export const getReplicationFolderName = (importId: string) => `${ReservedFolders.SecretReplication}${importId}`;
const getDecryptedKeyValue = (key: string, secret: TSecrets) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
const secretValue = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
return { key: secretKey, value: secretValue };
};

View File

@@ -1,4 +1,4 @@
import { randomInt } from "crypto";
import { crypto } from "@app/lib/crypto/cryptography";
type TPasswordRequirements = {
length: number;
@@ -39,7 +39,7 @@ export const generatePassword = (passwordRequirements?: TPasswordRequirements) =
parts.push(
...Array(required.lowercase)
.fill(0)
.map(() => chars.lowercase[randomInt(chars.lowercase.length)])
.map(() => chars.lowercase[crypto.randomInt(chars.lowercase.length)])
);
}
@@ -47,7 +47,7 @@ export const generatePassword = (passwordRequirements?: TPasswordRequirements) =
parts.push(
...Array(required.uppercase)
.fill(0)
.map(() => chars.uppercase[randomInt(chars.uppercase.length)])
.map(() => chars.uppercase[crypto.randomInt(chars.uppercase.length)])
);
}
@@ -55,7 +55,7 @@ export const generatePassword = (passwordRequirements?: TPasswordRequirements) =
parts.push(
...Array(required.digits)
.fill(0)
.map(() => chars.digits[randomInt(chars.digits.length)])
.map(() => chars.digits[crypto.randomInt(chars.digits.length)])
);
}
@@ -63,7 +63,7 @@ export const generatePassword = (passwordRequirements?: TPasswordRequirements) =
parts.push(
...Array(required.symbols)
.fill(0)
.map(() => chars.symbols[randomInt(chars.symbols.length)])
.map(() => chars.symbols[crypto.randomInt(chars.symbols.length)])
);
}
@@ -78,12 +78,12 @@ export const generatePassword = (passwordRequirements?: TPasswordRequirements) =
parts.push(
...Array(remainingLength)
.fill(0)
.map(() => allowedChars[randomInt(allowedChars.length)])
.map(() => allowedChars[crypto.randomInt(allowedChars.length)])
);
// shuffle the array to mix up the characters
for (let i = parts.length - 1; i > 0; i -= 1) {
const j = randomInt(i + 1);
const j = crypto.randomInt(i + 1);
[parts[i], parts[j]] = [parts[j], parts[i]];
}

View File

@@ -7,7 +7,7 @@ import {
import { SecretType } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto/encryption";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
import { NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -117,6 +117,7 @@ export const secretRotationQueueFactory = ({
queue.start(QueueName.SecretRotation, async (job) => {
const { rotationId } = job.data;
const appCfg = getConfig();
logger.info(`secretRotationQueue.process: [rotationDocument=${rotationId}]`);
const secretRotation = await secretRotationDAL.findById(rotationId);
const rotationProvider = rotationTemplates.find(({ name }) => name === secretRotation?.provider);
@@ -365,15 +366,19 @@ export const secretRotationQueueFactory = ({
throw new NotFoundError({
message: `Project bot not found for project with ID '${secretRotation.projectId}'`
});
const encryptedSecrets = rotationOutputs.map(({ key: outputKey, secretId }) => ({
secretId,
value: encryptSymmetric128BitHexKeyUTF8(
typeof newCredential.outputs[outputKey] === "object"
? JSON.stringify(newCredential.outputs[outputKey])
: String(newCredential.outputs[outputKey]),
botKey
)
value: crypto.encryption().encryptSymmetric({
plaintext:
typeof newCredential.outputs[outputKey] === "object"
? JSON.stringify(newCredential.outputs[outputKey])
: String(newCredential.outputs[outputKey]),
key: botKey,
keySize: SymmetricKeySize.Bits128
})
}));
// map the final values to output keys in the board
await secretRotationDAL.transaction(async (tx) => {
await secretRotationDAL.updateById(

View File

@@ -2,7 +2,7 @@ import { ForbiddenError, subject } from "@casl/ability";
import Ajv from "ajv";
import { ActionProjectType, ProjectVersion, TableName } from "@app/db/schemas";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto/encryption";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TProjectPermission } from "@app/lib/types";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
@@ -230,6 +230,7 @@ export const secretRotationServiceFactory = ({
if (!botKey) throw new NotFoundError({ message: `Project bot not found for project with ID '${projectId}'` });
const docs = await secretRotationDAL.find({ projectId });
return docs.map((el) => ({
...el,
outputs: el.outputs.map((output) => ({
@@ -237,11 +238,12 @@ export const secretRotationServiceFactory = ({
secret: {
id: output.secret.id,
version: output.secret.version,
secretKey: decryptSymmetric128BitHexKeyUTF8({
secretKey: crypto.encryption().decryptSymmetric({
ciphertext: output.secret.secretKeyCiphertext,
iv: output.secret.secretKeyIV,
tag: output.secret.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
}
}))

View File

@@ -1,5 +1,3 @@
import crypto from "node:crypto";
import { ForbiddenError } from "@casl/ability";
import { WebhookEventMap } from "@octokit/webhooks-types";
import { ProbotOctokit } from "probot";
@@ -7,6 +5,7 @@ import { ProbotOctokit } from "probot";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { TGitAppDALFactory } from "./git-app-dal";

View File

@@ -3,7 +3,7 @@
import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, TableName, TSecretTagJunctionInsert, TSecretV2TagJunctionInsert } from "@app/db/schemas";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { InternalServerError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
@@ -236,14 +236,16 @@ export const secretSnapshotServiceFactory = ({
const { botKey } = await projectBotService.getBotKey(snapshot.projectId);
if (!botKey)
throw new NotFoundError({ message: `Project bot key not found for project with ID '${snapshot.projectId}'` });
snapshotDetails = {
...encryptedSnapshotDetails,
secretVersions: encryptedSnapshotDetails.secretVersions.map((el) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: el.secretKeyCiphertext,
iv: el.secretKeyIV,
tag: el.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const canReadValue = hasSecretReadValueOrDescribePermission(
@@ -260,11 +262,12 @@ export const secretSnapshotServiceFactory = ({
let secretValue = "";
if (canReadValue) {
secretValue = decryptSymmetric128BitHexKeyUTF8({
secretValue = crypto.encryption().decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
} else {
secretValue = INFISICAL_SECRET_VALUE_HIDDEN_MASK;
@@ -277,11 +280,12 @@ export const secretSnapshotServiceFactory = ({
secretValue,
secretComment:
el.secretCommentTag && el.secretCommentIV && el.secretCommentCiphertext
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.secretCommentCiphertext,
iv: el.secretCommentIV,
tag: el.secretCommentTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: ""
};

View File

@@ -1,5 +1,4 @@
import { execFile } from "child_process";
import crypto from "crypto";
import { promises as fs } from "fs";
import { Knex } from "knex";
import os from "os";
@@ -9,6 +8,7 @@ import { promisify } from "util";
import { TSshCertificateTemplates } from "@app/db/schemas";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { ms } from "@app/lib/ms";
import { CharacterType, characterValidator } from "@app/lib/validator/validate-string";

View File

@@ -1,7 +1,7 @@
import crypto from "node:crypto";
import { AxiosError, AxiosInstance, AxiosRequestConfig } from "axios";
import { crypto, DigestType } from "../crypto/cryptography";
export const createDigestAuthRequestInterceptor = (
axiosInstance: AxiosInstance,
username: string,
@@ -30,18 +30,13 @@ export const createDigestAuthRequestInterceptor = (
const cnonce = crypto.randomBytes(24).toString("hex");
const realm = authDetails.find((el) => el[0].toLowerCase().indexOf("realm") > -1)?.[1]?.replaceAll('"', "") || "";
const nonce = authDetails.find((el) => el[0].toLowerCase().indexOf("nonce") > -1)?.[1]?.replaceAll('"', "") || "";
const ha1 = crypto.createHash("md5").update(`${username}:${realm}:${password}`).digest("hex");
const ha1 = crypto.hashing().md5(`${username}:${realm}:${password}`, DigestType.Hex);
const path = opts.url;
const ha2 = crypto
.createHash("md5")
.update(`${opts.method ?? "GET"}:${path}`)
.digest("hex");
const ha2 = crypto.hashing().md5(`${opts.method ?? "GET"}:${path}`, DigestType.Hex);
const response = crypto.hashing().md5(`${ha1}:${nonce}:${nonceCount}:${cnonce}:auth:${ha2}`, DigestType.Hex);
const response = crypto
.createHash("md5")
.update(`${ha1}:${nonce}:${nonceCount}:${cnonce}:auth:${ha2}`)
.digest("hex");
const authorization = `Digest username="${username}",realm="${realm}",nonce="${nonce}",uri="${path}",qop="auth",algorithm="MD5",response="${response}",nc="${nonceCount}",cnonce="${cnonce}"`;
if (opts.headers) {

View File

@@ -63,7 +63,7 @@ const envSchema = z
DB_PASSWORD: zpStr(z.string().describe("Postgres database password").optional()),
DB_NAME: zpStr(z.string().describe("Postgres database name").optional()),
DB_READ_REPLICAS: zpStr(z.string().describe("Postgres read replicas").optional()),
BCRYPT_SALT_ROUND: z.number().default(12),
BCRYPT_SALT_ROUND: z.number().optional(), // note(daniel): this is deprecated, use SALT_ROUNDS instead. only keeping this for backwards compatibility.
NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
SALT_ROUNDS: z.coerce.number().default(10),
INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()),
@@ -306,6 +306,7 @@ const envSchema = z
)
.transform((data) => ({
...data,
SALT_ROUNDS: data.SALT_ROUNDS || data.BCRYPT_SALT_ROUND || 12,
DB_READ_REPLICAS: data.DB_READ_REPLICAS
? databaseReadReplicaSchema.parse(JSON.parse(data.DB_READ_REPLICAS))
: undefined,

View File

@@ -1,10 +1,4 @@
import crypto from "node:crypto";
import { crypto } from "@app/lib/crypto/cryptography";
export const generateCacheKeyFromData = (data: unknown) =>
crypto
.createHash("md5")
.update(JSON.stringify(data))
.digest("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
crypto.rawCrypto.createHash("sha256").update(JSON.stringify(data)).digest("base64");

View File

@@ -1,24 +1,16 @@
import crypto from "crypto";
import { crypto } from "@app/lib/crypto/cryptography";
import { SymmetricKeyAlgorithm, TSymmetricEncryptionFns } from "./types";
const getIvLength = () => {
return 12;
};
const getTagLength = () => {
return 16;
};
const IV_LENGTH = 12;
const TAG_LENGTH = 16;
export const symmetricCipherService = (
type: SymmetricKeyAlgorithm.AES_GCM_128 | SymmetricKeyAlgorithm.AES_GCM_256
): TSymmetricEncryptionFns => {
const IV_LENGTH = getIvLength();
const TAG_LENGTH = getTagLength();
const encrypt = (text: Buffer, key: Buffer) => {
const iv = crypto.randomBytes(IV_LENGTH);
const cipher = crypto.createCipheriv(type, key, iv);
const cipher = crypto.rawCrypto.createCipheriv(type, key, iv);
let encrypted = cipher.update(text);
encrypted = Buffer.concat([encrypted, cipher.final()]);
@@ -37,7 +29,7 @@ export const symmetricCipherService = (
const tag = ciphertextBlob.subarray(-TAG_LENGTH);
const encrypted = ciphertextBlob.subarray(IV_LENGTH, -TAG_LENGTH);
const decipher = crypto.createDecipheriv(type, key, iv);
const decipher = crypto.rawCrypto.createDecipheriv(type, key, iv);
decipher.setAuthTag(tag);
const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);

View File

@@ -0,0 +1,630 @@
// NOTE: DO NOT USE crypto-js ANYWHERE EXCEPT THIS FILE.
// We use crypto-js purely to get around our native node crypto FIPS restrictions in FIPS mode.
import crypto, { subtle } from "node:crypto";
import bcrypt from "bcrypt";
import cryptoJs from "crypto-js";
import nacl from "tweetnacl";
import naclUtils from "tweetnacl-util";
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
import { ADMIN_CONFIG_DB_UUID } from "@app/services/super-admin/super-admin-service";
import { getConfig } from "../config/env";
import { logger } from "../logger";
enum DigestType {
Hex = "hex",
Base64 = "base64"
}
export enum SymmetricKeySize {
Bits128 = "128-bits",
Bits256 = "256-bits"
}
type TDecryptSymmetricInput =
| {
ciphertext: string;
iv: string;
tag: string;
key: string | Buffer; // can be hex encoded or buffer
keySize: SymmetricKeySize.Bits128;
}
| {
ciphertext: string;
iv: string;
tag: string;
key: string; // must be base64 encoded
keySize: SymmetricKeySize.Bits256;
};
type TEncryptSymmetricInput =
| {
plaintext: string;
key: string;
keySize: SymmetricKeySize.Bits256;
}
| {
plaintext: string;
key: string | Buffer;
keySize: SymmetricKeySize.Bits128;
};
type TDecryptAsymmetricInput = {
ciphertext: string;
nonce: string;
publicKey: string;
privateKey: string;
};
const IV_BYTES_SIZE = 12;
const BLOCK_SIZE_BYTES_16 = 16;
const hasherFipsValidated = () => {
const $hashPassword = (password: string, salt: string, iterations: number, keyLength: number) => {
return new Promise<string>((resolve, reject) => {
crypto.pbkdf2(password, salt, iterations, keyLength, "sha256", (err, derivedKey) => {
if (err) {
return reject(err);
}
resolve(derivedKey.toString("hex"));
});
});
};
const $validatePassword = (
inputPassword: string,
storedHash: string,
salt: string,
iterations: number,
keyLength: number
) => {
return $hashPassword(inputPassword, salt, iterations, keyLength).then((hash) => hash === storedHash);
};
const hash = async (password: string, saltRounds: number) => {
const salt = crypto.randomBytes(16).toString("hex");
const derivedKey = await $hashPassword(password, salt, saltRounds, 32);
return `$infisical$${saltRounds}$${salt}$${derivedKey}`;
};
const compare = async (password: string, hashedPassword: string) => {
if (!hashedPassword.startsWith("$infisical$")) {
throw new Error("Invalid hash format");
}
const [, , iterations, salt, storedHash] = hashedPassword.split("$");
return $validatePassword(password, storedHash, salt, Number(iterations), 32);
};
return {
hash,
compare
};
};
const generateAsymmetricKeyPairFipsValidated = () => {
const { publicKey, privateKey } = crypto.generateKeyPairSync("x25519");
return {
publicKey: publicKey.export({ type: "spki", format: "der" }).toString("base64"),
privateKey: privateKey.export({ type: "pkcs8", format: "der" }).toString("base64")
};
};
const encryptAsymmetricFipsValidated = (data: string, publicKey: string, privateKey: string) => {
const pubKeyObj = crypto.createPublicKey({
key: Buffer.from(publicKey, "base64"),
type: "spki",
format: "der"
});
const privKeyObj = crypto.createPrivateKey({
key: Buffer.from(privateKey, "base64"),
type: "pkcs8",
format: "der"
});
// Generate shared secret using X25519
const sharedSecret = crypto.diffieHellman({
privateKey: privKeyObj,
publicKey: pubKeyObj
});
// Generate 24-byte nonce (same as NaCl)
const nonce = crypto.randomBytes(24);
// Derive 32-byte key from shared secret
const key = crypto.createHash("sha256").update(sharedSecret).digest();
// Use first 12 bytes of nonce as IV for AES-GCM
const iv = nonce.subarray(0, 12);
// Encrypt with AES-256-GCM
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ciphertext = cipher.update(data, "utf8");
cipher.final();
const authTag = cipher.getAuthTag();
// Combine ciphertext and auth tag
const combined = Buffer.concat([ciphertext, authTag]);
return {
ciphertext: combined.toString("base64"),
nonce: nonce.toString("base64")
};
};
const decryptAsymmetricFipsValidated = ({
ciphertext,
nonce,
publicKey,
privateKey
}: {
ciphertext: string;
nonce: string;
publicKey: string;
privateKey: string;
}) => {
// Convert base64 keys back to key objects
const pubKeyObj = crypto.createPublicKey({
key: Buffer.from(publicKey, "base64"),
type: "spki",
format: "der"
});
const privKeyObj = crypto.createPrivateKey({
key: Buffer.from(privateKey, "base64"),
type: "pkcs8",
format: "der"
});
// Generate same shared secret
const sharedSecret = crypto.diffieHellman({
privateKey: privKeyObj,
publicKey: pubKeyObj
});
const nonceBuffer = Buffer.from(nonce, "base64");
const combinedBuffer = Buffer.from(ciphertext, "base64");
// Split ciphertext and auth tag (last 16 bytes for GCM)
const actualCiphertext = combinedBuffer.subarray(0, -16);
const authTag = combinedBuffer.subarray(-16);
// Derive same 32-byte key
const key = crypto.createHash("sha256").update(sharedSecret).digest();
// Use first 12 bytes of nonce as IV
const iv = nonceBuffer.subarray(0, 12);
// Decrypt
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(authTag);
const plaintext = decipher.update(actualCiphertext);
try {
const final = decipher.final();
return Buffer.concat([plaintext, final]).toString("utf8");
} catch (error) {
throw new Error("Invalid ciphertext or keys");
}
};
const generateAsymmetricKeyPairNoFipsValidation = () => {
const pair = nacl.box.keyPair();
return {
publicKey: naclUtils.encodeBase64(pair.publicKey),
privateKey: naclUtils.encodeBase64(pair.secretKey)
};
};
export const encryptAsymmetricNoFipsValidation = (plaintext: string, publicKey: string, privateKey: string) => {
const nonce = nacl.randomBytes(24);
const ciphertext = nacl.box(
naclUtils.decodeUTF8(plaintext),
nonce,
naclUtils.decodeBase64(publicKey),
naclUtils.decodeBase64(privateKey)
);
return {
ciphertext: naclUtils.encodeBase64(ciphertext),
nonce: naclUtils.encodeBase64(nonce)
};
};
const decryptAsymmetricNoFipsValidation = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => {
const plaintext: Uint8Array | null = nacl.box.open(
naclUtils.decodeBase64(ciphertext),
naclUtils.decodeBase64(nonce),
naclUtils.decodeBase64(publicKey),
naclUtils.decodeBase64(privateKey)
);
if (plaintext == null) throw Error("Invalid ciphertext or keys");
return naclUtils.encodeUTF8(plaintext);
};
export const generateAsymmetricKeyPair = () => {
const pair = nacl.box.keyPair();
return {
publicKey: naclUtils.encodeBase64(pair.publicKey),
privateKey: naclUtils.encodeBase64(pair.secretKey)
};
};
export const computeMd5 = (message: string, digest: DigestType = DigestType.Hex) => {
let encoder;
switch (digest) {
case DigestType.Hex:
encoder = cryptoJs.enc.Hex;
break;
case DigestType.Base64:
encoder = cryptoJs.enc.Base64;
break;
default:
throw new Error(`Invalid digest type: ${digest as string}`);
}
return cryptoJs.MD5(message).toString(encoder);
};
const cryptographyFactory = () => {
// placeholder for now
let $fipsEnabled = false;
let $isInitialized = false;
const $checkIsInitialized = () => {
if (!$isInitialized) {
throw new Error("Internal cryptography module is not initialized");
}
};
const isFipsModeEnabled = () => {
$checkIsInitialized();
return $fipsEnabled;
};
const $setFipsModeEnabled = (enabled: boolean) => {
$fipsEnabled = enabled;
$isInitialized = true;
};
const initialize = async (superAdminDAL: TSuperAdminDALFactory) => {
if ($isInitialized) {
return isFipsModeEnabled();
}
if (process.env.FIPS_ENABLED !== "true") {
logger.info("[FIPS]: Instance is running in non-FIPS mode.");
return false;
}
const serverCfg = await superAdminDAL.findById(ADMIN_CONFIG_DB_UUID).catch(() => null);
// if fips mode is enabled, we need to check if the deployment is a new deployment or an old one.
if (serverCfg) {
if (serverCfg.fipsEnabled) {
logger.info("[FIPS]: Instance is configured for FIPS mode of operation. Continuing startup with FIPS enabled.");
$setFipsModeEnabled(true);
return true;
}
logger.info("[FIPS]: Instance age predates FIPS mode inception date. Continuing without FIPS.");
$setFipsModeEnabled(false);
return false;
}
logger.info("[FIPS]: First time initializing cryptography module on a new deployment. FIPS mode is enabled.");
// TODO(daniel): check if it's an enterprise deployment
// if there is no server cfg, and FIPS_MODE is `true`, its a fresh FIPS deployment. We need to set the fipsEnabled to true.
$setFipsModeEnabled(true);
return true;
};
const encryption = () => {
$checkIsInitialized();
const asymmetric = () => {
const generateKeyPair = () => {
if (isFipsModeEnabled()) {
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is enabled.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is enabled.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is enabled.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is enabled.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is enabled.");
return generateAsymmetricKeyPairFipsValidated();
}
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is DISABLED.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is DISABLED.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is DISABLED.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is DISABLED.");
logger.info("[FIPS]: Generating asymmetric key pair. FIPS mode is DISABLED.");
return generateAsymmetricKeyPairNoFipsValidation();
};
const encrypt = (data: string, publicKey: string, privateKey: string) => {
if (isFipsModeEnabled()) {
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is enabled.");
return encryptAsymmetricFipsValidated(data, publicKey, privateKey);
}
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Encrypting asymmetric data. FIPS mode is DISABLED.");
return encryptAsymmetricNoFipsValidation(data, publicKey, privateKey);
};
const decrypt = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => {
if (isFipsModeEnabled()) {
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is enabled.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is enabled.");
return decryptAsymmetricFipsValidated({ ciphertext, nonce, publicKey, privateKey });
}
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is DISABLED.");
logger.info("[FIPS]: Decrypting asymmetric data. FIPS mode is DISABLED.");
return decryptAsymmetricNoFipsValidation({ ciphertext, nonce, publicKey, privateKey });
};
return {
generateKeyPair,
encrypt,
decrypt
};
};
const decryptSymmetric = ({ ciphertext, iv, tag, key, keySize }: TDecryptSymmetricInput): string => {
let decipher;
if (keySize === SymmetricKeySize.Bits128) {
// Not ideal: 128-bit hex key (32 chars) gets interpreted as 32 UTF-8 bytes (256 bits)
// This works but reduces effective key entropy from 256 to 128 bits
// Note: Never use this for FIPS mode of operation.
decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64"));
} else {
const secretKey = crypto.createSecretKey(key, "base64");
decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, secretKey, Buffer.from(iv, "base64"));
}
decipher.setAuthTag(Buffer.from(tag, "base64"));
let cleartext = decipher.update(ciphertext, "base64", "utf8");
cleartext += decipher.final("utf8");
return cleartext;
};
const encryptSymmetric = ({ plaintext, key, keySize }: TEncryptSymmetricInput) => {
let iv;
let cipher;
if (keySize === SymmetricKeySize.Bits128) {
iv = crypto.randomBytes(BLOCK_SIZE_BYTES_16);
cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv);
} else {
iv = crypto.randomBytes(IV_BYTES_SIZE);
cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, crypto.createSecretKey(key, "base64"), iv);
}
let ciphertext = cipher.update(plaintext, "utf8", "base64");
ciphertext += cipher.final("base64");
return {
ciphertext,
iv: iv.toString("base64"),
tag: cipher.getAuthTag().toString("base64")
};
};
const encryptWithRootEncryptionKey = (data: string) => {
const appCfg = getConfig();
const rootEncryptionKey = appCfg.ROOT_ENCRYPTION_KEY;
const encryptionKey = appCfg.ENCRYPTION_KEY;
if (rootEncryptionKey) {
const { iv, tag, ciphertext } = encryptSymmetric({
plaintext: data,
key: rootEncryptionKey,
keySize: SymmetricKeySize.Bits256
});
return {
iv,
tag,
ciphertext,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
encoding: SecretKeyEncoding.BASE64
};
}
if (encryptionKey) {
const { iv, tag, ciphertext } = encryptSymmetric({
plaintext: data,
key: encryptionKey,
keySize: SymmetricKeySize.Bits128
});
return {
iv,
tag,
ciphertext,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
encoding: SecretKeyEncoding.UTF8
};
}
throw new Error("Missing both encryption keys");
};
const decryptWithRootEncryptionKey = <T = string>({
keyEncoding,
ciphertext,
tag,
iv
}: Omit<TDecryptSymmetricInput, "key" | "keySize"> & {
keyEncoding: SecretKeyEncoding;
}) => {
logger.info(
`[FIPS]: decryptWithRootEncryptionKey -> Decrypting symmetric data. FIPS mode is: ${isFipsModeEnabled()}`
);
logger.info(
`[FIPS]: decryptWithRootEncryptionKey -> Decrypting symmetric data. FIPS mode is: ${isFipsModeEnabled()}`
);
logger.info(
`[FIPS]: decryptWithRootEncryptionKey -> Decrypting symmetric data. FIPS mode is: ${isFipsModeEnabled()}`
);
logger.info(
`[FIPS]: decryptWithRootEncryptionKey -> Decrypting symmetric data. FIPS mode is: ${isFipsModeEnabled()}`
);
logger.info(
`[FIPS]: decryptWithRootEncryptionKey -> Decrypting symmetric data. FIPS mode is: ${isFipsModeEnabled()}`
);
const appCfg = getConfig();
// the or gate is used used in migration
const rootEncryptionKey = appCfg?.ROOT_ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
const encryptionKey = appCfg?.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY;
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
const data = decryptSymmetric({
key: rootEncryptionKey,
iv,
tag,
ciphertext,
keySize: SymmetricKeySize.Bits256
});
return data as T;
}
if (encryptionKey && keyEncoding === SecretKeyEncoding.UTF8) {
const data = decryptSymmetric({ key: encryptionKey, iv, tag, ciphertext, keySize: SymmetricKeySize.Bits128 });
return data as T;
}
throw new Error("Missing both encryption keys");
};
return {
asymmetric,
encryptWithRootEncryptionKey,
decryptWithRootEncryptionKey,
encryptSymmetric,
decryptSymmetric
};
};
const hashing = () => {
$checkIsInitialized();
// mark this function as deprecated
/**
* @deprecated Do not use MD5 unless you absolutely have to. It is considered an unsafe hashing algorithm, and should only be used if absolutely necessary.
*/
const md5 = (message: string, digest: DigestType = DigestType.Hex) => {
logger.info(`[FIPS]: md5 -> Hashing message. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: md5 -> Hashing message. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: md5 -> Hashing message. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: md5 -> Hashing message. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: md5 -> Hashing message. FIPS mode is: ${isFipsModeEnabled()}`);
// If FIPS is enabled and we need MD5, we use the crypto-js implementation.
// Avoid this at all costs unless strictly necessary, like for mongo atlas digest auth.
if (isFipsModeEnabled()) {
return computeMd5(message, digest);
}
return crypto.createHash("md5").update(message).digest(digest);
};
const createHash = async (password: string, saltRounds: number) => {
logger.info(`[FIPS]: createHash -> Hashing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: createHash -> Hashing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: createHash -> Hashing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: createHash -> Hashing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: createHash -> Hashing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: createHash -> Hashing password. FIPS mode is: ${isFipsModeEnabled()}`);
if (isFipsModeEnabled()) {
const hasher = hasherFipsValidated();
// For the salt when using pkdf2, we do salt rounds * 100.000.
// The reason for this is because pbkdf2 is not as compute intense as bcrypt, making it faster to brute-force.
// From my testing, doing salt rounds * 100.000 brings the computational power required to roughly the same as bcrypt.
// OWASP recommends a minimum of 600.000 iterations for pbkdf2.
// Ref: https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2
const hash = await hasher.hash(password, saltRounds * 100_000);
return hash;
}
const hash = await bcrypt.hash(password, saltRounds);
return hash;
};
const compareHash = async (password: string, hash: string) => {
logger.info(`[FIPS]: compareHash -> Comparing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: compareHash -> Comparing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: compareHash -> Comparing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: compareHash -> Comparing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: compareHash -> Comparing password. FIPS mode is: ${isFipsModeEnabled()}`);
logger.info(`[FIPS]: compareHash -> Comparing password. FIPS mode is: ${isFipsModeEnabled()}`);
if (isFipsModeEnabled()) {
const isValid = await hasherFipsValidated().compare(password, hash);
return isValid;
}
const isValid = await bcrypt.compare(password, hash);
return isValid;
};
return {
md5,
createHash,
compareHash
};
};
return {
initialize,
isFipsModeEnabled,
hashing,
encryption,
randomBytes: crypto.randomBytes,
randomInt: crypto.randomInt,
rawCrypto: {
createHash: crypto.createHash,
createHmac: crypto.createHmac,
sign: crypto.sign,
verify: crypto.verify,
createSign: crypto.createSign,
createVerify: crypto.createVerify,
generateKeyPair: crypto.generateKeyPair,
createCipheriv: crypto.createCipheriv,
createDecipheriv: crypto.createDecipheriv,
createPublicKey: crypto.createPublicKey,
createPrivateKey: crypto.createPrivateKey,
getRandomValues: crypto.getRandomValues,
randomUUID: crypto.randomUUID,
subtle: {
// eslint-disable-next-line @typescript-eslint/unbound-method
generateKey: subtle.generateKey,
// eslint-disable-next-line @typescript-eslint/unbound-method
importKey: subtle.importKey,
// eslint-disable-next-line @typescript-eslint/unbound-method
exportKey: subtle.exportKey
},
constants: crypto.constants,
X509Certificate: crypto.X509Certificate,
KeyObject: crypto.KeyObject
}
};
};
const factoryInstance = cryptographyFactory();
export type TCryptographyFactory = ReturnType<typeof cryptographyFactory>;
export { factoryInstance as crypto, DigestType };

View File

@@ -1,133 +1,10 @@
import crypto from "node:crypto";
import argon2 from "argon2";
import nacl from "tweetnacl";
import naclUtils from "tweetnacl-util";
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
import { SecretKeyEncoding } from "@app/db/schemas";
import { getConfig } from "../config/env";
import { crypto, SymmetricKeySize } from "./cryptography";
export const decodeBase64 = (s: string) => naclUtils.decodeBase64(s);
export const encodeBase64 = (u: Uint8Array) => naclUtils.encodeBase64(u);
export const randomSecureBytes = (length = 32) => crypto.randomBytes(length);
export type TDecryptSymmetricInput = {
ciphertext: string;
iv: string;
tag: string;
key: string;
};
export const IV_BYTES_SIZE = 12;
export const BLOCK_SIZE_BYTES_16 = 16;
export const decryptSymmetric = ({ ciphertext, iv, tag, key }: TDecryptSymmetricInput): string => {
const secretKey = crypto.createSecretKey(key, "base64");
const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, secretKey, Buffer.from(iv, "base64"));
decipher.setAuthTag(Buffer.from(tag, "base64"));
let cleartext = decipher.update(ciphertext, "base64", "utf8");
cleartext += decipher.final("utf8");
return cleartext;
};
export const encryptSymmetric = (plaintext: string, key: string) => {
const iv = crypto.randomBytes(IV_BYTES_SIZE);
const secretKey = crypto.createSecretKey(key, "base64");
const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, secretKey, iv);
let ciphertext = cipher.update(plaintext, "utf8", "base64");
ciphertext += cipher.final("base64");
return {
ciphertext,
iv: iv.toString("base64"),
tag: cipher.getAuthTag().toString("base64")
};
};
export const encryptSymmetric128BitHexKeyUTF8 = (plaintext: string, key: string | Buffer) => {
const iv = crypto.randomBytes(BLOCK_SIZE_BYTES_16);
const cipher = crypto.createCipheriv(SecretEncryptionAlgo.AES_256_GCM, key, iv);
let ciphertext = cipher.update(plaintext, "utf8", "base64");
ciphertext += cipher.final("base64");
return {
ciphertext,
iv: iv.toString("base64"),
tag: cipher.getAuthTag().toString("base64")
};
};
export const decryptSymmetric128BitHexKeyUTF8 = ({
ciphertext,
iv,
tag,
key
}: Omit<TDecryptSymmetricInput, "key"> & { key: string | Buffer }): string => {
const decipher = crypto.createDecipheriv(SecretEncryptionAlgo.AES_256_GCM, key, Buffer.from(iv, "base64"));
decipher.setAuthTag(Buffer.from(tag, "base64"));
let cleartext = decipher.update(ciphertext, "base64", "utf8");
cleartext += decipher.final("utf8");
return cleartext;
};
export const encryptAsymmetric = (plaintext: string, publicKey: string, privateKey: string) => {
const nonce = nacl.randomBytes(24);
const ciphertext = nacl.box(
naclUtils.decodeUTF8(plaintext),
nonce,
naclUtils.decodeBase64(publicKey),
naclUtils.decodeBase64(privateKey)
);
return {
ciphertext: naclUtils.encodeBase64(ciphertext),
nonce: naclUtils.encodeBase64(nonce)
};
};
export type TDecryptAsymmetricInput = {
ciphertext: string;
nonce: string;
publicKey: string;
privateKey: string;
};
export const decryptAsymmetric = ({ ciphertext, nonce, publicKey, privateKey }: TDecryptAsymmetricInput) => {
const plaintext: Uint8Array | null = nacl.box.open(
naclUtils.decodeBase64(ciphertext),
naclUtils.decodeBase64(nonce),
naclUtils.decodeBase64(publicKey),
naclUtils.decodeBase64(privateKey)
);
if (plaintext == null) throw Error("Invalid ciphertext or keys");
return naclUtils.encodeUTF8(plaintext);
};
export const generateSymmetricKey = (size = 32) => crypto.randomBytes(size).toString("base64");
export const generateHash = (value: string | Buffer) => crypto.createHash("sha256").update(value).digest("hex");
export const generateAsymmetricKeyPair = () => {
const pair = nacl.box.keyPair();
return {
publicKey: naclUtils.encodeBase64(pair.publicKey),
privateKey: naclUtils.encodeBase64(pair.secretKey)
};
};
export type TGenSecretBlindIndex = {
type TBuildSecretBlindIndexDTO = {
secretName: string;
keyEncoding: SecretKeyEncoding;
rootEncryptionKey?: string;
@@ -137,6 +14,10 @@ export type TGenSecretBlindIndex = {
ciphertext: string;
};
/**
*
* @deprecated `buildSecretBlindIndexFromName` is no longer used for newer projects. It remains a relic from V1 secrets which is still supported on very old projects.
*/
export const buildSecretBlindIndexFromName = async ({
secretName,
ciphertext,
@@ -145,13 +26,17 @@ export const buildSecretBlindIndexFromName = async ({
tag,
encryptionKey,
rootEncryptionKey
}: TGenSecretBlindIndex) => {
}: TBuildSecretBlindIndexDTO) => {
if (!encryptionKey && !rootEncryptionKey) throw new Error("Missing secret blind index key");
let salt = "";
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
salt = decryptSymmetric({ iv, ciphertext, key: rootEncryptionKey, tag });
salt = crypto
.encryption()
.decryptSymmetric({ iv, ciphertext, key: rootEncryptionKey, tag, keySize: SymmetricKeySize.Bits256 });
} else if (encryptionKey && keyEncoding === SecretKeyEncoding.UTF8) {
salt = decryptSymmetric128BitHexKeyUTF8({ iv, ciphertext, key: encryptionKey, tag });
salt = crypto
.encryption()
.decryptSymmetric({ iv, ciphertext, key: encryptionKey, tag, keySize: SymmetricKeySize.Bits128 });
}
if (!salt) throw new Error("Missing secret blind index key");
@@ -167,75 +52,3 @@ export const buildSecretBlindIndexFromName = async ({
return secretBlindIndex.toString("base64");
};
export const createSecretBlindIndex = (rootEncryptionKey?: string, encryptionKey?: string) => {
if (!encryptionKey && !rootEncryptionKey) throw new Error("Atleast one encryption key needed");
const salt = crypto.randomBytes(16).toString("base64");
if (rootEncryptionKey) {
const data = encryptSymmetric(salt, rootEncryptionKey);
return {
...data,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
keyEncoding: SecretKeyEncoding.BASE64
};
}
if (encryptionKey) {
const data = encryptSymmetric128BitHexKeyUTF8(salt, encryptionKey);
return {
...data,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
keyEncoding: SecretKeyEncoding.UTF8
};
}
throw new Error("Failed to generate blind index due to encryption key missing");
};
export const infisicalSymmetricEncypt = (data: string) => {
const appCfg = getConfig();
const rootEncryptionKey = appCfg.ROOT_ENCRYPTION_KEY;
const encryptionKey = appCfg.ENCRYPTION_KEY;
if (rootEncryptionKey) {
const { iv, tag, ciphertext } = encryptSymmetric(data, rootEncryptionKey);
return {
iv,
tag,
ciphertext,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
encoding: SecretKeyEncoding.BASE64
};
}
if (encryptionKey) {
const { iv, tag, ciphertext } = encryptSymmetric128BitHexKeyUTF8(data, encryptionKey);
return {
iv,
tag,
ciphertext,
algorithm: SecretEncryptionAlgo.AES_256_GCM,
encoding: SecretKeyEncoding.UTF8
};
}
throw new Error("Missing both encryption keys");
};
export const infisicalSymmetricDecrypt = <T = string>({
keyEncoding,
ciphertext,
tag,
iv
}: Omit<TDecryptSymmetricInput, "key"> & {
keyEncoding: SecretKeyEncoding;
}) => {
const appCfg = getConfig();
// the or gate is used used in migration
const rootEncryptionKey = appCfg?.ROOT_ENCRYPTION_KEY || process.env.ROOT_ENCRYPTION_KEY;
const encryptionKey = appCfg?.ENCRYPTION_KEY || process.env.ENCRYPTION_KEY;
if (rootEncryptionKey && keyEncoding === SecretKeyEncoding.BASE64) {
const data = decryptSymmetric({ key: rootEncryptionKey, iv, tag, ciphertext });
return data as T;
}
if (encryptionKey && keyEncoding === SecretKeyEncoding.UTF8) {
const data = decryptSymmetric128BitHexKeyUTF8({ key: encryptionKey, iv, tag, ciphertext });
return data as T;
}
throw new Error("Missing both encryption keys");
};

View File

@@ -1,17 +1,5 @@
export {
buildSecretBlindIndexFromName,
createSecretBlindIndex,
decodeBase64,
decryptAsymmetric,
decryptSymmetric,
decryptSymmetric128BitHexKeyUTF8,
encodeBase64,
encryptAsymmetric,
encryptSymmetric,
encryptSymmetric128BitHexKeyUTF8,
generateAsymmetricKeyPair,
randomSecureBytes
} from "./encryption";
export { crypto, SymmetricKeySize } from "./cryptography";
export { buildSecretBlindIndexFromName } from "./encryption";
export {
decryptIntegrationAuths,
decryptSecretApprovals,

View File

@@ -1,4 +1,4 @@
import crypto from "crypto";
import nodeCrypto from "crypto";
import { z } from "zod";
import {
@@ -12,7 +12,7 @@ import {
TSecrets,
TSecretVersions
} from "../../db/schemas";
import { decryptAsymmetric } from "./encryption";
import { crypto } from "./cryptography";
const DecryptedValuesSchema = z.object({
id: z.string(),
@@ -68,7 +68,7 @@ const decryptCipher = ({
tag: string;
key: string | Buffer;
}) => {
const decipher = crypto.createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "base64"));
const decipher = nodeCrypto.createDecipheriv("aes-256-gcm", key, Buffer.from(iv, "base64"));
decipher.setAuthTag(Buffer.from(tag, "base64"));
let cleartext = decipher.update(ciphertext, "base64", "utf8");
@@ -91,7 +91,7 @@ const getDecryptedValues = (data: Array<{ ciphertext: string; iv: string; tag: s
return results;
};
export const decryptSecrets = (encryptedSecrets: TSecrets[], privateKey: string, latestKey: TLatestKey) => {
const key = decryptAsymmetric({
const key = crypto.encryption().asymmetric().decrypt({
ciphertext: latestKey.encryptedKey,
nonce: latestKey.nonce,
publicKey: latestKey.sender.publicKey,
@@ -143,7 +143,7 @@ export const decryptSecretVersions = (
privateKey: string,
latestKey: TLatestKey
) => {
const key = decryptAsymmetric({
const key = crypto.encryption().asymmetric().decrypt({
ciphertext: latestKey.encryptedKey,
nonce: latestKey.nonce,
publicKey: latestKey.sender.publicKey,
@@ -195,7 +195,7 @@ export const decryptSecretApprovals = (
privateKey: string,
latestKey: TLatestKey
) => {
const key = decryptAsymmetric({
const key = crypto.encryption().asymmetric().decrypt({
ciphertext: latestKey.encryptedKey,
nonce: latestKey.nonce,
publicKey: latestKey.sender.publicKey,
@@ -247,7 +247,7 @@ export const decryptIntegrationAuths = (
privateKey: string,
latestKey: TLatestKey
) => {
const key = decryptAsymmetric({
const key = crypto.encryption().asymmetric().decrypt({
ciphertext: latestKey.encryptedKey,
nonce: latestKey.nonce,
publicKey: latestKey.sender.publicKey,

View File

@@ -1,9 +1,9 @@
import { execFile } from "child_process";
import crypto from "crypto";
import fs from "fs/promises";
import path from "path";
import { promisify } from "util";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { cleanTemporaryDirectory, createTemporaryDirectory, writeToTemporaryFile } from "@app/lib/files";
import { logger } from "@app/lib/logger";
@@ -43,19 +43,19 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
case SigningAlgorithm.RSASSA_PSS_SHA_512:
return {
hashAlgorithm: SupportedHashAlgorithm.SHA512,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
padding: crypto.rawCrypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: SHA512_DIGEST_LENGTH
};
case SigningAlgorithm.RSASSA_PSS_SHA_256:
return {
hashAlgorithm: SupportedHashAlgorithm.SHA256,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
padding: crypto.rawCrypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: SHA256_DIGEST_LENGTH
};
case SigningAlgorithm.RSASSA_PSS_SHA_384:
return {
hashAlgorithm: SupportedHashAlgorithm.SHA384,
padding: crypto.constants.RSA_PKCS1_PSS_PADDING,
padding: crypto.rawCrypto.constants.RSA_PKCS1_PSS_PADDING,
saltLength: SHA384_DIGEST_LENGTH
};
@@ -63,17 +63,17 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
case SigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_512:
return {
hashAlgorithm: SupportedHashAlgorithm.SHA512,
padding: crypto.constants.RSA_PKCS1_PADDING
padding: crypto.rawCrypto.constants.RSA_PKCS1_PADDING
};
case SigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_384:
return {
hashAlgorithm: SupportedHashAlgorithm.SHA384,
padding: crypto.constants.RSA_PKCS1_PADDING
padding: crypto.rawCrypto.constants.RSA_PKCS1_PADDING
};
case SigningAlgorithm.RSASSA_PKCS1_V1_5_SHA_256:
return {
hashAlgorithm: SupportedHashAlgorithm.SHA256,
padding: crypto.constants.RSA_PKCS1_PADDING
padding: crypto.rawCrypto.constants.RSA_PKCS1_PADDING
};
// ECDSA
@@ -389,7 +389,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
return signature;
}
const privateKeyObject = crypto.createPrivateKey({
const privateKeyObject = crypto.rawCrypto.createPrivateKey({
key: privateKey,
format: "pem",
type: "pkcs8"
@@ -397,7 +397,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
// For RSA signatures
if (signingAlgorithm.startsWith("RSA")) {
const signer = crypto.createSign(hashAlgorithm);
const signer = crypto.rawCrypto.createSign(hashAlgorithm);
signer.update(data);
return signer.sign({
@@ -408,7 +408,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
}
if (signingAlgorithm.startsWith("ECDSA")) {
// For ECDSA signatures
const signer = crypto.createSign(hashAlgorithm);
const signer = crypto.rawCrypto.createSign(hashAlgorithm);
signer.update(data);
return signer.sign({
key: privateKeyObject,
@@ -452,7 +452,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
return signatureValid;
}
const publicKeyObject = crypto.createPublicKey({
const publicKeyObject = crypto.rawCrypto.createPublicKey({
key: publicKey,
format: "der",
type: "spki"
@@ -460,7 +460,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
// For RSA signatures
if (signingAlgorithm.startsWith("RSA")) {
const verifier = crypto.createVerify(hashAlgorithm);
const verifier = crypto.rawCrypto.createVerify(hashAlgorithm);
verifier.update(data);
return verifier.verify(
@@ -474,7 +474,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
}
// For ECDSA signatures
if (signingAlgorithm.startsWith("ECDSA")) {
const verifier = crypto.createVerify(hashAlgorithm);
const verifier = crypto.rawCrypto.createVerify(hashAlgorithm);
verifier.update(data);
return verifier.verify(
{
@@ -499,7 +499,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
const generateAsymmetricPrivateKey = async () => {
const { privateKey } = await new Promise<{ privateKey: string }>((resolve, reject) => {
if (algorithm.startsWith("RSA")) {
crypto.generateKeyPair(
crypto.rawCrypto.generateKeyPair(
"rsa",
{
modulusLength: Number(algorithm.split("_")[1]),
@@ -517,7 +517,7 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
} else {
const { full: namedCurve } = $getEcCurveName(algorithm);
crypto.generateKeyPair(
crypto.rawCrypto.generateKeyPair(
"ec",
{
namedCurve,
@@ -541,13 +541,13 @@ export const signingService = (algorithm: AsymmetricKeyAlgorithm): TAsymmetricSi
};
const getPublicKeyFromPrivateKey = (privateKey: Buffer) => {
const privateKeyObj = crypto.createPrivateKey({
const privateKeyObj = crypto.rawCrypto.createPrivateKey({
key: privateKey,
format: "pem",
type: "pkcs8"
});
const publicKey = crypto.createPublicKey(privateKeyObj).export({
const publicKey = crypto.rawCrypto.createPublicKey(privateKeyObj).export({
type: "spki",
format: "der"
});

View File

@@ -1,9 +1,11 @@
import crypto, { KeyObject } from "crypto";
import { KeyObject } from "crypto";
import fs from "fs/promises";
import path from "path";
import { crypto } from "./cryptography";
export const verifySignature = (data: string, signature: Buffer, publicKey: KeyObject) => {
const verify = crypto.createVerify("SHA256");
const verify = crypto.rawCrypto.createVerify("SHA256");
verify.update(data);
verify.end();
return verify.verify(publicKey, signature);
@@ -12,7 +14,7 @@ export const verifySignature = (data: string, signature: Buffer, publicKey: KeyO
export const verifyOfflineLicense = async (licenseContents: string, signature: string) => {
const publicKeyPem = await fs.readFile(path.join(__dirname, "license_public_key.pem"), "utf8");
const publicKey = crypto.createPublicKey({
const publicKey = crypto.rawCrypto.createPublicKey({
key: publicKeyPem,
format: "pem",
type: "pkcs1"

View File

@@ -1,13 +1,10 @@
import argon2 from "argon2";
import crypto from "crypto";
import jsrp from "jsrp";
import nacl from "tweetnacl";
import tweetnacl from "tweetnacl-util";
import { TUserEncryptionKeys } from "@app/db/schemas";
import { UserEncryption } from "@app/services/user/user-types";
import { decryptSymmetric128BitHexKeyUTF8, encryptAsymmetric, encryptSymmetric } from "./encryption";
import { crypto, SymmetricKeySize } from "./cryptography";
export const generateSrpServerKey = async (salt: string, verifier: string) => {
// eslint-disable-next-line new-cap
@@ -42,11 +39,10 @@ export const generateUserSrpKeys = async (
password: string,
customKeys?: { publicKey: string; privateKey: string }
) => {
const pair = nacl.box.keyPair();
const secretKeyUint8Array = pair.secretKey;
const publicKeyUint8Array = pair.publicKey;
const privateKey = customKeys?.privateKey || tweetnacl.encodeBase64(secretKeyUint8Array);
const publicKey = customKeys?.publicKey || tweetnacl.encodeBase64(publicKeyUint8Array);
const pair = crypto.encryption().asymmetric().generateKeyPair();
const privateKey = customKeys?.privateKey || pair.privateKey;
const publicKey = customKeys?.publicKey || pair.publicKey;
// eslint-disable-next-line
const client = new jsrp.client();
@@ -78,7 +74,11 @@ export const generateUserSrpKeys = async (
ciphertext: encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
} = encryptSymmetric(privateKey, key.toString("base64"));
} = crypto.encryption().encryptSymmetric({
plaintext: privateKey,
key: key.toString("base64"),
keySize: SymmetricKeySize.Bits256
});
// create the protected key by encrypting the symmetric key
// [key] with the derived key
@@ -86,7 +86,11 @@ export const generateUserSrpKeys = async (
ciphertext: protectedKey,
iv: protectedKeyIV,
tag: protectedKeyTag
} = encryptSymmetric(key.toString("hex"), derivedKey.toString("base64"));
} = crypto.encryption().encryptSymmetric({
plaintext: key.toString("hex"),
key: derivedKey.toString("base64"),
keySize: SymmetricKeySize.Bits256
});
return {
protectedKey,
@@ -117,11 +121,12 @@ export const getUserPrivateKey = async (
>
) => {
if (user.encryptionVersion === UserEncryption.V1) {
return decryptSymmetric128BitHexKeyUTF8({
return crypto.encryption().decryptSymmetric({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key: password.slice(0, 32).padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
key: password.slice(0, 32).padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0"),
keySize: SymmetricKeySize.Bits128
});
}
if (
@@ -140,18 +145,20 @@ export const getUserPrivateKey = async (
raw: true
});
if (!derivedKey) throw new Error("Failed to derive key from password");
const key = decryptSymmetric128BitHexKeyUTF8({
const key = crypto.encryption().decryptSymmetric({
ciphertext: user.protectedKey,
iv: user.protectedKeyIV,
tag: user.protectedKeyTag,
key: derivedKey
key: derivedKey,
keySize: SymmetricKeySize.Bits128
});
const privateKey = decryptSymmetric128BitHexKeyUTF8({
const privateKey = crypto.encryption().decryptSymmetric({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key: Buffer.from(key, "hex")
key: Buffer.from(key, "hex"),
keySize: SymmetricKeySize.Bits128
});
return privateKey;
}
@@ -160,6 +167,6 @@ export const getUserPrivateKey = async (
export const buildUserProjectKey = async (privateKey: string, publickey: string) => {
const randomBytes = crypto.randomBytes(16).toString("hex");
const { nonce, ciphertext } = encryptAsymmetric(randomBytes, publickey, privateKey);
const { nonce, ciphertext } = crypto.encryption().asymmetric().encrypt(randomBytes, publickey, privateKey);
return { nonce, ciphertext };
};

View File

@@ -1,8 +1,8 @@
import crypto from "crypto";
import fs from "fs/promises";
import os from "os";
import path from "path";
import { crypto } from "@app/lib/crypto/cryptography";
import { logger } from "@app/lib/logger";
const baseDir = path.join(os.tmpdir(), "infisical");

View File

@@ -1,11 +1,12 @@
/* eslint-disable no-await-in-loop */
import crypto from "node:crypto";
import net from "node:net";
import quicDefault, * as quicModule from "@infisical/quic";
import axios from "axios";
import https from "https";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "../errors";
import { logger } from "../logger";
import {
@@ -48,8 +49,8 @@ const createQuicConnection = async (
verifyPeer: true,
verifyCallback: async (certs) => {
if (!certs || certs.length === 0) return quic.native.CryptoError.CertificateRequired;
const serverCertificate = new crypto.X509Certificate(Buffer.from(certs[0]));
const caCertificate = new crypto.X509Certificate(tlsOptions.ca);
const serverCertificate = new crypto.rawCrypto.X509Certificate(Buffer.from(certs[0]));
const caCertificate = new crypto.rawCrypto.X509Certificate(tlsOptions.ca);
const isValidServerCertificate = serverCertificate.verify(caCertificate.publicKey);
if (!isValidServerCertificate) return quic.native.CryptoError.BadCertificate;
@@ -72,7 +73,7 @@ const createQuicConnection = async (
crypto: {
ops: {
randomBytes: async (data) => {
crypto.getRandomValues(new Uint8Array(data));
crypto.rawCrypto.getRandomValues(new Uint8Array(data));
}
}
}

View File

@@ -1,7 +1,7 @@
/* eslint-disable */
// Source code credits: https://github.com/mike-marcacci/node-redlock
// Taken to avoid external dependency
import { randomBytes, createHash } from "crypto";
import { crypto } from "@app/lib/crypto/cryptography";
import { EventEmitter } from "events";
// AbortController became available as a global in node version 16. Once version
@@ -251,14 +251,14 @@ export class Redlock extends EventEmitter {
* Generate a sha1 hash compatible with redis evalsha.
*/
private _hash(value: string): string {
return createHash("sha1").update(value).digest("hex");
return crypto.rawCrypto.createHash("sha1").update(value).digest("hex");
}
/**
* Generate a cryptographically random string.
*/
private _random(): string {
return randomBytes(16).toString("hex");
return crypto.randomBytes(16).toString("hex");
}
/**

View File

@@ -1,11 +1,11 @@
import crypto from "node:crypto";
import { crypto } from "@app/lib/crypto/cryptography";
const TURN_TOKEN_TTL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
export const getTurnCredentials = (id: string, authSecret: string, ttl = TURN_TOKEN_TTL) => {
const timestamp = Math.floor((Date.now() + ttl) / 1000);
const username = `${timestamp}:${id}`;
const hmac = crypto.createHmac("sha1", authSecret);
const hmac = crypto.rawCrypto.createHmac("sha1", authSecret);
hmac.update(username);
const password = hmac.digest("base64");

View File

@@ -9,12 +9,14 @@ import { initAuditLogDbConnection, initDbConnection } from "./db";
import { keyStoreFactory } from "./keystore/keystore";
import { formatSmtpConfig, initEnvConfig } from "./lib/config/env";
import { buildRedisFromConfig } from "./lib/config/redis";
import { crypto } from "./lib/crypto/cryptography";
import { removeTemporaryBaseDirectory } from "./lib/files";
import { initLogger } from "./lib/logger";
import { queueServiceFactory } from "./queue";
import { main } from "./server/app";
import { bootstrapCheck } from "./server/boot-strap-check";
import { smtpServiceFactory } from "./services/smtp/smtp-service";
import { superAdminDALFactory } from "./services/super-admin/super-admin-dal";
dotenv.config();
@@ -33,6 +35,9 @@ const run = async () => {
}))
});
const superAdminDAL = superAdminDALFactory(db);
await crypto.initialize(superAdminDAL);
const auditLogDb = envConfig.AUDIT_LOGS_DB_CONNECTION_URI
? initAuditLogDbConnection({
dbConnectionUri: envConfig.AUDIT_LOGS_DB_CONNECTION_URI,
@@ -60,6 +65,7 @@ const run = async () => {
const server = await main({
db,
auditLogDb,
superAdminDAL,
hsmModule: hsmModule.getModule(),
smtp,
logger,

View File

@@ -1,4 +1,13 @@
import { Job, JobsOptions, Queue, QueueOptions, RepeatOptions, Worker, WorkerListener } from "bullmq";
import {
Job,
JobsOptions,
Queue,
QueueOptions,
RepeatOptions,
Worker,
WorkerListener,
AdvancedRepeatOptions
} from "bullmq";
import PgBoss, { WorkOptions } from "pg-boss";
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
@@ -438,6 +447,9 @@ export const queueServiceFactory = (
queueContainer[name] = new Queue(name as string, {
...queueSettings,
settings: {
repeatKeyHashAlgorithm: "sha256"
},
connection
});

View File

@@ -22,6 +22,7 @@ import { CustomLogger } from "@app/lib/logger/logger";
import { alphaNumericNanoId } from "@app/lib/nanoid";
import { TQueueServiceFactory } from "@app/queue";
import { TSmtpService } from "@app/services/smtp/smtp-service";
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
import { globalRateLimiterCfg } from "./config/rateLimiter";
import { addErrorsToResponseSchemas } from "./plugins/add-errors-to-response-schemas";
@@ -44,10 +45,22 @@ type TMain = {
hsmModule: HsmModule;
redis: Redis;
envConfig: TEnvConfig;
superAdminDAL: TSuperAdminDALFactory;
};
// Run the server!
export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, keyStore, redis, envConfig }: TMain) => {
export const main = async ({
db,
hsmModule,
auditLogDb,
smtp,
logger,
queue,
keyStore,
redis,
envConfig,
superAdminDAL
}: TMain) => {
const appCfg = getConfig();
const server = fastify({
@@ -128,7 +141,16 @@ export const main = async ({ db, hsmModule, auditLogDb, smtp, logger, queue, key
})
});
await server.register(registerRoutes, { smtp, queue, db, auditLogDb, keyStore, hsmModule, envConfig });
await server.register(registerRoutes, {
smtp,
queue,
db,
auditLogDb,
keyStore,
hsmModule,
envConfig,
superAdminDAL
});
await server.register(registerServeUI, {
standaloneMode: appCfg.STANDALONE_MODE || IS_PACKAGED,

View File

@@ -278,7 +278,7 @@ import { slackIntegrationDALFactory } from "@app/services/slack/slack-integratio
import { slackServiceFactory } from "@app/services/slack/slack-service";
import { TSmtpService } from "@app/services/smtp/smtp-service";
import { invalidateCacheQueueFactory } from "@app/services/super-admin/invalidate-cache-queue";
import { superAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
import { TSuperAdminDALFactory } from "@app/services/super-admin/super-admin-dal";
import { getServerCfg, superAdminServiceFactory } from "@app/services/super-admin/super-admin-service";
import { telemetryDALFactory } from "@app/services/telemetry/telemetry-dal";
import { telemetryQueueServiceFactory } from "@app/services/telemetry/telemetry-queue";
@@ -310,6 +310,7 @@ export const registerRoutes = async (
server: FastifyZodProvider,
{
auditLogDb,
superAdminDAL,
db,
hsmModule,
smtp: smtpService,
@@ -318,6 +319,7 @@ export const registerRoutes = async (
envConfig
}: {
auditLogDb?: Knex;
superAdminDAL: TSuperAdminDALFactory;
db: Knex;
hsmModule: HsmModule;
smtp: TSmtpService;
@@ -341,7 +343,6 @@ export const registerRoutes = async (
const orgBotDAL = orgBotDALFactory(db);
const incidentContactDAL = incidentContactDALFactory(db);
const orgRoleDAL = orgRoleDALFactory(db);
const superAdminDAL = superAdminDALFactory(db);
const rateLimitDAL = rateLimitDALFactory(db);
const apiKeyDAL = apiKeyDALFactory(db);

View File

@@ -9,6 +9,7 @@ import {
UsersSchema
} from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { invalidateCacheLimit, readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { getTelemetryDistinctId } from "@app/server/lib/telemetry";
@@ -56,9 +57,11 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
handler: async () => {
const config = await getServerCfg();
const serverEnvs = getConfig();
return {
config: {
...config,
fipsEnabled: crypto.isFipsModeEnabled(),
isMigrationModeOn: serverEnvs.MAINTENANCE_MODE,
isSecretScanningDisabled: serverEnvs.DISABLE_SECRET_SCANNING
}

View File

@@ -1,11 +1,10 @@
import crypto from "node:crypto";
import { z } from "zod";
import { IdentityTlsCertAuthsSchema } from "@app/db/schemas";
import { EventType } from "@app/ee/services/audit-log/audit-log-types";
import { ApiDocsTags, TLS_CERT_AUTH } from "@app/lib/api-docs";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { readLimit, writeLimit } from "@app/server/config/rateLimiter";
import { verifyAuth } from "@app/server/plugins/auth/verify-auth";
@@ -28,9 +27,9 @@ const validateCaCertificate = (caCert: string) => {
if (!caCert) return true;
try {
// eslint-disable-next-line no-new
new crypto.X509Certificate(caCert);
new crypto.rawCrypto.X509Certificate(caCert);
return true;
} catch (err) {
} catch {
return false;
}
};

View File

@@ -1,9 +1,6 @@
import crypto from "node:crypto";
import bcrypt from "bcrypt";
import { TApiKeys } from "@app/db/schemas/api-keys";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { TUserDALFactory } from "../user/user-dal";
@@ -27,7 +24,7 @@ export const apiKeyServiceFactory = ({ apiKeyDAL, userDAL }: TApiKeyServiceFacto
const createApiKey = async (userId: string, name: string, expiresIn: number) => {
const appCfg = getConfig();
const secret = crypto.randomBytes(16).toString("hex");
const secretHash = await bcrypt.hash(secret, appCfg.SALT_ROUNDS);
const secretHash = await crypto.hashing().createHash(secret, appCfg.SALT_ROUNDS);
const expiresAt = new Date();
expiresAt.setSeconds(expiresAt.getSeconds() + expiresIn);
@@ -59,7 +56,7 @@ export const apiKeyServiceFactory = ({ apiKeyDAL, userDAL }: TApiKeyServiceFacto
throw new UnauthorizedError();
}
const isMatch = await bcrypt.compare(TOKEN_SECRET, apiKey.secretHash);
const isMatch = await crypto.hashing().compareHash(TOKEN_SECRET, apiKey.secretHash);
if (!isMatch) throw new UnauthorizedError();
await apiKeyDAL.updateById(apiKey.id, { lastUsed: new Date() });
const user = await userDAL.findById(apiKey.userId);

View File

@@ -6,7 +6,7 @@ import {
} from "@app/ee/services/app-connections/oci";
import { getOracleDBConnectionListItem, OracleDBConnectionMethod } from "@app/ee/services/app-connections/oracledb";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { generateHash } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { APP_CONNECTION_NAME_MAP, APP_CONNECTION_PLAN_MAP } from "@app/services/app-connection/app-connection-maps";
import {
@@ -290,7 +290,7 @@ export const decryptAppConnection = async (
orgId: appConnection.orgId,
kmsService
}),
credentialsHash: generateHash(appConnection.encryptedCredentials)
credentialsHash: crypto.rawCrypto.createHash("sha256").update(appConnection.encryptedCredentials).digest("hex")
} as TAppConnection;
};

View File

@@ -6,7 +6,7 @@ import { ValidateOracleDBConnectionCredentialsSchema } from "@app/ee/services/ap
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { OrgPermissionAppConnectionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { generateHash } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { DatabaseErrorCode } from "@app/lib/error-codes";
import { BadRequestError, DatabaseError, NotFoundError } from "@app/lib/errors";
import { DiscriminativePick, OrgServiceActor } from "@app/lib/types";
@@ -272,7 +272,7 @@ export const appConnectionServiceFactory = ({
return {
...connection,
credentialsHash: generateHash(connection.encryptedCredentials),
credentialsHash: crypto.rawCrypto.createHash("sha256").update(connection.encryptedCredentials).digest("hex"),
credentials: validatedCredentials
} as TAppConnection;
} catch (err) {

View File

@@ -1,9 +1,9 @@
import { AssumeRoleCommand, STSClient } from "@aws-sdk/client-sts";
import AWS from "aws-sdk";
import { AxiosError } from "axios";
import { randomUUID } from "crypto";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, InternalServerError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { AppConnection, AWSRegion } from "@app/services/app-connection/app-connection-enums";
@@ -46,7 +46,7 @@ export const getAwsConnectionConfig = async (appConnection: TAwsConnectionConfig
const command = new AssumeRoleCommand({
RoleArn: credentials.roleArn,
RoleSessionName: `infisical-app-connection-${randomUUID()}`,
RoleSessionName: `infisical-app-connection-${crypto.rawCrypto.randomUUID()}`,
DurationSeconds: 900, // 15 mins
ExternalId: orgId
});

View File

@@ -1,11 +1,9 @@
import crypto from "node:crypto";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { Knex } from "knex";
import { TAuthTokens, TAuthTokenSessions } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { TOrgMembershipDALFactory } from "@app/services/org-membership/org-membership-dal";
@@ -81,7 +79,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, orgMembershipDAL }: TAu
const createTokenForUser = async ({ type, userId, orgId }: TCreateTokenForUserDTO) => {
const { token, ...tkCfg } = getTokenConfig(type);
const appCfg = getConfig();
const tokenHash = await bcrypt.hash(token, appCfg.SALT_ROUNDS);
const tokenHash = await crypto.hashing().createHash(token, appCfg.SALT_ROUNDS);
await tokenDAL.transaction(async (tx) => {
await tokenDAL.delete({ userId, type, orgId: orgId || null }, tx);
const newToken = await tokenDAL.create(
@@ -115,7 +113,7 @@ export const tokenServiceFactory = ({ tokenDAL, userDAL, orgMembershipDAL }: TAu
throw new Error("Token expired. Please try again");
}
const isValidToken = await bcrypt.compare(code, token.tokenHash);
const isValidToken = await crypto.hashing().compareHash(code, token.tokenHash);
if (!isValidToken) {
if (token?.triesLeft) {
if (token.triesLeft === 1) {

View File

@@ -1,4 +1,3 @@
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { Knex } from "knex";
@@ -8,7 +7,7 @@ import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError, DatabaseError, ForbiddenRequestError, UnauthorizedError } from "@app/lib/errors";
import { getMinExpiresIn, removeTrailingSlash } from "@app/lib/fn";
@@ -336,8 +335,11 @@ export const authLoginServiceFactory = ({
);
return "";
});
const hashedPassword = await bcrypt.hash(password, cfg.BCRYPT_SALT_ROUND);
const { iv, tag, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
const hashedPassword = await crypto.hashing().createHash(password, cfg.SALT_ROUNDS);
const { iv, tag, ciphertext, encoding } = crypto.encryption().encryptWithRootEncryptionKey(privateKey);
await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
serverPrivateKey: null,
clientPublicKey: null,

View File

@@ -1,10 +1,9 @@
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
import { BadRequestError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -95,7 +94,7 @@ export const authPaswordServiceFactory = ({
if (!isValidClientProof) throw new Error("Failed to authenticate. Try again?");
const appCfg = getConfig();
const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
await userDAL.updateUserEncryptionByUserId(userId, {
encryptionVersion: 2,
protectedKey,
@@ -208,13 +207,13 @@ export const authPaswordServiceFactory = ({
throw new BadRequestError({ message: "Current password is required." });
}
const isValid = await bcrypt.compare(oldPassword, user.hashedPassword);
const isValid = await crypto.hashing().compareHash(oldPassword, user.hashedPassword);
if (!isValid) {
throw new BadRequestError({ message: "Incorrect current password." });
}
}
const newHashedPassword = await bcrypt.hash(newPassword, cfg.BCRYPT_SALT_ROUND);
const newHashedPassword = await crypto.hashing().createHash(newPassword, cfg.SALT_ROUNDS);
// we need to get the original private key first for v2
let privateKey: string;
@@ -225,7 +224,7 @@ export const authPaswordServiceFactory = ({
user.serverEncryptedPrivateKeyEncoding &&
user.encryptionVersion === UserEncryption.V2
) {
privateKey = infisicalSymmetricDecrypt({
privateKey = crypto.encryption().decryptWithRootEncryptionKey({
iv: user.serverEncryptedPrivateKeyIV,
tag: user.serverEncryptedPrivateKeyTag,
ciphertext: user.serverEncryptedPrivateKey,
@@ -243,7 +242,7 @@ export const authPaswordServiceFactory = ({
privateKey
});
const { tag, iv, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
const { tag, iv, ciphertext, encoding } = crypto.encryption().encryptWithRootEncryptionKey(privateKey);
await userDAL.updateUserEncryptionByUserId(userId, {
hashedPassword: newHashedPassword,
@@ -285,7 +284,7 @@ export const authPaswordServiceFactory = ({
}: TResetPasswordViaBackupKeyDTO) => {
const cfg = getConfig();
const hashedPassword = await bcrypt.hash(password, cfg.BCRYPT_SALT_ROUND);
const hashedPassword = await crypto.hashing().createHash(password, cfg.SALT_ROUNDS);
await userDAL.updateUserEncryptionByUserId(userId, {
encryptionVersion: 2,
@@ -461,7 +460,7 @@ export const authPaswordServiceFactory = ({
const cfg = getConfig();
const hashedPassword = await bcrypt.hash(password, cfg.BCRYPT_SALT_ROUND);
const hashedPassword = await crypto.hashing().createHash(password, cfg.SALT_ROUNDS);
await userDAL.updateUserEncryptionByUserId(
actor.id,

View File

@@ -1,4 +1,3 @@
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { OrgMembershipStatus, SecretKeyEncoding, TableName } from "@app/db/schemas";
@@ -7,7 +6,7 @@ import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-grou
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { getMinExpiresIn } from "@app/lib/fn";
@@ -193,7 +192,7 @@ export const authSignupServiceFactory = ({
validateSignUpAuthorization(authorization, user.id);
}
const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
const privateKey = await getUserPrivateKey(password, {
salt,
protectedKey,
@@ -204,7 +203,7 @@ export const authSignupServiceFactory = ({
tag: encryptedPrivateKeyTag,
encryptionVersion: UserEncryption.V2
});
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(privateKey);
const { tag, encoding, ciphertext, iv } = crypto.encryption().encryptWithRootEncryptionKey(privateKey);
const updateduser = await authDAL.transaction(async (tx) => {
const us = await userDAL.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
if (!us) throw new Error("User not found");
@@ -225,7 +224,7 @@ export const authSignupServiceFactory = ({
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyEncoding
) {
// get server generated password
const serverGeneratedPassword = infisicalSymmetricDecrypt({
const serverGeneratedPassword = crypto.encryption().decryptWithRootEncryptionKey({
iv: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyIV,
tag: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyTag,
ciphertext: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKey,
@@ -436,7 +435,7 @@ export const authSignupServiceFactory = ({
});
const appCfg = getConfig();
const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
const privateKey = await getUserPrivateKey(password, {
salt,
protectedKey,
@@ -447,7 +446,7 @@ export const authSignupServiceFactory = ({
tag: encryptedPrivateKeyTag,
encryptionVersion: 2
});
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(privateKey);
const { tag, encoding, ciphertext, iv } = crypto.encryption().encryptWithRootEncryptionKey(privateKey);
const updateduser = await authDAL.transaction(async (tx) => {
const us = await userDAL.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
if (!us) throw new Error("User not found");
@@ -464,7 +463,7 @@ export const authSignupServiceFactory = ({
systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyEncoding
) {
// get server generated password
const serverGeneratedPassword = infisicalSymmetricDecrypt({
const serverGeneratedPassword = crypto.encryption().decryptWithRootEncryptionKey({
iv: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyIV,
tag: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKeyTag,
ciphertext: systemGeneratedUserEncryptionKey.serverEncryptedPrivateKey,

View File

@@ -1,9 +1,9 @@
import { ChangeResourceRecordSetsCommand, Route53Client } from "@aws-sdk/client-route-53";
import * as x509 from "@peculiar/x509";
import acme from "acme-client";
import { KeyObject } from "crypto";
import { TableName } from "@app/db/schemas";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { OrgServiceActor } from "@app/lib/types";
import { blockLocalAndPrivateIpAddresses } from "@app/lib/validator";
@@ -404,8 +404,9 @@ export const AcmeCertificateAuthorityFns = ({
});
const alg = keyAlgorithmToAlgCfg(CertKeyAlgorithm.RSA_2048);
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const leafKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const skLeafObj = crypto.rawCrypto.KeyObject.from(leafKeys.privateKey);
const skLeaf = skLeafObj.export({ format: "pem", type: "pkcs8" }) as string;
const [, certificateCsr] = await acme.crypto.createCsr(

View File

@@ -1,6 +1,6 @@
import * as x509 from "@peculiar/x509";
import crypto from "crypto";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { getProjectKmsCertificateKeyId } from "@app/services/project/project-fns";
@@ -133,8 +133,8 @@ export const getCaCredentials = async ({
});
const alg = keyAlgorithmToAlgCfg(ca.internalCa.keyAlgorithm as CertKeyAlgorithm);
const skObj = crypto.createPrivateKey({ key: decryptedPrivateKey, format: "der", type: "pkcs8" });
const caPrivateKey = await crypto.subtle.importKey(
const skObj = crypto.rawCrypto.createPrivateKey({ key: decryptedPrivateKey, format: "der", type: "pkcs8" });
const caPrivateKey = await crypto.rawCrypto.subtle.importKey(
"pkcs8",
skObj.export({ format: "der", type: "pkcs8" }),
alg,
@@ -142,10 +142,14 @@ export const getCaCredentials = async ({
["sign"]
);
const pkObj = crypto.createPublicKey(skObj);
const caPublicKey = await crypto.subtle.importKey("spki", pkObj.export({ format: "der", type: "spki" }), alg, true, [
"verify"
]);
const pkObj = crypto.rawCrypto.createPublicKey(skObj);
const caPublicKey = await crypto.rawCrypto.subtle.importKey(
"spki",
pkObj.export({ format: "der", type: "spki" }),
alg,
true,
["verify"]
);
return {
caSecret,
@@ -277,10 +281,14 @@ export const rebuildCaCrl = async ({
cipherTextBlob: caSecret.encryptedPrivateKey
});
const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [
"sign"
]);
const skObj = crypto.rawCrypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
const sk = await crypto.rawCrypto.subtle.importKey(
"pkcs8",
skObj.export({ format: "der", type: "pkcs8" }),
alg,
true,
["sign"]
);
const revokedCerts = await certificateDAL.find({
caId: ca.id,

View File

@@ -1,8 +1,8 @@
import * as x509 from "@peculiar/x509";
import crypto from "crypto";
import { KeyStorePrefixes, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -198,10 +198,14 @@ export const certificateAuthorityQueueFactory = ({
cipherTextBlob: caSecret.encryptedPrivateKey
});
const skObj = crypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
const sk = await crypto.subtle.importKey("pkcs8", skObj.export({ format: "der", type: "pkcs8" }), alg, true, [
"sign"
]);
const skObj = crypto.rawCrypto.createPrivateKey({ key: privateKey, format: "der", type: "pkcs8" });
const sk = await crypto.rawCrypto.subtle.importKey(
"pkcs8",
skObj.export({ format: "der", type: "pkcs8" }),
alg,
true,
["sign"]
);
const revokedCerts = await certificateDAL.find({
caId: ca.id,

View File

@@ -1,12 +1,12 @@
/* eslint-disable no-bitwise */
import * as x509 from "@peculiar/x509";
import { KeyObject } from "crypto";
import RE2 from "re2";
import { z } from "zod";
import { TCertificateTemplates, TPkiSubscribers } from "@app/db/schemas";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError } from "@app/lib/errors";
import { ms } from "@app/lib/ms";
import { isFQDN } from "@app/lib/validator/validate-url";
@@ -99,7 +99,7 @@ export const InternalCertificateAuthorityFns = ({
}
const alg = keyAlgorithmToAlgCfg(ca.internalCa.keyAlgorithm as CertKeyAlgorithm);
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const leafKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
name: `CN=${subscriber.commonName}`,
@@ -184,7 +184,7 @@ export const InternalCertificateAuthorityFns = ({
extensions
});
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const skLeafObj = crypto.rawCrypto.KeyObject.from(leafKeys.privateKey);
const skLeaf = skLeafObj.export({ format: "pem", type: "pkcs8" }) as string;
const kmsEncryptor = await kmsService.encryptWithKmsKey({
@@ -331,7 +331,7 @@ export const InternalCertificateAuthorityFns = ({
});
const alg = keyAlgorithmToAlgCfg(ca.internalCa.keyAlgorithm as CertKeyAlgorithm);
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const leafKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
name: `CN=${commonName}`,
@@ -450,7 +450,7 @@ export const InternalCertificateAuthorityFns = ({
extensions
});
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const skLeafObj = crypto.rawCrypto.KeyObject.from(leafKeys.privateKey);
const skLeaf = skLeafObj.export({ format: "pem", type: "pkcs8" }) as string;
const kmsEncryptor = await kmsService.encryptWithKmsKey({

View File

@@ -2,7 +2,6 @@
import { ForbiddenError, subject } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import slugify from "@sindresorhus/slugify";
import crypto, { KeyObject } from "crypto";
import { z } from "zod";
import {
@@ -21,6 +20,7 @@ import {
} from "@app/ee/services/permission/project-permission";
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { ms } from "@app/lib/ms";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -189,7 +189,7 @@ export const internalCertificateAuthorityServiceFactory = ({
});
const alg = keyAlgorithmToAlgCfg(keyAlgorithm);
const keys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const keys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const newCa = await certificateAuthorityDAL.transaction(async (tx) => {
const notBeforeDate = notBefore ? new Date(notBefore) : new Date();
@@ -243,8 +243,8 @@ export const internalCertificateAuthorityServiceFactory = ({
kmsId: certificateManagerKmsId
});
// // https://nodejs.org/api/crypto.html#static-method-keyobjectfromkey
const skObj = KeyObject.from(keys.privateKey);
// https://nodejs.org/api/crypto.html#static-method-keyobjectfromkey
const skObj = crypto.rawCrypto.KeyObject.from(keys.privateKey);
const { cipherTextBlob: encryptedPrivateKey } = await kmsEncryptor({
plainText: skObj.export({
@@ -1129,7 +1129,7 @@ export const internalCertificateAuthorityServiceFactory = ({
kmsService
});
const isCaAndCertPublicKeySame = Buffer.from(await crypto.subtle.exportKey("spki", caPublicKey)).equals(
const isCaAndCertPublicKeySame = Buffer.from(await crypto.rawCrypto.subtle.exportKey("spki", caPublicKey)).equals(
Buffer.from(certObj.publicKey.rawData)
);
@@ -1293,7 +1293,7 @@ export const internalCertificateAuthorityServiceFactory = ({
}
const alg = keyAlgorithmToAlgCfg(ca.internalCa.keyAlgorithm as CertKeyAlgorithm);
const leafKeys = await crypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const leafKeys = await crypto.rawCrypto.subtle.generateKey(alg, true, ["sign", "verify"]);
const csrObj = await x509.Pkcs10CertificateRequestGenerator.create({
name: `CN=${commonName}`,
@@ -1440,7 +1440,7 @@ export const internalCertificateAuthorityServiceFactory = ({
extensions
});
const skLeafObj = KeyObject.from(leafKeys.privateKey);
const skLeafObj = crypto.rawCrypto.KeyObject.from(leafKeys.privateKey);
const skLeaf = skLeafObj.export({ format: "pem", type: "pkcs8" }) as string;
const kmsEncryptor = await kmsService.encryptWithKmsKey({

View File

@@ -1,6 +1,5 @@
import { ForbiddenError, subject } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import bcrypt from "bcrypt";
import { ActionProjectType, TCertificateTemplateEstConfigsUpdate } from "@app/db/schemas";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
@@ -11,6 +10,7 @@ import {
} from "@app/ee/services/permission/project-permission";
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { isCertChainValid } from "../certificate/certificate-fns";
@@ -313,7 +313,7 @@ export const certificateTemplateServiceFactory = ({
encryptedCaChain = cipherTextBlob;
}
const hashedPassphrase = await bcrypt.hash(passphrase, appCfg.SALT_ROUNDS);
const hashedPassphrase = await crypto.hashing().createHash(passphrase, appCfg.SALT_ROUNDS);
const estConfig = await certificateTemplateEstConfigDAL.create({
certificateTemplateId,
hashedPassphrase,
@@ -410,7 +410,7 @@ export const certificateTemplateServiceFactory = ({
}
if (passphrase) {
const hashedPassphrase = await bcrypt.hash(passphrase, appCfg.SALT_ROUNDS);
const hashedPassphrase = await crypto.hashing().createHash(passphrase, appCfg.SALT_ROUNDS);
updatedData.hashedPassphrase = hashedPassphrase;
}

View File

@@ -1,8 +1,7 @@
import crypto from "node:crypto";
import * as x509 from "@peculiar/x509";
import RE2 from "re2";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { getProjectKmsCertificateKeyId } from "../project/project-fns";
@@ -87,10 +86,10 @@ export const getCertificateCredentials = async ({
});
try {
const skObj = crypto.createPrivateKey({ key: decryptedPrivateKey, format: "pem", type: "pkcs8" });
const skObj = crypto.rawCrypto.createPrivateKey({ key: decryptedPrivateKey, format: "pem", type: "pkcs8" });
const certPrivateKey = skObj.export({ format: "pem", type: "pkcs8" }).toString();
const pkObj = crypto.createPublicKey(skObj);
const pkObj = crypto.rawCrypto.createPublicKey(skObj);
const certPublicKey = pkObj.export({ format: "pem", type: "spki" }).toString();
return {

View File

@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import * as x509 from "@peculiar/x509";
import { createPrivateKey, createPublicKey, sign, verify } from "crypto";
import { ActionProjectType, ProjectType } from "@app/db/schemas";
import { TCertificateAuthorityCrlDALFactory } from "@app/ee/services/certificate-authority-crl/certificate-authority-crl-dal";
@@ -9,6 +8,7 @@ import {
ProjectPermissionCertificateActions,
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TCertificateBodyDALFactory } from "@app/services/certificate/certificate-body-dal";
import { TCertificateDALFactory } from "@app/services/certificate/certificate-dal";
@@ -391,16 +391,16 @@ export const certificateServiceFactory = ({
// Verify private key matches the certificate
let privateKey;
try {
privateKey = createPrivateKey(privateKeyPem);
privateKey = crypto.rawCrypto.createPrivateKey(privateKeyPem);
} catch (err) {
throw new BadRequestError({ message: "Invalid private key format" });
}
try {
const message = Buffer.from(Buffer.alloc(32));
const publicKey = createPublicKey(certificatePem);
const signature = sign(null, message, privateKey);
const isValid = verify(null, message, publicKey, signature);
const publicKey = crypto.rawCrypto.createPublicKey(certificatePem);
const signature = crypto.rawCrypto.sign(null, message, privateKey);
const isValid = crypto.rawCrypto.verify(null, message, publicKey, signature);
if (!isValid) {
throw new BadRequestError({ message: "Private key does not match certificate" });

View File

@@ -1,10 +1,10 @@
import slugify from "@sindresorhus/slugify";
import { randomUUID } from "crypto";
import sjcl from "sjcl";
import tweetnacl from "tweetnacl";
import tweetnaclUtil from "tweetnacl-util";
import { SecretType, TSecretFolders } from "@app/db/schemas";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { chunkArray } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
@@ -228,7 +228,7 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
}
infisicalImportData.secrets.push({
id: randomUUID(),
id: crypto.rawCrypto.randomUUID(),
name: secretName,
environmentId: matchingAppEnv.id,
value: resolvedSecret.val || "",
@@ -261,7 +261,7 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
}
infisicalImportData.secrets.push({
id: randomUUID(),
id: crypto.rawCrypto.randomUUID(),
name: secretName,
environmentId: matchingAppEnv.id,
value: secretData.val || "",
@@ -348,7 +348,7 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
}
infisicalImportData.secrets.push({
id: randomUUID(),
id: crypto.rawCrypto.randomUUID(),
name: secret,
environmentId: matchingEnv.id,
value: resolvedSecret.val || "",
@@ -380,7 +380,7 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
}
infisicalImportData.secrets.push({
id: randomUUID(),
id: crypto.rawCrypto.randomUUID(),
name: secret,
environmentId: matchingEnv.id,
value: selectedSecret.val || "",
@@ -461,7 +461,7 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
}
infisicalImportData.secrets.push({
id: randomUUID(),
id: crypto.rawCrypto.randomUUID(),
name: secretName,
environmentId: subEnv ? subEnv.parentEnvironmentId : env,
value: resolvedSecret.val || "",
@@ -487,7 +487,7 @@ export const parseEnvKeyDataFn = async (decryptedJson: string): Promise<Infisica
const folderId = targetIdToFolderIdsMap.get(subEnv?.id || "") || subEnv?.id;
infisicalImportData.secrets.push({
id: randomUUID(),
id: crypto.rawCrypto.randomUUID(),
name: secretName,
environmentId: subEnv ? subEnv.parentEnvironmentId : env,
value: secretData.val || "",

View File

@@ -1,5 +1,5 @@
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueServiceFactory } from "@app/queue";
@@ -98,7 +98,7 @@ export const externalMigrationQueueFactory = ({
template: SmtpTemplates.ExternalImportStarted
});
const decrypted = infisicalSymmetricDecrypt({
const decrypted = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: data.ciphertext,
iv: data.iv,
keyEncoding: data.encoding,

View File

@@ -1,7 +1,7 @@
import { OrgMembershipRole } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { ForbiddenRequestError } from "@app/lib/errors";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError } from "@app/lib/errors";
import { TUserDALFactory } from "../user/user-dal";
import { decryptEnvKeyDataFn, parseEnvKeyDataFn } from "./external-migration-fns";
@@ -29,6 +29,10 @@ export const externalMigrationServiceFactory = ({
actorOrgId,
actorAuthMethod
}: TImportEnvKeyDataCreate) => {
if (crypto.isFipsModeEnabled()) {
throw new BadRequestError({ message: "EnvKey migration is not supported when running in FIPS mode." });
}
const { membership } = await permissionService.getOrgPermission(
actor,
actorId,
@@ -52,7 +56,7 @@ export const externalMigrationServiceFactory = ({
actorAuthMethod
});
const encrypted = infisicalSymmetricEncypt(stringifiedJson);
const encrypted = crypto.encryption().encryptWithRootEncryptionKey(stringifiedJson);
await externalMigrationQueue.startImport({
actorEmail: user.email!,

View File

@@ -8,8 +8,7 @@ import {
} from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionGroupActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError, PermissionBoundaryError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { ms } from "@app/lib/ms";
@@ -214,14 +213,14 @@ export const groupProjectServiceFactory = ({
});
}
const botPrivateKey = infisicalSymmetricDecrypt({
const botPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
iv: bot.iv,
tag: bot.tag,
ciphertext: bot.encryptedPrivateKey
});
const plaintextProjectKey = decryptAsymmetric({
const plaintextProjectKey = crypto.encryption().asymmetric().decrypt({
ciphertext: ghostUserLatestKey.encryptedKey,
nonce: ghostUserLatestKey.nonce,
publicKey: ghostUserLatestKey.sender.publicKey,
@@ -229,7 +228,10 @@ export const groupProjectServiceFactory = ({
});
const projectKeyData = groupMembers.map(({ user: { publicKey, id } }) => {
const { ciphertext: encryptedKey, nonce } = encryptAsymmetric(plaintextProjectKey, publicKey, botPrivateKey);
const { ciphertext: encryptedKey, nonce } = crypto
.encryption()
.asymmetric()
.encrypt(plaintextProjectKey, publicKey, botPrivateKey);
return {
encryptedKey,

View File

@@ -1,5 +1,3 @@
import crypto from "node:crypto";
import { ForbiddenError } from "@casl/ability";
import jwt from "jsonwebtoken";
@@ -13,6 +11,7 @@ import {
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { extractX509CertFromChain } from "@app/lib/certificates/extract-certificate";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
import { extractIPDetails, isValidIpOrCidr } from "@app/lib/ip";
@@ -87,8 +86,8 @@ export const identityTlsCertAuthServiceFactory = ({
throw new BadRequestError({ message: "Missing client certificate" });
}
const clientCertificateX509 = new crypto.X509Certificate(leafCertificate);
const caCertificateX509 = new crypto.X509Certificate(caCertificate);
const clientCertificateX509 = new crypto.rawCrypto.X509Certificate(leafCertificate);
const caCertificateX509 = new crypto.rawCrypto.X509Certificate(caCertificate);
const isValidCertificate = clientCertificateX509.verify(caCertificateX509.publicKey);
if (!isValidCertificate)

View File

@@ -1,7 +1,4 @@
import crypto from "node:crypto";
import { ForbiddenError } from "@casl/ability";
import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { IdentityAuthMethod } from "@app/db/schemas";
@@ -13,6 +10,7 @@ import {
} from "@app/ee/services/permission/permission-fns";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError, PermissionBoundaryError, UnauthorizedError } from "@app/lib/errors";
import { checkIPAgainstBlocklist, extractIPDetails, isValidIpOrCidr, TIp } from "@app/lib/ip";
@@ -76,7 +74,8 @@ export const identityUaServiceFactory = ({
let validClientSecretInfo: (typeof clientSecrtInfo)[0] | null = null;
for await (const info of clientSecrtInfo) {
const isMatch = await bcrypt.compare(clientSecret, info.clientSecretHash);
const isMatch = await crypto.hashing().compareHash(clientSecret, info.clientSecretHash);
if (isMatch) {
validClientSecretInfo = info;
break;
@@ -250,7 +249,7 @@ export const identityUaServiceFactory = ({
const doc = await identityUaDAL.create(
{
identityId: identityMembershipOrg.identityId,
clientId: crypto.randomUUID(),
clientId: crypto.rawCrypto.randomUUID(),
clientSecretTrustedIps: JSON.stringify(reformattedClientSecretTrustedIps),
accessTokenMaxTTL,
accessTokenTTL,
@@ -494,7 +493,7 @@ export const identityUaServiceFactory = ({
const appCfg = getConfig();
const clientSecret = crypto.randomBytes(32).toString("hex");
const clientSecretHash = await bcrypt.hash(clientSecret, appCfg.SALT_ROUNDS);
const clientSecretHash = await crypto.hashing().createHash(clientSecret, appCfg.SALT_ROUNDS);
const identityUaAuth = await identityUaDAL.findOne({ identityId: identityMembershipOrg.identityId });
if (!identityUaAuth) throw new NotFoundError({ message: `Failed to find identity with ID ${identityId}` });

View File

@@ -15,7 +15,7 @@ import { TPermissionServiceFactory } from "@app/ee/services/permission/permissio
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { decryptSymmetric128BitHexKeyUTF8, encryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { BadRequestError, InternalServerError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
@@ -228,13 +228,22 @@ export const integrationAuthServiceFactory = ({
} else {
if (!botKey) throw new NotFoundError({ message: `Project bot key for project with ID '${projectId}' not found` });
if (tokenExchange.refreshToken) {
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenExchange.refreshToken, botKey);
const refreshEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenExchange.refreshToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.refreshIV = refreshEncToken.iv;
updateDoc.refreshTag = refreshEncToken.tag;
updateDoc.refreshCiphertext = refreshEncToken.ciphertext;
}
if (tokenExchange.accessToken) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenExchange.accessToken, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenExchange.accessToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext;
@@ -357,11 +366,19 @@ export const integrationAuthServiceFactory = ({
url,
updateDoc.metadata as Record<string, string>
);
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, botKey);
const refreshEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenDetails.refreshToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.refreshIV = refreshEncToken.iv;
updateDoc.refreshTag = refreshEncToken.tag;
updateDoc.refreshCiphertext = refreshEncToken.ciphertext;
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenDetails.accessToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext;
@@ -371,19 +388,31 @@ export const integrationAuthServiceFactory = ({
if (!refreshToken && (accessId || accessToken || awsAssumeIamRoleArn)) {
if (accessToken) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessToken, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: accessToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext;
}
if (accessId) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessId, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: accessId,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIdIV = accessEncToken.iv;
updateDoc.accessIdTag = accessEncToken.tag;
updateDoc.accessIdCiphertext = accessEncToken.ciphertext;
}
if (awsAssumeIamRoleArn) {
const awsAssumeIamRoleArnEnc = encryptSymmetric128BitHexKeyUTF8(awsAssumeIamRoleArn, botKey);
const awsAssumeIamRoleArnEnc = crypto.encryption().encryptSymmetric({
plaintext: awsAssumeIamRoleArn,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.awsAssumeIamRoleArnCipherText = awsAssumeIamRoleArnEnc.ciphertext;
updateDoc.awsAssumeIamRoleArnIV = awsAssumeIamRoleArnEnc.iv;
updateDoc.awsAssumeIamRoleArnTag = awsAssumeIamRoleArnEnc.tag;
@@ -499,11 +528,21 @@ export const integrationAuthServiceFactory = ({
url,
updateDoc.metadata as Record<string, string>
);
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, botKey);
const refreshEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenDetails.refreshToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.refreshIV = refreshEncToken.iv;
updateDoc.refreshTag = refreshEncToken.tag;
updateDoc.refreshCiphertext = refreshEncToken.ciphertext;
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenDetails.accessToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext;
@@ -513,19 +552,32 @@ export const integrationAuthServiceFactory = ({
if (!refreshToken && (accessId || accessToken || awsAssumeIamRoleArn)) {
if (accessToken) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessToken, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: accessToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIV = accessEncToken.iv;
updateDoc.accessTag = accessEncToken.tag;
updateDoc.accessCiphertext = accessEncToken.ciphertext;
}
if (accessId) {
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(accessId, botKey);
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: accessId,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.accessIdIV = accessEncToken.iv;
updateDoc.accessIdTag = accessEncToken.tag;
updateDoc.accessIdCiphertext = accessEncToken.ciphertext;
}
if (awsAssumeIamRoleArn) {
const awsAssumeIamRoleArnEnc = encryptSymmetric128BitHexKeyUTF8(awsAssumeIamRoleArn, botKey);
const awsAssumeIamRoleArnEnc = crypto.encryption().encryptSymmetric({
plaintext: awsAssumeIamRoleArn,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
updateDoc.awsAssumeIamRoleArnCipherText = awsAssumeIamRoleArnEnc.ciphertext;
updateDoc.awsAssumeIamRoleArnIV = awsAssumeIamRoleArnEnc.iv;
updateDoc.awsAssumeIamRoleArnTag = awsAssumeIamRoleArnEnc.tag;
@@ -608,20 +660,22 @@ export const integrationAuthServiceFactory = ({
} else {
if (!botKey) throw new NotFoundError({ message: "Project bot key not found" });
if (integrationAuth.accessTag && integrationAuth.accessIV && integrationAuth.accessCiphertext) {
accessToken = decryptSymmetric128BitHexKeyUTF8({
accessToken = crypto.encryption().decryptSymmetric({
ciphertext: integrationAuth.accessCiphertext,
iv: integrationAuth.accessIV,
tag: integrationAuth.accessTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
}
if (integrationAuth.refreshCiphertext && integrationAuth.refreshIV && integrationAuth.refreshTag) {
const refreshToken = decryptSymmetric128BitHexKeyUTF8({
const refreshToken = crypto.encryption().decryptSymmetric({
key: botKey,
ciphertext: integrationAuth.refreshCiphertext,
iv: integrationAuth.refreshIV,
tag: integrationAuth.refreshTag
tag: integrationAuth.refreshTag,
keySize: SymmetricKeySize.Bits128
});
if (integrationAuth.accessExpiresAt && integrationAuth.accessExpiresAt < new Date()) {
@@ -632,8 +686,18 @@ export const integrationAuthServiceFactory = ({
integrationAuth?.url,
integrationAuth.metadata as Record<string, string>
);
const refreshEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.refreshToken, botKey);
const accessEncToken = encryptSymmetric128BitHexKeyUTF8(tokenDetails.accessToken, botKey);
const refreshEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenDetails.refreshToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const accessEncToken = crypto.encryption().encryptSymmetric({
plaintext: tokenDetails.accessToken,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
accessToken = tokenDetails.accessToken;
await integrationAuthDAL.updateById(integrationAuth.id, {
refreshIV: refreshEncToken.iv,
@@ -649,11 +713,12 @@ export const integrationAuthServiceFactory = ({
if (!accessToken) throw new BadRequestError({ message: "Missing access token" });
if (integrationAuth.accessIdTag && integrationAuth.accessIdIV && integrationAuth.accessIdCiphertext) {
accessId = decryptSymmetric128BitHexKeyUTF8({
accessId = crypto.encryption().decryptSymmetric({
key: botKey,
ciphertext: integrationAuth.accessIdCiphertext,
iv: integrationAuth.accessIdIV,
tag: integrationAuth.accessIdTag
tag: integrationAuth.accessIdTag,
keySize: SymmetricKeySize.Bits128
});
}
}

View File

@@ -5,7 +5,7 @@ import { Octokit } from "@octokit/rest";
import { TIntegrationAuths, TIntegrations } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -109,12 +109,14 @@ const getIntegrationSecretsV1 = async (
// process secrets in current folder
const secrets = await secretDAL.findByFolderId(dto.folderId);
secrets.forEach((secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: dto.key
key: dto.key,
keySize: SymmetricKeySize.Bits128
});
content[secretKey] = true;

View File

@@ -23,7 +23,6 @@ import { createAppAuth } from "@octokit/auth-app";
import { Octokit } from "@octokit/rest";
import AWS, { AWSError } from "aws-sdk";
import { AxiosError } from "axios";
import { randomUUID } from "crypto";
import https from "https";
import sodium from "libsodium-wrappers";
import isEqual from "lodash.isequal";
@@ -33,6 +32,7 @@ import { z } from "zod";
import { SecretType, TIntegrationAuths, TIntegrations } from "@app/db/schemas";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, InternalServerError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TCreateManySecretsRawFn, TUpdateManySecretsRawFn } from "@app/services/secret/secret-types";
@@ -806,7 +806,7 @@ const syncSecretsAWSParameterStore = async ({
});
const command = new AssumeRoleCommand({
RoleArn: awsAssumeRoleArn,
RoleSessionName: `infisical-parameter-store-${randomUUID()}`,
RoleSessionName: `infisical-parameter-store-${crypto.rawCrypto.randomUUID()}`,
DurationSeconds: 900, // 15mins
ExternalId: projectId
});
@@ -1126,7 +1126,7 @@ const syncSecretsAWSSecretManager = async ({
});
const command = new AssumeRoleCommand({
RoleArn: awsAssumeRoleArn,
RoleSessionName: `infisical-sm-${randomUUID()}`,
RoleSessionName: `infisical-sm-${crypto.rawCrypto.randomUUID()}`,
DurationSeconds: 900, // 15mins
ExternalId: projectId
});

View File

@@ -14,9 +14,8 @@ import {
import { THsmServiceFactory } from "@app/ee/services/hsm/hsm-service";
import { KeyStorePrefixes, PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
import { TEnvConfig } from "@app/lib/config/env";
import { randomSecureBytes } from "@app/lib/crypto";
import { symmetricCipherService, SymmetricKeyAlgorithm } from "@app/lib/crypto/cipher";
import { generateHash } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { AsymmetricKeyAlgorithm, signingService } from "@app/lib/crypto/sign";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -101,7 +100,7 @@ export const kmsServiceFactory = ({
let kmsKeyMaterial: Buffer | null = null;
if (keyUsage === KmsKeyUsage.ENCRYPT_DECRYPT) {
kmsKeyMaterial = randomSecureBytes(
kmsKeyMaterial = crypto.randomBytes(
getByteLengthForSymmetricEncryptionAlgorithm(encryptionAlgorithm as SymmetricKeyAlgorithm)
);
} else if (keyUsage === KmsKeyUsage.SIGN_VERIFY) {
@@ -618,7 +617,7 @@ export const kmsServiceFactory = ({
return;
}
const dataKey = randomSecureBytes();
const dataKey = crypto.randomBytes(32);
const kmsEncryptor = await encryptWithKmsKey(
{
kmsId: kmsKeyId
@@ -761,7 +760,7 @@ export const kmsServiceFactory = ({
return;
}
const dataKey = randomSecureBytes();
const dataKey = crypto.randomBytes(32);
const kmsEncryptor = await encryptWithKmsKey(
{
kmsId: kmsKeyId
@@ -994,7 +993,7 @@ export const kmsServiceFactory = ({
"base64"
)}`;
const verificationHash = generateHash(secretManagerBackup);
const verificationHash = crypto.rawCrypto.createHash("sha256").update(secretManagerBackup).digest("hex");
secretManagerBackup = `${secretManagerBackup}.${verificationHash}`;
return {
@@ -1011,7 +1010,12 @@ export const kmsServiceFactory = ({
}
const [, backupProjectId, , backupKmsKeyId, backupBase64EncryptedDataKey, backupHash] = backup.split(".");
const computedHash = generateHash(backup.substring(0, backup.lastIndexOf(".")));
const computedHash = crypto.rawCrypto
.createHash("sha256")
.update(backup.substring(0, backup.lastIndexOf(".")))
.digest("hex");
if (computedHash !== backupHash) {
throw new BadRequestError({
message: "Invalid backup"
@@ -1075,7 +1079,7 @@ export const kmsServiceFactory = ({
if (existingRootConfig) return existingRootConfig;
logger.info("KMS: Generating new ROOT Key");
const newRootKey = randomSecureBytes(32);
const newRootKey = crypto.randomBytes(32);
const encryptedRootKey = await $encryptRootKey(newRootKey, RootKeyEncryptionStrategy.Software).catch((err) => {
logger.error({ hsmEnabled: hsmService.isActive() }, "KMS: Failed to encrypt ROOT Key");
throw err;

View File

@@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability";
import { ProjectMembershipRole, ProjectVersion, SecretKeyEncoding } from "@app/db/schemas";
import { OrgPermissionAdminConsoleAction, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TProjectDALFactory } from "../project/project-dal";
@@ -144,7 +144,7 @@ export const orgAdminServiceFactory = ({
});
}
const botPrivateKey = infisicalSymmetricDecrypt({
const botPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
iv: bot.iv,
tag: bot.tag,

View File

@@ -1,6 +1,5 @@
import { ForbiddenError } from "@casl/ability";
import slugify from "@sindresorhus/slugify";
import crypto from "crypto";
import jwt from "jsonwebtoken";
import { Knex } from "knex";
@@ -34,8 +33,7 @@ import { ProjectPermissionMemberActions, ProjectPermissionSub } from "@app/ee/se
import { TProjectUserAdditionalPrivilegeDALFactory } from "@app/ee/services/project-user-additional-privilege/project-user-additional-privilege-dal";
import { TSamlConfigDALFactory } from "@app/ee/services/saml-config/saml-config-dal";
import { getConfig } from "@app/lib/config/env";
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
import { generateSymmetricKey, infisicalSymmetricDecrypt, infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { generateUserSrpKeys } from "@app/lib/crypto/srp";
import {
BadRequestError,
@@ -251,6 +249,7 @@ export const orgServiceFactory = ({
const addGhostUser = async (orgId: string, tx?: Knex) => {
const email = `sudo-${alphaNumericNanoId(16)}-${orgId}@infisical.com`; // We add a nanoid because the email is unique. And we have to create a new ghost user each time, so we can have access to the private key.
const password = crypto.randomBytes(128).toString("hex");
const user = await userDAL.create(
@@ -492,22 +491,22 @@ export const orgServiceFactory = ({
orgName: string;
userEmail?: string | null;
}) => {
const { privateKey, publicKey } = generateAsymmetricKeyPair();
const key = generateSymmetricKey();
const { privateKey, publicKey } = crypto.encryption().asymmetric().generateKeyPair();
const key = crypto.randomBytes(32).toString("base64");
const {
ciphertext: encryptedPrivateKey,
iv: privateKeyIV,
tag: privateKeyTag,
encoding: privateKeyKeyEncoding,
algorithm: privateKeyAlgorithm
} = infisicalSymmetricEncypt(privateKey);
} = crypto.encryption().encryptWithRootEncryptionKey(privateKey);
const {
ciphertext: encryptedSymmetricKey,
iv: symmetricKeyIV,
tag: symmetricKeyTag,
encoding: symmetricKeyKeyEncoding,
algorithm: symmetricKeyAlgorithm
} = infisicalSymmetricEncypt(key);
} = crypto.encryption().encryptWithRootEncryptionKey(key);
const customerId = await licenseService.generateOrgCustomerId(orgName, userEmail);
const organization = await orgDAL.transaction(async (tx) => {
@@ -825,6 +824,7 @@ export const orgServiceFactory = ({
const mailsForOrgInvitation: { email: string; userId: string; firstName: string; lastName: string }[] = [];
const mailsForProjectInvitation: { email: string[]; projectName: string }[] = [];
const newProjectMemberships: TProjectMemberships[] = [];
await orgDAL.transaction(async (tx) => {
const users: Pick<TUsers, "id" | "firstName" | "lastName" | "email" | "username">[] = [];
@@ -864,7 +864,9 @@ export const orgServiceFactory = ({
// Then when user sign in (as login is not possible as isAccepted is false) we rencrypt the private key with the user password
if (!inviteeUser || (inviteeUser && !inviteeUser?.isAccepted && !existingEncrytionKey)) {
const serverGeneratedPassword = crypto.randomBytes(32).toString("hex");
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(serverGeneratedPassword);
const { tag, encoding, ciphertext, iv } = crypto
.encryption()
.encryptWithRootEncryptionKey(serverGeneratedPassword);
const encKeys = await generateUserSrpKeys(inviteeEmail, serverGeneratedPassword);
await userDAL.createUserEncryption(
{
@@ -1091,9 +1093,9 @@ export const orgServiceFactory = ({
tx
);
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(
newGhostUser.keys.plainPrivateKey
);
const { iv, tag, ciphertext, encoding, algorithm } = crypto
.encryption()
.encryptWithRootEncryptionKey(newGhostUser.keys.plainPrivateKey);
if (autoGeneratedBot) {
await projectBotDAL.updateById(
autoGeneratedBot.id,
@@ -1131,7 +1133,7 @@ export const orgServiceFactory = ({
});
}
const botPrivateKey = infisicalSymmetricDecrypt({
const botPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
iv: bot.iv,
tag: bot.tag,

View File

@@ -1,24 +1,19 @@
import { SecretKeyEncoding } from "@app/db/schemas";
import {
decryptAsymmetric,
encryptAsymmetric,
generateAsymmetricKeyPair,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { TProjectBotDALFactory } from "@app/services/project-bot/project-bot-dal";
import { TProjectDALFactory } from "../project/project-dal";
import { TGetPrivateKeyDTO } from "./project-bot-types";
export const getBotPrivateKey = ({ bot }: TGetPrivateKeyDTO) =>
infisicalSymmetricDecrypt({
export const getBotPrivateKey = ({ bot }: TGetPrivateKeyDTO) => {
return crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: bot.keyEncoding as SecretKeyEncoding,
iv: bot.iv,
tag: bot.tag,
ciphertext: bot.encryptedPrivateKey
});
};
export const getBotKeyFnFactory = (
projectBotDAL: TProjectBotDALFactory,
@@ -51,22 +46,27 @@ export const getBotKeyFnFactory = (
projectV1Keys.serverEncryptedPrivateKeyTag &&
projectV1Keys.serverEncryptedPrivateKeyEncoding
) {
userPrivateKey = infisicalSymmetricDecrypt({
userPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
iv: projectV1Keys.serverEncryptedPrivateKeyIV,
tag: projectV1Keys.serverEncryptedPrivateKeyTag,
ciphertext: projectV1Keys.serverEncryptedPrivateKey,
keyEncoding: projectV1Keys.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding
});
}
const workspaceKey = decryptAsymmetric({
const workspaceKey = crypto.encryption().asymmetric().decrypt({
ciphertext: projectV1Keys.projectEncryptedKey,
nonce: projectV1Keys.projectKeyNonce,
publicKey: projectV1Keys.senderPublicKey,
privateKey: userPrivateKey
});
const botKey = generateAsymmetricKeyPair();
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(botKey.privateKey);
const encryptedWorkspaceKey = encryptAsymmetric(workspaceKey, botKey.publicKey, userPrivateKey);
const botKey = crypto.encryption().asymmetric().generateKeyPair();
const { iv, tag, ciphertext, encoding, algorithm } = crypto
.encryption()
.encryptWithRootEncryptionKey(botKey.privateKey);
const encryptedWorkspaceKey = crypto
.encryption()
.asymmetric()
.encrypt(workspaceKey, botKey.publicKey, userPrivateKey);
let botId;
if (!bot) {
@@ -105,7 +105,7 @@ export const getBotKeyFnFactory = (
}
const botPrivateKey = getBotPrivateKey({ bot });
const botKey = decryptAsymmetric({
const botKey = crypto.encryption().asymmetric().decrypt({
ciphertext: bot.encryptedProjectKey,
privateKey: botPrivateKey,
nonce: bot.encryptedProjectKeyNonce,

View File

@@ -3,8 +3,7 @@ import { ForbiddenError } from "@casl/ability";
import { ActionProjectType, ProjectVersion } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionActions, ProjectPermissionSub } from "@app/ee/services/permission/project-permission";
import { generateAsymmetricKeyPair } from "@app/lib/crypto";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { TProjectDALFactory } from "../project/project-dal";
@@ -55,9 +54,12 @@ export const projectBotServiceFactory = ({
const doc = await projectBotDAL.findOne({ projectId }, tx);
if (doc) return doc;
const keys = privateKey && publicKey ? { privateKey, publicKey } : generateAsymmetricKeyPair();
const keys =
privateKey && publicKey ? { privateKey, publicKey } : crypto.encryption().asymmetric().generateKeyPair();
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(keys.privateKey);
const { iv, tag, ciphertext, encoding, algorithm } = crypto
.encryption()
.encryptWithRootEncryptionKey(keys.privateKey);
const project = await projectDAL.findById(projectId, tx);

View File

@@ -1,10 +1,8 @@
import crypto from "crypto";
import { ProjectVersion, TProjects } from "@app/db/schemas";
import { createSshCaHelper } from "@app/ee/services/ssh/ssh-certificate-authority-fns";
import { SshCaKeySource } from "@app/ee/services/ssh/ssh-certificate-authority-types";
import { SshCertKeyAlgorithm } from "@app/ee/services/ssh-certificate/ssh-certificate-types";
import { decryptAsymmetric, encryptAsymmetric } from "@app/lib/crypto";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { TKmsServiceFactory } from "@app/services/kms/kms-service";
import { TProjectDALFactory } from "@app/services/project/project-dal";
@@ -12,7 +10,7 @@ import { TProjectDALFactory } from "@app/services/project/project-dal";
import { AddUserToWsDTO, TBootstrapSshProjectDTO } from "./project-types";
export const assignWorkspaceKeysToMembers = ({ members, decryptKey, userPrivateKey }: AddUserToWsDTO) => {
const plaintextProjectKey = decryptAsymmetric({
const plaintextProjectKey = crypto.encryption().asymmetric().decrypt({
ciphertext: decryptKey.encryptedKey,
nonce: decryptKey.nonce,
publicKey: decryptKey.sender.publicKey,
@@ -20,11 +18,10 @@ export const assignWorkspaceKeysToMembers = ({ members, decryptKey, userPrivateK
});
const newWsMembers = members.map(({ orgMembershipId, userPublicKey }) => {
const { ciphertext: inviteeCipherText, nonce: inviteeNonce } = encryptAsymmetric(
plaintextProjectKey,
userPublicKey,
userPrivateKey
);
const { ciphertext: inviteeCipherText, nonce: inviteeNonce } = crypto
.encryption()
.asymmetric()
.encrypt(plaintextProjectKey, userPublicKey, userPrivateKey);
return {
orgMembershipId,
@@ -47,11 +44,10 @@ export const createProjectKey = ({ publicKey, privateKey, plainProjectKey }: TCr
const randomBytes = plainProjectKey || crypto.randomBytes(16).toString("hex");
// 4. Encrypt the project key with the users key pair.
const { ciphertext: encryptedProjectKey, nonce: encryptedProjectKeyIv } = encryptAsymmetric(
randomBytes,
publicKey,
privateKey
);
const { ciphertext: encryptedProjectKey, nonce: encryptedProjectKeyIv } = crypto
.encryption()
.asymmetric()
.encrypt(randomBytes, publicKey, privateKey);
return { key: encryptedProjectKey, iv: encryptedProjectKeyIv };
};

View File

@@ -21,14 +21,10 @@ import {
decryptIntegrationAuths,
decryptSecretApprovals,
decryptSecrets,
decryptSecretVersions
decryptSecretVersions,
SymmetricKeySize
} from "@app/lib/crypto";
import {
decryptAsymmetric,
encryptSymmetric128BitHexKeyUTF8,
infisicalSymmetricDecrypt,
infisicalSymmetricEncypt
} from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { logger } from "@app/lib/logger";
import { QueueJobs, QueueName, TQueueJobTypes, TQueueServiceFactory } from "@app/queue";
@@ -118,17 +114,14 @@ export const projectQueueFactory = ({
await projectDAL.setProjectUpgradeStatus(data.projectId, ProjectUpgradeStatus.InProgress); // Set the status to in progress. This is important to prevent multiple upgrades at the same time.
// eslint-disable-next-line no-promise-executor-return
// await new Promise((resolve) => setTimeout(resolve, 50_000));
const userPrivateKey = infisicalSymmetricDecrypt({
const userPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: data.encryptedPrivateKey.keyEncoding,
ciphertext: data.encryptedPrivateKey.encryptedKey,
iv: data.encryptedPrivateKey.encryptedKeyIv,
tag: data.encryptedPrivateKey.encryptedKeyTag
});
const decryptedPlainProjectKey = decryptAsymmetric({
const decryptedPlainProjectKey = crypto.encryption().asymmetric().decrypt({
ciphertext: oldProjectKey.encryptedKey,
nonce: oldProjectKey.nonce,
publicKey: oldProjectKey.sender.publicKey,
@@ -321,7 +314,9 @@ export const projectQueueFactory = ({
await projectKeyDAL.insertMany(newProjectMembers, tx);
// Encrypt the bot private key (which is the same as the ghost user)
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(ghostUser.keys.plainPrivateKey);
const { iv, tag, ciphertext, encoding, algorithm } = crypto
.encryption()
.encryptWithRootEncryptionKey(ghostUser.keys.plainPrivateKey);
// 5. Create a bot for the project
const newBot = await projectBotDAL.create(
@@ -342,14 +337,14 @@ export const projectQueueFactory = ({
tx
);
const botPrivateKey = infisicalSymmetricDecrypt({
const botPrivateKey = crypto.encryption().decryptWithRootEncryptionKey({
keyEncoding: newBot.keyEncoding as SecretKeyEncoding,
iv: newBot.iv,
tag: newBot.tag,
ciphertext: newBot.encryptedPrivateKey
});
const botKey = decryptAsymmetric({
const botKey = crypto.encryption().asymmetric().decrypt({
ciphertext: newBot.encryptedProjectKey!,
privateKey: botPrivateKey,
nonce: newBot.encryptedProjectKeyNonce!,
@@ -361,12 +356,23 @@ export const projectQueueFactory = ({
const updatedSecretApprovals: TSecretApprovalRequestsSecrets[] = [];
const updatedIntegrationAuths: TIntegrationAuths[] = [];
for (const rawSecret of decryptedSecrets) {
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(rawSecret.decrypted.secretKey, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(rawSecret.decrypted.secretValue || "", botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(
rawSecret.decrypted.secretComment || "",
botKey
);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecret.decrypted.secretKey,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecret.decrypted.secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecret.decrypted.secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const payload: TSecrets = {
...rawSecret.original,
@@ -393,15 +399,23 @@ export const projectQueueFactory = ({
}
for (const rawSecretVersion of decryptedSecretVersions) {
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(rawSecretVersion.decrypted.secretKey, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(
rawSecretVersion.decrypted.secretValue || "",
botKey
);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(
rawSecretVersion.decrypted.secretComment || "",
botKey
);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecretVersion.decrypted.secretKey,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecretVersion.decrypted.secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecretVersion.decrypted.secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const payload: TSecretVersions = {
...rawSecretVersion.original,
@@ -428,15 +442,21 @@ export const projectQueueFactory = ({
}
for (const rawSecretApproval of decryptedApprovalSecrets) {
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(rawSecretApproval.decrypted.secretKey, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(
rawSecretApproval.decrypted.secretValue || "",
botKey
);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(
rawSecretApproval.decrypted.secretComment || "",
botKey
);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecretApproval.decrypted.secretKey,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecretApproval.decrypted.secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: rawSecretApproval.decrypted.secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const payload: TSecretApprovalRequestsSecrets = {
...rawSecretApproval.original,
@@ -463,9 +483,21 @@ export const projectQueueFactory = ({
}
for (const integrationAuth of decryptedIntegrationAuths) {
const access = encryptSymmetric128BitHexKeyUTF8(integrationAuth.decrypted.access, botKey);
const accessId = encryptSymmetric128BitHexKeyUTF8(integrationAuth.decrypted.accessId, botKey);
const refresh = encryptSymmetric128BitHexKeyUTF8(integrationAuth.decrypted.refresh, botKey);
const access = crypto.encryption().encryptSymmetric({
plaintext: integrationAuth.decrypted.access,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const accessId = crypto.encryption().encryptSymmetric({
plaintext: integrationAuth.decrypted.accessId,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const refresh = crypto.encryption().encryptSymmetric({
plaintext: integrationAuth.decrypted.refresh,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const payload: TIntegrationAuths = {
...integrationAuth.original,

View File

@@ -34,7 +34,7 @@ import { TSshHostDALFactory } from "@app/ee/services/ssh-host/ssh-host-dal";
import { TSshHostGroupDALFactory } from "@app/ee/services/ssh-host-group/ssh-host-group-dal";
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy } from "@app/lib/fn";
import { alphaNumericNanoId } from "@app/lib/nanoid";
@@ -392,7 +392,9 @@ export const projectServiceFactory = ({
tx
);
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(ghostUser.keys.plainPrivateKey);
const { iv, tag, ciphertext, encoding, algorithm } = crypto
.encryption()
.encryptWithRootEncryptionKey(ghostUser.keys.plainPrivateKey);
// 5. Create & a bot for the project
await projectBotDAL.create(
@@ -853,7 +855,7 @@ export const projectServiceFactory = ({
});
}
const encryptedPrivateKey = infisicalSymmetricEncypt(userPrivateKey);
const encryptedPrivateKey = crypto.encryption().encryptWithRootEncryptionKey(userPrivateKey);
await projectQueue.upgradeProject({
projectId,

View File

@@ -1,10 +1,7 @@
import crypto from "node:crypto";
import bcrypt from "bcrypt";
import { TSecretSharing } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { SecretSharingAccessType } from "@app/lib/types";
@@ -136,7 +133,7 @@ export const secretSharingServiceFactory = ({
const encryptedSecret = encryptWithRoot(Buffer.from(secretValue));
const id = crypto.randomBytes(32).toString("hex");
const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
const hashedPassword = password ? await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS) : null;
const newSharedSecret = await secretSharingDAL.create({
identifier: id,
@@ -386,8 +383,10 @@ export const secretSharingServiceFactory = ({
const encryptWithRoot = kmsService.encryptWithRootKey();
const encryptedSecret = encryptWithRoot(Buffer.from(secretValue));
const appCfg = getConfig();
const id = crypto.randomBytes(32).toString("hex");
const hashedPassword = password ? await bcrypt.hash(password, 10) : null;
const hashedPassword = password ? await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS) : null;
const newSharedSecret = await secretSharingDAL.create({
identifier: id,
@@ -529,7 +528,7 @@ export const secretSharingServiceFactory = ({
const hasProvidedPassword = Boolean(password);
if (isPasswordProtected) {
if (hasProvidedPassword) {
const isMatch = await bcrypt.compare(password as string, sharedSecret.password as string);
const isMatch = await crypto.hashing().compareHash(password as string, sharedSecret.password as string);
if (!isMatch) throw new UnauthorizedError({ message: "Invalid credentials" });
} else {
return { isPasswordProtected };

View File

@@ -16,11 +16,8 @@ import { hasSecretReadValueOrDescribePermission } from "@app/ee/services/permiss
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { ProjectPermissionSecretActions } from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
import {
buildSecretBlindIndexFromName,
decryptSymmetric128BitHexKeyUTF8,
encryptSymmetric128BitHexKeyUTF8
} from "@app/lib/crypto";
import { buildSecretBlindIndexFromName } from "@app/lib/crypto";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy, unique } from "@app/lib/fn";
@@ -239,17 +236,19 @@ export const interpolateSecrets = ({ projectId, secretEncKey, secretDAL, folderD
const secrets = await secretDAL.findByFolderId(folder.id);
const decryptedSec = secrets.reduce<Record<string, string>>((prev, secret) => {
const decryptedSecretKey = decryptSymmetric128BitHexKeyUTF8({
const decryptedSecretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: secretEncKey
key: secretEncKey,
keySize: SymmetricKeySize.Bits128
});
const decryptedSecretValue = decryptSymmetric128BitHexKeyUTF8({
const decryptedSecretValue = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: secretEncKey
key: secretEncKey,
keySize: SymmetricKeySize.Bits128
});
// eslint-disable-next-line
@@ -366,30 +365,33 @@ export const decryptSecretRaw = (
},
key: string
) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
const secretValue = !secret.secretValueHidden
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
key,
keySize: SymmetricKeySize.Bits128
})
: INFISICAL_SECRET_VALUE_HIDDEN_MASK;
let secretComment = "";
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
secretComment = decryptSymmetric128BitHexKeyUTF8({
secretComment = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
}
@@ -877,11 +879,24 @@ export const createManySecretsRawFnFactory = ({
message: `Project bot not found for project with ID '${projectId}'. Please upgrade your project.`,
name: "bot_not_found_error"
});
const inputSecrets = secrets.map((secret) => {
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretName, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretValue || "", botKey);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secret.secretName,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secret.secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretReferences = getAllNestedSecretReferences(secret.secretValue || "");
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretComment || "", botKey);
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secret.secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
return {
type: secret.type,
@@ -1065,10 +1080,22 @@ export const updateManySecretsRawFnFactory = ({
throw new BadRequestError({ message: "New secret name cannot be empty" });
}
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretName, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretValue || "", botKey);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secret.secretName,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secret.secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretReferences = getAllNestedSecretReferences(secret.secretValue || "");
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secret.secretComment || "", botKey);
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secret.secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
return {
type: secret.type,
@@ -1151,28 +1178,31 @@ export const decryptSecretWithBot = (
>,
key: string
) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
const secretValue = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
let secretComment = "";
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
secretComment = decryptSymmetric128BitHexKeyUTF8({
secretComment = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key
key,
keySize: SymmetricKeySize.Bits128
});
}

View File

@@ -18,8 +18,7 @@ import { TSnapshotDALFactory } from "@app/ee/services/secret-snapshot/snapshot-d
import { TSnapshotSecretV2DALFactory } from "@app/ee/services/secret-snapshot/snapshot-secret-v2-dal";
import { KeyStorePrefixes, KeyStoreTtls, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { decryptSymmetric128BitHexKeyUTF8 } from "@app/lib/crypto";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto, SymmetricKeySize } from "@app/lib/crypto/cryptography";
import { daysToMillisecond, secondsToMillis } from "@app/lib/dates";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { getTimeDifferenceInSeconds, groupBy, isSamePath, unique } from "@app/lib/fn";
@@ -504,18 +503,20 @@ export const secretQueueFactory = ({
const secrets = await secretDAL.findByFolderId(dto.folderId);
await Promise.allSettled(
secrets.map(async (secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: dto.key
key: dto.key,
keySize: SymmetricKeySize.Bits128
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
const secretValue = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: dto.key
key: dto.key,
keySize: SymmetricKeySize.Bits128
});
const expandedSecretValue = await expandSecretReferences({
environment: dto.environment,
@@ -527,11 +528,12 @@ export const secretQueueFactory = ({
content[secretKey] = { value: expandedSecretValue || "" };
if (secret.secretCommentCiphertext && secret.secretCommentIV && secret.secretCommentTag) {
const commentValue = decryptSymmetric128BitHexKeyUTF8({
const commentValue = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretCommentCiphertext,
iv: secret.secretCommentIV,
tag: secret.secretCommentTag,
key: dto.key
key: dto.key,
keySize: SymmetricKeySize.Bits128
});
content[secretKey].comment = commentValue;
}
@@ -952,11 +954,12 @@ export const secretQueueFactory = ({
integrationAuth.awsAssumeIamRoleArnIV &&
integrationAuth.awsAssumeIamRoleArnCipherText
) {
awsAssumeRoleArn = decryptSymmetric128BitHexKeyUTF8({
awsAssumeRoleArn = crypto.encryption().decryptSymmetric({
ciphertext: integrationAuth.awsAssumeIamRoleArnCipherText,
iv: integrationAuth.awsAssumeIamRoleArnIV,
tag: integrationAuth.awsAssumeIamRoleArnTag,
key: botKey as string
key: botKey as string,
keySize: SymmetricKeySize.Bits128
});
}
@@ -1236,7 +1239,9 @@ export const secretQueueFactory = ({
},
tx
);
const { iv, tag, ciphertext, encoding, algorithm } = infisicalSymmetricEncypt(ghostUser.keys.plainPrivateKey);
const { iv, tag, ciphertext, encoding, algorithm } = crypto
.encryption()
.encryptWithRootEncryptionKey(ghostUser.keys.plainPrivateKey);
await projectBotDAL.updateById(
bot.id,
{
@@ -1267,27 +1272,31 @@ export const secretQueueFactory = ({
secretId: string;
references: { environment: string; secretPath: string; secretKey: string }[];
}[] = [];
await secretV2BridgeDAL.batchInsert(
projectV1Secrets.map((el) => {
const key = decryptSymmetric128BitHexKeyUTF8({
const key = crypto.encryption().decryptSymmetric({
ciphertext: el.secretKeyCiphertext,
iv: el.secretKeyIV,
tag: el.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const value = decryptSymmetric128BitHexKeyUTF8({
const value = crypto.encryption().decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const comment =
el.secretCommentCiphertext && el.secretCommentTag && el.secretCommentIV
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.secretCommentCiphertext,
iv: el.secretCommentIV,
tag: el.secretCommentTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: "";
const encryptedValue = secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
@@ -1325,6 +1334,7 @@ export const secretQueueFactory = ({
const projectV3SecretVersionsGroupById: Record<string, TSecretVersionsV2> = {};
const projectV3SecretVersionTags: { secret_versions_v2Id: string; secret_tagsId: string }[] = [];
const projectV3SnapshotSecrets: Omit<TSecretSnapshotSecretsV2, "id">[] = [];
snapshots.forEach(({ secretVersions = [], ...snapshot }) => {
secretVersions.forEach((el) => {
projectV3SnapshotSecrets.push({
@@ -1336,25 +1346,28 @@ export const secretQueueFactory = ({
});
if (projectV3SecretVersionsGroupById[el.id]) return;
const key = decryptSymmetric128BitHexKeyUTF8({
const key = crypto.encryption().decryptSymmetric({
ciphertext: el.secretKeyCiphertext,
iv: el.secretKeyIV,
tag: el.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const value = decryptSymmetric128BitHexKeyUTF8({
const value = crypto.encryption().decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const comment =
el.secretCommentCiphertext && el.secretCommentTag && el.secretCommentIV
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.secretCommentCiphertext,
iv: el.secretCommentIV,
tag: el.secretCommentTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: "";
const encryptedValue = secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
@@ -1395,25 +1408,28 @@ export const secretQueueFactory = ({
);
Object.values(latestSecretVersionByFolder).forEach((el) => {
if (projectV3SecretVersionsGroupById[el.id]) return;
const key = decryptSymmetric128BitHexKeyUTF8({
const key = crypto.encryption().decryptSymmetric({
ciphertext: el.secretKeyCiphertext,
iv: el.secretKeyIV,
tag: el.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const value = decryptSymmetric128BitHexKeyUTF8({
const value = crypto.encryption().decryptSymmetric({
ciphertext: el.secretValueCiphertext,
iv: el.secretValueIV,
tag: el.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const comment =
el.secretCommentCiphertext && el.secretCommentTag && el.secretCommentIV
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.secretCommentCiphertext,
iv: el.secretCommentIV,
tag: el.secretCommentTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: "";
const encryptedValue = secretManagerEncryptor({ plainText: Buffer.from(value) }).cipherTextBlob;
@@ -1475,42 +1491,47 @@ export const secretQueueFactory = ({
* */
// eslint-disable-next-line no-await-in-loop
const projectV1IntegrationAuths = await integrationAuthDAL.find({ projectId }, { tx });
await integrationAuthDAL.upsert(
projectV1IntegrationAuths.map((el) => {
const accessToken =
el.accessIV && el.accessTag && el.accessCiphertext
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.accessCiphertext,
iv: el.accessIV,
tag: el.accessTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: undefined;
const accessId =
el.accessIdIV && el.accessIdTag && el.accessIdCiphertext
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.accessIdCiphertext,
iv: el.accessIdIV,
tag: el.accessIdTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: undefined;
const refreshToken =
el.refreshIV && el.refreshTag && el.refreshCiphertext
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.refreshCiphertext,
iv: el.refreshIV,
tag: el.refreshTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: undefined;
const awsAssumeRoleArn =
el.awsAssumeIamRoleArnCipherText && el.awsAssumeIamRoleArnIV && el.awsAssumeIamRoleArnTag
? decryptSymmetric128BitHexKeyUTF8({
? crypto.encryption().decryptSymmetric({
ciphertext: el.awsAssumeIamRoleArnCipherText,
iv: el.awsAssumeIamRoleArnIV,
tag: el.awsAssumeIamRoleArnTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
: undefined;

View File

@@ -29,11 +29,8 @@ import { TSecretApprovalRequestSecretDALFactory } from "@app/ee/services/secret-
import { TSecretApprovalRequestServiceFactory } from "@app/ee/services/secret-approval-request/secret-approval-request-service";
import { TSecretSnapshotServiceFactory } from "@app/ee/services/secret-snapshot/secret-snapshot-service";
import { getConfig } from "@app/lib/config/env";
import {
buildSecretBlindIndexFromName,
decryptSymmetric128BitHexKeyUTF8,
encryptSymmetric128BitHexKeyUTF8
} from "@app/lib/crypto";
import { buildSecretBlindIndexFromName, SymmetricKeySize } from "@app/lib/crypto";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { groupBy, pick } from "@app/lib/fn";
import { logger } from "@app/lib/logger";
@@ -162,11 +159,12 @@ export const secretServiceFactory = ({
return (el: { ciphertext?: string; iv: string; tag: string }) =>
projectBot?.botKey
? getAllNestedSecretReferences(
decryptSymmetric128BitHexKeyUTF8({
crypto.encryption().decryptSymmetric({
ciphertext: el.ciphertext || "",
iv: el.iv,
tag: el.tag,
key: projectBot.botKey
key: projectBot.botKey,
keySize: SymmetricKeySize.Bits128
})
)
: undefined;
@@ -1705,9 +1703,22 @@ export const secretServiceFactory = ({
message: `Project bot for project with ID '${projectId}' not found. Please upgrade your project.`,
name: "bot_not_found_error"
});
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretName, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretName,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
if (policy) {
const approval = await secretApprovalRequestService.generateSecretApprovalRequest({
policy,
@@ -1873,9 +1884,22 @@ export const secretServiceFactory = ({
name: "bot_not_found_error"
});
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(newSecretName || secretName, botKey);
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: newSecretName || secretName,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
if (policy) {
const approval = await secretApprovalRequestService.generateSecretApprovalRequest({
@@ -2119,11 +2143,24 @@ export const secretServiceFactory = ({
message: `Project bot for project with ID '${projectId}' not found. Please upgrade your project.`,
name: "bot_not_found_error"
});
const sanitizedSecrets = inputSecrets.map(
({ secretComment, secretKey, metadata, tagIds, secretValue, skipMultilineEncoding }) => {
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(secretKey, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretKey,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
return {
secretName: secretKey,
skipMultilineEncoding,
@@ -2263,6 +2300,7 @@ export const secretServiceFactory = ({
message: `Project bot for project with ID '${projectId}' not found. Please upgrade your project.`,
name: "bot_not_found_error"
});
const sanitizedSecrets = inputSecrets.map(
({
secretComment,
@@ -2274,9 +2312,21 @@ export const secretServiceFactory = ({
secretReminderNote,
secretReminderRepeatDays
}) => {
const secretKeyEncrypted = encryptSymmetric128BitHexKeyUTF8(newSecretName || secretKey, botKey);
const secretValueEncrypted = encryptSymmetric128BitHexKeyUTF8(secretValue || "", botKey);
const secretCommentEncrypted = encryptSymmetric128BitHexKeyUTF8(secretComment || "", botKey);
const secretKeyEncrypted = crypto.encryption().encryptSymmetric({
plaintext: newSecretName || secretKey,
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretValue || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretCommentEncrypted = crypto.encryption().encryptSymmetric({
plaintext: secretComment || "",
key: botKey,
keySize: SymmetricKeySize.Bits128
});
return {
secretName: secretKey,
newSecretName,
@@ -2486,12 +2536,14 @@ export const secretServiceFactory = ({
limit,
sort: [["createdAt", "desc"]]
});
return secretVersions.map((el) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
const secretValueHidden = !hasSecretReadValueOrDescribePermission(
@@ -2811,11 +2863,12 @@ export const secretServiceFactory = ({
secrets.map(({ id, secretValueCiphertext, secretValueIV, secretValueTag }) => ({
secretId: id,
references: getAllNestedSecretReferences(
decryptSymmetric128BitHexKeyUTF8({
crypto.encryption().decryptSymmetric({
ciphertext: secretValueCiphertext,
iv: secretValueIV,
tag: secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
)
})),
@@ -2916,11 +2969,12 @@ export const secretServiceFactory = ({
const destinationActions = [ProjectPermissionSecretActions.Create, ProjectPermissionSecretActions.Edit] as const;
const decryptedSourceSecrets = sourceSecrets.map((secret) => {
const secretKey = decryptSymmetric128BitHexKeyUTF8({
const secretKey = crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
});
for (const destinationAction of destinationActions) {
@@ -2956,11 +3010,12 @@ export const secretServiceFactory = ({
return {
...secret,
secretKey,
secretValue: decryptSymmetric128BitHexKeyUTF8({
secretValue: crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
};
});
@@ -2981,17 +3036,19 @@ export const secretServiceFactory = ({
const decryptedDestinationSecrets = destinationSecretsFromDB.map((secret) => {
return {
...secret,
secretKey: decryptSymmetric128BitHexKeyUTF8({
secretKey: crypto.encryption().decryptSymmetric({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
}),
secretValue: decryptSymmetric128BitHexKeyUTF8({
secretValue: crypto.encryption().decryptSymmetric({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key: botKey
key: botKey,
keySize: SymmetricKeySize.Bits128
})
};
});

View File

@@ -1,7 +1,4 @@
import crypto from "node:crypto";
import { ForbiddenError, subject } from "@casl/ability";
import bcrypt from "bcrypt";
import { ActionProjectType } from "@app/db/schemas";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
@@ -11,6 +8,7 @@ import {
ProjectPermissionSub
} from "@app/ee/services/permission/project-permission";
import { getConfig } from "@app/lib/config/env";
import { crypto } from "@app/lib/crypto/cryptography";
import { ForbiddenRequestError, NotFoundError, UnauthorizedError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -89,7 +87,7 @@ export const serviceTokenServiceFactory = ({
throw new NotFoundError({ message: `One or more selected environments not found` });
const secret = crypto.randomBytes(16).toString("hex");
const secretHash = await bcrypt.hash(secret, appCfg.SALT_ROUNDS);
const secretHash = await crypto.hashing().createHash(secret, appCfg.SALT_ROUNDS);
let expiresAt: Date | null = null;
if (expiresIn) {
expiresAt = new Date();
@@ -182,7 +180,7 @@ export const serviceTokenServiceFactory = ({
throw new ForbiddenRequestError({ message: "Service token has expired" });
}
const isMatch = await bcrypt.compare(tokenSecret, serviceToken.secretHash);
const isMatch = await crypto.hashing().compareHash(tokenSecret, serviceToken.secretHash);
if (!isMatch) throw new UnauthorizedError({ message: "Invalid service token" });
await accessTokenQueue.updateServiceTokenStatus(serviceToken.id);

View File

@@ -1,4 +1,3 @@
import bcrypt from "bcrypt";
import { CronJob } from "cron";
import jwt from "jsonwebtoken";
@@ -6,7 +5,7 @@ import { IdentityAuthMethod, OrgMembershipRole, TSuperAdmin, TSuperAdminUpdate }
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { PgSqlLock, TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { generateUserSrpKeys, getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
@@ -157,6 +156,7 @@ export const superAdminServiceFactory = ({
const newCfg = await serverCfgDAL.create({
// @ts-expect-error id is kept as fixed for idempotence and to avoid race condition
id: ADMIN_CONFIG_DB_UUID,
fipsEnabled: crypto.isFipsModeEnabled(),
initialized: false,
allowSignUp: true,
defaultAuthOrgId: null
@@ -435,8 +435,10 @@ export const superAdminServiceFactory = ({
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
});
const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
const { iv, tag, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
const hashedPassword = await crypto.hashing().createHash(password, appCfg.SALT_ROUNDS);
const { iv, tag, ciphertext, encoding } = crypto.encryption().encryptWithRootEncryptionKey(privateKey);
const userInfo = await userDAL.transaction(async (tx) => {
const newUser = await userDAL.create(
{
@@ -522,7 +524,7 @@ export const superAdminServiceFactory = ({
},
tx
);
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(password);
const { tag, encoding, ciphertext, iv } = crypto.encryption().encryptWithRootEncryptionKey(password);
const encKeys = await generateUserSrpKeys(sanitizedEmail, password);
const userEnc = await userDAL.createUserEncryption(

View File

@@ -1,4 +1,3 @@
import { createHash, randomUUID } from "crypto";
import { PostHog } from "posthog-node";
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
@@ -6,6 +5,7 @@ import { InstanceType } from "@app/ee/services/license/license-types";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto/cryptography";
import { logger } from "@app/lib/logger";
import { PostHogEventTypes, TPostHogEvent, TSecretModifiedEvent } from "./telemetry-types";
@@ -42,7 +42,7 @@ export type TTelemetryServiceFactoryDep = {
const getBucketForDistinctId = (distinctId: string): string => {
// Use SHA-256 hash for consistent distribution
const hash = createHash("sha256").update(distinctId).digest("hex");
const hash = crypto.rawCrypto.createHash("sha256").update(distinctId).digest("hex");
// Take first 8 characters and convert to number for better distribution
const hashNumber = parseInt(hash.substring(0, 8), 16);
@@ -53,7 +53,7 @@ const getBucketForDistinctId = (distinctId: string): string => {
export const createTelemetryEventKey = (event: string, distinctId: string): string => {
const bucketId = getBucketForDistinctId(distinctId);
return `telemetry-event-${event}-${bucketId}-${distinctId}-${randomUUID()}`;
return `telemetry-event-${event}-${bucketId}-${distinctId}-${crypto.rawCrypto.randomUUID()}`;
};
export const telemetryServiceFactory = ({ keyStore, licenseService }: TTelemetryServiceFactoryDep) => {

View File

@@ -1,3 +1,3 @@
import crypto from "node:crypto";
import { crypto } from "@app/lib/crypto/cryptography";
export const generateRecoveryCode = () => String(crypto.randomInt(10 ** 7, 10 ** 8 - 1));

View File

@@ -3,7 +3,7 @@ import { ForbiddenError } from "@casl/ability";
import { SecretKeyEncoding } from "@app/db/schemas";
import { OrgPermissionActions, OrgPermissionSubjects } from "@app/ee/services/permission/org-permission";
import { TPermissionServiceFactory } from "@app/ee/services/permission/permission-service-types";
import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { crypto } from "@app/lib/crypto/cryptography";
import { BadRequestError, ForbiddenRequestError, NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
@@ -217,7 +217,8 @@ export const userServiceFactory = ({
if (!user?.serverEncryptedPrivateKey || !user.serverEncryptedPrivateKeyIV || !user.serverEncryptedPrivateKeyTag) {
throw new NotFoundError({ message: `Private key for user with ID '${userId}' not found` });
}
const privateKey = infisicalSymmetricDecrypt({
const privateKey = crypto.encryption().decryptWithRootEncryptionKey({
ciphertext: user.serverEncryptedPrivateKey,
tag: user.serverEncryptedPrivateKeyTag,
iv: user.serverEncryptedPrivateKeyIV,

View File

@@ -1,11 +1,10 @@
import crypto from "node:crypto";
import { AxiosError } from "axios";
import picomatch from "picomatch";
import { TWebhooks } from "@app/db/schemas";
import { EventType, TAuditLogServiceFactory, WebhookTriggeredEvent } from "@app/ee/services/audit-log/audit-log-types";
import { request } from "@app/lib/config/request";
import { crypto } from "@app/lib/crypto/cryptography";
import { NotFoundError } from "@app/lib/errors";
import { logger } from "@app/lib/logger";
import { ActorType } from "@app/services/auth/auth-type";
@@ -41,9 +40,8 @@ export const triggerWebhookRequest = async (
const headers: Record<string, string> = {};
const payload = { ...data, timestamp: Date.now() };
const { secretKey, url } = decryptWebhookDetails(webhook, decryptor);
if (secretKey) {
const webhookSign = crypto.createHmac("sha256", secretKey).update(JSON.stringify(payload)).digest("hex");
const webhookSign = crypto.rawCrypto.createHmac("sha256", secretKey).update(JSON.stringify(payload)).digest("hex");
headers["x-infisical-signature"] = `t=${payload.timestamp};${webhookSign}`;
}

Some files were not shown because too many files have changed in this diff Show More