Skip to content

Commit

Permalink
Merge pull request #1141 from akhilmhdh/fix/backward-enc-key
Browse files Browse the repository at this point in the history
feat: backward compatiable enc key in webhook
  • Loading branch information
maidul98 authored Oct 30, 2023
2 parents 46f03f3 + c6846f8 commit 5bdb6ad
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 21 deletions.
37 changes: 28 additions & 9 deletions backend/src/controllers/v1/webhookController.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
import { Request, Response } from "express";
import { Types } from "mongoose";
import { client, getRootEncryptionKey } from "../../config";
import { client, getEncryptionKey, getRootEncryptionKey } from "../../config";
import { Webhook } from "../../models";
import { getWebhookPayload, triggerWebhookRequest } from "../../services/WebhookService";
import { BadRequestError, ResourceNotFoundError } from "../../utils/errors";
import { EEAuditLogService } from "../../ee/services";
import { EventType } from "../../ee/models";
import { ALGORITHM_AES_256_GCM, ENCODING_SCHEME_BASE64 } from "../../variables";
import {
ALGORITHM_AES_256_GCM,
ENCODING_SCHEME_BASE64,
ENCODING_SCHEME_UTF8
} from "../../variables";
import { validateRequest } from "../../helpers/validation";
import * as reqValidator from "../../validation/webhooks";
import {
Expand All @@ -15,6 +19,7 @@ import {
getUserProjectPermissions
} from "../../ee/services/ProjectRoleService";
import { ForbiddenError } from "@casl/ability";
import { encryptSymmetric128BitHexKeyUTF8 } from "../../utils/crypto";

export const createWebhook = async (req: Request, res: Response) => {
const {
Expand All @@ -31,17 +36,31 @@ export const createWebhook = async (req: Request, res: Response) => {
workspace: workspaceId,
environment,
secretPath,
url: webhookUrl,
algorithm: ALGORITHM_AES_256_GCM,
keyEncoding: ENCODING_SCHEME_BASE64
url: webhookUrl
});

if (webhookSecretKey) {
const encryptionKey = await getEncryptionKey();
const rootEncryptionKey = await getRootEncryptionKey();
const { ciphertext, iv, tag } = client.encryptSymmetric(webhookSecretKey, rootEncryptionKey);
webhook.iv = iv;
webhook.tag = tag;
webhook.encryptedSecretKey = ciphertext;

if (rootEncryptionKey) {
const { ciphertext, iv, tag } = client.encryptSymmetric(webhookSecretKey, rootEncryptionKey);
webhook.iv = iv;
webhook.tag = tag;
webhook.encryptedSecretKey = ciphertext;
webhook.algorithm = ALGORITHM_AES_256_GCM;
webhook.keyEncoding = ENCODING_SCHEME_BASE64;
} else if (encryptionKey) {
const { ciphertext, iv, tag } = encryptSymmetric128BitHexKeyUTF8({
plaintext: webhookSecretKey,
key: encryptionKey
});
webhook.iv = iv;
webhook.tag = tag;
webhook.encryptedSecretKey = ciphertext;
webhook.algorithm = ALGORITHM_AES_256_GCM;
webhook.keyEncoding = ENCODING_SCHEME_UTF8;
}
}

await webhook.save();
Expand Down
4 changes: 1 addition & 3 deletions backend/src/models/webhooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,11 @@ const WebhookSchema = new Schema<IWebhook>(
// the encryption algorithm used
type: String,
enum: [ALGORITHM_AES_256_GCM],
required: true,
select: false
},
keyEncoding: {
type: String,
enum: [ENCODING_SCHEME_UTF8, ENCODING_SCHEME_BASE64],
required: true,
select: false
}
},
Expand All @@ -80,4 +78,4 @@ const WebhookSchema = new Schema<IWebhook>(
}
);

export const Webhook = model<IWebhook>("Webhook", WebhookSchema);
export const Webhook = model<IWebhook>("Webhook", WebhookSchema);
34 changes: 25 additions & 9 deletions backend/src/services/WebhookService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,42 @@ import axios from "axios";
import crypto from "crypto";
import { Types } from "mongoose";
import picomatch from "picomatch";
import { client, getRootEncryptionKey } from "../config";
import { client, getEncryptionKey, getRootEncryptionKey } from "../config";
import { IWebhook, Webhook } from "../models";
import { decryptSymmetric128BitHexKeyUTF8 } from "../utils/crypto";
import { ENCODING_SCHEME_BASE64, ENCODING_SCHEME_UTF8 } from "../variables";

export const triggerWebhookRequest = async (
{ url, encryptedSecretKey, iv, tag }: IWebhook,
{ url, encryptedSecretKey, iv, tag, keyEncoding }: IWebhook,
payload: Record<string, unknown>
) => {
const headers: Record<string, string> = {};
payload["timestamp"] = Date.now();

if (encryptedSecretKey) {
const encryptionKey = await getEncryptionKey();
const rootEncryptionKey = await getRootEncryptionKey();
const secretKey = client.decryptSymmetric(encryptedSecretKey, rootEncryptionKey, iv, tag);
const webhookSign = crypto
.createHmac("sha256", secretKey)
.update(JSON.stringify(payload))
.digest("hex");
headers["x-infisical-signature"] = `t=${payload["timestamp"]};${webhookSign}`;
let secretKey;
if (rootEncryptionKey && keyEncoding === ENCODING_SCHEME_BASE64) {
// case: encoding scheme is base64
secretKey = client.decryptSymmetric(encryptedSecretKey, rootEncryptionKey, iv, tag);
} else if (encryptionKey && keyEncoding === ENCODING_SCHEME_UTF8) {
// case: encoding scheme is utf8
secretKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: encryptedSecretKey,
iv: iv,
tag: tag,
key: encryptionKey
});
}
if (secretKey) {
const webhookSign = crypto
.createHmac("sha256", secretKey)
.update(JSON.stringify(payload))
.digest("hex");
headers["x-infisical-signature"] = `t=${payload["timestamp"]};${webhookSign}`;
}
}

const req = await axios.post(url, payload, { headers });
return req;
};
Expand Down

0 comments on commit 5bdb6ad

Please sign in to comment.