Add non-e2ee option for getSecret, getSecrets, start createSecret

This commit is contained in:
Tuan Dang
2023-06-09 21:20:12 +01:00
parent 00fae0023a
commit 0187d3012b
8 changed files with 202 additions and 35 deletions

View File

@@ -6,8 +6,6 @@ import {
EventService
} from '../../services';
import { eventPushSecrets } from '../../events';
import { getAuthDataPayloadIdObj } from '../../utils/auth';
import { BadRequestError } from '../../utils/errors';
/**
* Get secrets for workspace with id [workspaceId] and environment
@@ -68,9 +66,11 @@ export const createSecret = async (req: Request, res: Response) => {
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValue,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretComment,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
@@ -85,14 +85,14 @@ export const createSecret = async (req: Request, res: Response) => {
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValue,
secretValueCiphertext,
secretValueIV,
secretValueTag,
...((secretCommentCiphertext && secretCommentIV && secretCommentTag) ? {
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
} : {})
secretComment,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag
});
await EventService.handleEvent({

View File

@@ -86,6 +86,18 @@ export const createBot = async ({
});
};
/**
* Return whether or not workspace with id [workspaceId] is end-to-end encrypted
* @param {Types.ObjectId} workspaceId - id of workspace to check
*/
export const getIsWorkspaceE2EEHelper = async (workspaceId: Types.ObjectId) => {
const botKey = await BotKey.exists({
workspace: workspaceId
});
return botKey ? false : true;
}
/**
* Return decrypted secrets for workspace with id [workspaceId]
* and [environment] using bot
@@ -101,7 +113,7 @@ export const getSecretsBotHelper = async ({
environment: string;
}) => {
const content = {} as any;
const key = await getKey({ workspaceId: workspaceId.toString() });
const key = await getKey({ workspaceId: workspaceId });
const secrets = await Secret.find({
workspace: workspaceId,
environment,
@@ -136,7 +148,7 @@ export const getSecretsBotHelper = async ({
* @param {String} obj.workspaceId - id of workspace
* @returns {String} key - decrypted workspace key
*/
export const getKey = async ({ workspaceId }: { workspaceId: string }) => {
export const getKey = async ({ workspaceId }: { workspaceId: Types.ObjectId }) => {
const encryptionKey = await getEncryptionKey();
const rootEncryptionKey = await getRootEncryptionKey();
@@ -201,7 +213,7 @@ export const encryptSymmetricHelper = async ({
workspaceId: Types.ObjectId;
plaintext: string;
}) => {
const key = await getKey({ workspaceId: workspaceId.toString() });
const key = await getKey({ workspaceId: workspaceId });
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext,
key,
@@ -233,7 +245,7 @@ export const decryptSymmetricHelper = async ({
iv: string;
tag: string;
}) => {
const key = await getKey({ workspaceId: workspaceId.toString() });
const key = await getKey({ workspaceId: workspaceId });
const plaintext = decryptSymmetric128BitHexKeyUTF8({
ciphertext,
iv,

View File

@@ -6,7 +6,12 @@ import {
UpdateSecretParams,
DeleteSecretParams,
} from '../interfaces/services/SecretService';
import { Secret, ISecret, SecretBlindIndexData } from '../models';
import {
ISecret,
Secret,
SecretBlindIndexData,
BotKey
} from '../models';
import { SecretVersion } from '../ee/models';
import {
BadRequestError,
@@ -32,7 +37,7 @@ import {
decryptSymmetric128BitHexKeyUTF8,
} from '../utils/crypto';
import { getEncryptionKey, client, getRootEncryptionKey } from '../config';
import { TelemetryService } from '../services';
import { BotService, TelemetryService } from '../services';
import { EESecretService, EELogService } from '../ee/services';
import {
getAuthDataPayloadIdObj,
@@ -237,6 +242,23 @@ export const generateSecretBlindIndexHelper = async ({
});
};
// secretName,
// workspaceId,
// environment,
// type,
// authData,
// secretKeyCiphertext,
// secretKeyIV,
// secretKeyTag,
// secretValue,
// secretValueCiphertext,
// secretValueIV,
// secretValueTag,
// secretCommentCiphertext,
// secretCommentIV,
// secretCommentTag,
// folderId,
/**
* Create secret with name [secretName]
* @param {Object} obj
@@ -256,14 +278,17 @@ export const createSecretHelper = async ({
secretKeyCiphertext,
secretKeyIV,
secretKeyTag,
secretValue,
secretValueCiphertext,
secretValueIV,
secretValueTag,
secretComment,
secretCommentCiphertext,
secretCommentIV,
secretCommentTag,
folderId,
}: CreateSecretParams) => {
const secretBlindIndex = await generateSecretBlindIndexHelper({
secretName,
workspaceId: new Types.ObjectId(workspaceId),
@@ -298,6 +323,52 @@ export const createSecretHelper = async ({
});
}
// can generate secretKeyCiphertext etc. if not E2EE.
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
if (!isWorkspaceE2EE) {
// if workspace is not end-to-end encrypted, then decrypt
// secret and return it in plaintext
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId
});
if (secretName) {
const encryptedSecretKey = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretName,
key
});
secretKeyCiphertext = encryptedSecretKey.ciphertext;
secretKeyIV = encryptedSecretKey.iv;
secretKeyTag = encryptedSecretKey.tag;
}
if (secretValue) {
const encryptedSecretValue = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretValue,
key
});
secretValueCiphertext = encryptedSecretValue.ciphertext;
secretValueIV = encryptedSecretValue.iv;
secretValueTag = encryptedSecretValue.tag;
}
if (secretComment) {
const encryptedSecretComment = encryptSymmetric128BitHexKeyUTF8({
plaintext: secretComment,
key
});
secretCommentCiphertext = encryptedSecretComment.ciphertext;
secretCommentIV = encryptedSecretComment.iv;
secretCommentTag = encryptedSecretComment.tag;
}
}
// create secret
const secret = await new Secret({
version: 1,
@@ -410,7 +481,7 @@ export const getSecretsHelper = async ({
environment,
type: SECRET_PERSONAL,
...getAuthDataPayloadUserObj(authData),
});
}).lean();
// concat with shared secrets
secrets = secrets.concat(
@@ -421,7 +492,7 @@ export const getSecretsHelper = async ({
secretBlindIndex: {
$nin: secrets.map((secret) => secret.secretBlindIndex),
},
})
}).lean()
);
// (EE) create (audit) log
@@ -458,8 +529,45 @@ export const getSecretsHelper = async ({
},
});
}
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
return secrets;
if (!isWorkspaceE2EE) {
// if workspace is not end-to-end encrypted, then decrypt
// secret and return it in plaintext
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId
});
return secrets.map((secret) => {
const secretName = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretKeyCiphertext,
iv: secret.secretKeyIV,
tag: secret.secretKeyTag,
key
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
});
return ({
...secret,
secretName,
secretValue
});
});
}
return secrets.map((secret) => ({
...secret,
secretName: null,
secretValue: null
}));
};
/**
@@ -492,7 +600,7 @@ export const getSecretHelper = async ({
environment,
type: type ?? SECRET_PERSONAL,
...(type === SECRET_PERSONAL ? getAuthDataPayloadUserObj(authData) : {}),
});
}).lean();
if (!secret) {
// case: failed to find personal secret matching criteria
@@ -502,7 +610,7 @@ export const getSecretHelper = async ({
workspace: new Types.ObjectId(workspaceId),
environment,
type: SECRET_SHARED,
});
}).lean();
}
if (!secret) throw SecretNotFoundError();
@@ -541,8 +649,36 @@ export const getSecretHelper = async ({
},
});
}
const isWorkspaceE2EE = await BotService.getIsWorkspaceE2EE(workspaceId);
if (!isWorkspaceE2EE) {
// if workspace is not end-to-end encrypted, then decrypt
// secret and return it in plaintext
return secret;
const key = await BotService.getWorkspaceKeyWithBot({
workspaceId
});
const secretValue = decryptSymmetric128BitHexKeyUTF8({
ciphertext: secret.secretValueCiphertext,
iv: secret.secretValueIV,
tag: secret.secretValueTag,
key
});
return ({
...secret,
secretName,
secretValue
});
}
return ({
...secret,
secretName: null,
secretValue: null
});
};
/**

View File

@@ -8,12 +8,14 @@ export interface CreateSecretParams {
folderId?: string;
type: "shared" | "personal";
authData: AuthData;
secretKeyCiphertext: string;
secretKeyIV: string;
secretKeyTag: string;
secretValueCiphertext: string;
secretValueIV: string;
secretValueTag: string;
secretKeyCiphertext?: string;
secretKeyIV?: string;
secretKeyTag?: string;
secretValue?: string;
secretValueCiphertext?: string;
secretValueIV?: string;
secretValueTag?: string;
secretComment?: string;
secretCommentCiphertext?: string;
secretCommentIV?: string;
secretCommentTag?: string;

View File

@@ -48,12 +48,14 @@ router.post(
body('workspaceId').exists().isString().trim(),
body('environment').exists().isString().trim(),
body('type').exists().isIn([SECRET_SHARED, SECRET_PERSONAL]),
body('secretKeyCiphertext').exists().isString().trim(),
body('secretKeyIV').exists().isString().trim(),
body('secretKeyTag').exists().isString().trim(),
body('secretValueCiphertext').exists().isString().trim(),
body('secretValueIV').exists().isString().trim(),
body('secretValueTag').exists().isString().trim(),
body('secretKeyCiphertext').optional().isString().trim(),
body('secretKeyIV').optional().isString().trim(),
body('secretKeyTag').optional().isString().trim(),
body('secretValue').optional().isString().trim(),
body('secretValueCiphertext').optional().isString().trim(),
body('secretValueIV').optional().isString().trim(),
body('secretValueTag').optional().isString().trim(),
body('secretComment').optional().isString().trim(),
body('secretCommentCiphertext').optional().isString().trim(),
body('secretCommentIV').optional().isString().trim(),
body('secretCommentTag').optional().isString().trim(),

View File

@@ -2,13 +2,31 @@ import { Types } from 'mongoose';
import {
getSecretsBotHelper,
encryptSymmetricHelper,
decryptSymmetricHelper
decryptSymmetricHelper,
getKey,
getIsWorkspaceE2EEHelper
} from '../helpers/bot';
// rename the functions here
// refactor the interface situation here
/**
* Class to handle bot actions
*/
class BotService {
static async getIsWorkspaceE2EE(workspaceId: Types.ObjectId) {
return await getIsWorkspaceE2EEHelper(workspaceId);
}
static async getWorkspaceKeyWithBot({
workspaceId
}: {
workspaceId: Types.ObjectId;
}) {
return await getKey({
workspaceId
});
}
/**
* Return decrypted secrets for workspace with id [workspaceId] and

View File

@@ -1,7 +1,4 @@
import { Types } from 'mongoose';
import {
ISecret
} from '../models';
import {
CreateSecretParams,
GetSecretsParams,