mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-08 23:18:05 -05:00
feat: fips inside support (checkpoint)
This commit is contained in:
@@ -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"]
|
||||
|
||||
@@ -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}`
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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,
|
||||
|
||||
15
backend/package-lock.json
generated
15
backend/package-lock.json
generated
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
2
backend/src/@types/fastify-zod.d.ts
vendored
2
backend/src/@types/fastify-zod.d.ts
vendored
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
23
backend/src/db/migrations/20250705074703_fips-mode.ts
Normal file
23
backend/src/db/migrations/20250705074703_fips-mode.ts
Normal 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");
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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>;
|
||||
|
||||
@@ -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
|
||||
})
|
||||
: "";
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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]];
|
||||
}
|
||||
|
||||
|
||||
@@ -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]];
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
|
||||
@@ -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]];
|
||||
}
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
}))
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
})
|
||||
: ""
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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()]);
|
||||
|
||||
630
backend/src/lib/crypto/cryptography.ts
Normal file
630
backend/src/lib/crypto/cryptography.ts
Normal 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 };
|
||||
@@ -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");
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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"
|
||||
});
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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" });
|
||||
|
||||
@@ -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 || "",
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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!,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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}` });
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 };
|
||||
};
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user