mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-10-15 19:16:22 +00:00
229 lines
5.7 KiB
TypeScript
229 lines
5.7 KiB
TypeScript
import * as Sentry from '@sentry/node';
|
|
import {
|
|
Bot,
|
|
BotKey,
|
|
Secret,
|
|
ISecret,
|
|
IUser
|
|
} from '../models';
|
|
import {
|
|
generateKeyPair,
|
|
encryptSymmetric,
|
|
decryptSymmetric,
|
|
decryptAsymmetric
|
|
} from '../utils/crypto';
|
|
import { ENCRYPTION_KEY } from '../config';
|
|
import { SECRET_SHARED } from '../variables';
|
|
|
|
/**
|
|
* Create an inactive bot with name [name] for workspace with id [workspaceId]
|
|
* @param {Object} obj
|
|
* @param {String} obj.name - name of bot
|
|
* @param {String} obj.workspaceId - id of workspace that bot belongs to
|
|
*/
|
|
const createBot = async ({
|
|
name,
|
|
workspaceId,
|
|
}: {
|
|
name: string;
|
|
workspaceId: string;
|
|
}) => {
|
|
let bot;
|
|
try {
|
|
const { publicKey, privateKey } = generateKeyPair();
|
|
const { ciphertext, iv, tag } = encryptSymmetric({
|
|
plaintext: privateKey,
|
|
key: ENCRYPTION_KEY
|
|
});
|
|
|
|
bot = await new Bot({
|
|
name,
|
|
workspace: workspaceId,
|
|
isActive: false,
|
|
publicKey,
|
|
encryptedPrivateKey: ciphertext,
|
|
iv,
|
|
tag
|
|
}).save();
|
|
} catch (err) {
|
|
Sentry.setUser(null);
|
|
Sentry.captureException(err);
|
|
throw new Error('Failed to create bot');
|
|
}
|
|
|
|
return bot;
|
|
}
|
|
|
|
/**
|
|
* Return decrypted secrets for workspace with id [workspaceId]
|
|
* and [environment] using bot
|
|
* @param {Object} obj
|
|
* @param {String} obj.workspaceId - id of workspace
|
|
* @param {String} obj.environment - environment
|
|
*/
|
|
const getSecretsHelper = async ({
|
|
workspaceId,
|
|
environment
|
|
}: {
|
|
workspaceId: string;
|
|
environment: string;
|
|
}) => {
|
|
const content = {} as any;
|
|
try {
|
|
const key = await getKey({ workspaceId });
|
|
const secrets = await Secret.find({
|
|
workspace: workspaceId,
|
|
environment,
|
|
type: SECRET_SHARED
|
|
});
|
|
|
|
secrets.forEach((secret: ISecret) => {
|
|
const secretKey = decryptSymmetric({
|
|
ciphertext: secret.secretKeyCiphertext,
|
|
iv: secret.secretKeyIV,
|
|
tag: secret.secretKeyTag,
|
|
key
|
|
});
|
|
|
|
const secretValue = decryptSymmetric({
|
|
ciphertext: secret.secretValueCiphertext,
|
|
iv: secret.secretValueIV,
|
|
tag: secret.secretValueTag,
|
|
key
|
|
});
|
|
|
|
content[secretKey] = secretValue;
|
|
});
|
|
} catch (err) {
|
|
Sentry.setUser(null);
|
|
Sentry.captureException(err);
|
|
throw new Error('Failed to get secrets');
|
|
}
|
|
|
|
return content;
|
|
}
|
|
|
|
/**
|
|
* Return bot's copy of the workspace key for workspace
|
|
* with id [workspaceId]
|
|
* @param {Object} obj
|
|
* @param {String} obj.workspaceId - id of workspace
|
|
* @returns {String} key - decrypted workspace key
|
|
*/
|
|
const getKey = async ({ workspaceId }: { workspaceId: string }) => {
|
|
let key;
|
|
try {
|
|
const botKey = await BotKey.findOne({
|
|
workspace: workspaceId
|
|
}).populate<{ sender: IUser }>('sender', 'publicKey');
|
|
|
|
if (!botKey) throw new Error('Failed to find bot key');
|
|
|
|
const bot = await Bot.findOne({
|
|
workspace: workspaceId
|
|
}).select('+encryptedPrivateKey +iv +tag');
|
|
|
|
if (!bot) throw new Error('Failed to find bot');
|
|
if (!bot.isActive) throw new Error('Bot is not active');
|
|
|
|
const privateKeyBot = decryptSymmetric({
|
|
ciphertext: bot.encryptedPrivateKey,
|
|
iv: bot.iv,
|
|
tag: bot.tag,
|
|
key: ENCRYPTION_KEY
|
|
});
|
|
|
|
key = decryptAsymmetric({
|
|
ciphertext: botKey.encryptedKey,
|
|
nonce: botKey.nonce,
|
|
publicKey: botKey.sender.publicKey as string,
|
|
privateKey: privateKeyBot
|
|
});
|
|
} catch (err) {
|
|
Sentry.setUser(null);
|
|
Sentry.captureException(err);
|
|
throw new Error('Failed to get workspace key');
|
|
}
|
|
|
|
return key;
|
|
}
|
|
|
|
/**
|
|
* Return symmetrically encrypted [plaintext] using the
|
|
* key for workspace with id [workspaceId]
|
|
* @param {Object} obj1
|
|
* @param {String} obj1.workspaceId - id of workspace
|
|
* @param {String} obj1.plaintext - plaintext to encrypt
|
|
*/
|
|
const encryptSymmetricHelper = async ({
|
|
workspaceId,
|
|
plaintext
|
|
}: {
|
|
workspaceId: string;
|
|
plaintext: string;
|
|
}) => {
|
|
|
|
try {
|
|
const key = await getKey({ workspaceId });
|
|
const { ciphertext, iv, tag } = encryptSymmetric({
|
|
plaintext,
|
|
key
|
|
});
|
|
|
|
return ({
|
|
ciphertext,
|
|
iv,
|
|
tag
|
|
});
|
|
} catch (err) {
|
|
Sentry.setUser(null);
|
|
Sentry.captureException(err);
|
|
throw new Error('Failed to perform symmetric encryption with bot');
|
|
}
|
|
}
|
|
/**
|
|
* Return symmetrically decrypted [ciphertext] using the
|
|
* key for workspace with id [workspaceId]
|
|
* @param {Object} obj
|
|
* @param {String} obj.workspaceId - id of workspace
|
|
* @param {String} obj.ciphertext - ciphertext to decrypt
|
|
* @param {String} obj.iv - iv
|
|
* @param {String} obj.tag - tag
|
|
*/
|
|
const decryptSymmetricHelper = async ({
|
|
workspaceId,
|
|
ciphertext,
|
|
iv,
|
|
tag
|
|
}: {
|
|
workspaceId: string;
|
|
ciphertext: string;
|
|
iv: string;
|
|
tag: string;
|
|
}) => {
|
|
let plaintext;
|
|
try {
|
|
const key = await getKey({ workspaceId });
|
|
const plaintext = decryptSymmetric({
|
|
ciphertext,
|
|
iv,
|
|
tag,
|
|
key
|
|
});
|
|
|
|
return plaintext;
|
|
} catch (err) {
|
|
Sentry.setUser(null);
|
|
Sentry.captureException(err);
|
|
throw new Error('Failed to perform symmetric decryption with bot');
|
|
}
|
|
|
|
return plaintext;
|
|
}
|
|
|
|
export {
|
|
createBot,
|
|
getSecretsHelper,
|
|
encryptSymmetricHelper,
|
|
decryptSymmetricHelper
|
|
} |