From c645a9bc21311db6fdc5bdb98a87a9d05111122d Mon Sep 17 00:00:00 2001
From: mike12345567 <me@michaeldrury.co.uk>
Date: Mon, 16 Jan 2023 18:15:43 +0000
Subject: [PATCH] Some type updates and an improvement to encryption to allow
 selecting the secret from an option list.

---
 packages/backend-core/src/environment.ts      |  1 +
 .../backend-core/src/security/encryption.ts   | 39 ++++++++++++++++---
 .../documents/global/environmentVariables.ts  |  4 ++
 3 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/packages/backend-core/src/environment.ts b/packages/backend-core/src/environment.ts
index 91556ddcd6..d742ca1cc9 100644
--- a/packages/backend-core/src/environment.ts
+++ b/packages/backend-core/src/environment.ts
@@ -37,6 +37,7 @@ const environment = {
   },
   JS_BCRYPT: process.env.JS_BCRYPT,
   JWT_SECRET: process.env.JWT_SECRET,
+  ENCRYPTION_KEY: process.env.ENCRYPTION_KEY,
   COUCH_DB_URL: process.env.COUCH_DB_URL || "http://localhost:4005",
   COUCH_DB_USERNAME: process.env.COUCH_DB_USER,
   COUCH_DB_PASSWORD: process.env.COUCH_DB_PASSWORD,
diff --git a/packages/backend-core/src/security/encryption.ts b/packages/backend-core/src/security/encryption.ts
index c673263202..d0707cb850 100644
--- a/packages/backend-core/src/security/encryption.ts
+++ b/packages/backend-core/src/security/encryption.ts
@@ -2,19 +2,45 @@ import crypto from "crypto"
 import env from "../environment"
 
 const ALGO = "aes-256-ctr"
-const SECRET = env.JWT_SECRET
 const SEPARATOR = "-"
 const ITERATIONS = 10000
 const RANDOM_BYTES = 16
 const STRETCH_LENGTH = 32
 
+export enum SecretOption {
+  JWT = "jwt",
+  ENCRYPTION = "encryption",
+}
+
+function getSecret(secretOption: SecretOption): string {
+  let secret, secretName
+  switch (secretOption) {
+    case SecretOption.ENCRYPTION:
+      secret = env.ENCRYPTION_KEY
+      secretName = "ENCRYPTION_KEY"
+      break
+    case SecretOption.JWT:
+    default:
+      secret = env.JWT_SECRET
+      secretName = "JWT_SECRET"
+      break
+  }
+  if (!secret) {
+    throw new Error(`Secret "${secretName}" has not been set in environment.`)
+  }
+  return secret
+}
+
 function stretchString(string: string, salt: Buffer) {
   return crypto.pbkdf2Sync(string, salt, ITERATIONS, STRETCH_LENGTH, "sha512")
 }
 
-export function encrypt(input: string, secret: string | undefined = SECRET) {
+export function encrypt(
+  input: string,
+  secretOption: SecretOption = SecretOption.JWT
+) {
   const salt = crypto.randomBytes(RANDOM_BYTES)
-  const stretched = stretchString(secret!, salt)
+  const stretched = stretchString(getSecret(secretOption), salt)
   const cipher = crypto.createCipheriv(ALGO, stretched, salt)
   const base = cipher.update(input)
   const final = cipher.final()
@@ -22,10 +48,13 @@ export function encrypt(input: string, secret: string | undefined = SECRET) {
   return `${salt.toString("hex")}${SEPARATOR}${encrypted}`
 }
 
-export function decrypt(input: string, secret: string | undefined = SECRET) {
+export function decrypt(
+  input: string,
+  secretOption: SecretOption = SecretOption.JWT
+) {
   const [salt, encrypted] = input.split(SEPARATOR)
   const saltBuffer = Buffer.from(salt, "hex")
-  const stretched = stretchString(secret!, saltBuffer)
+  const stretched = stretchString(getSecret(secretOption), saltBuffer)
   const decipher = crypto.createDecipheriv(ALGO, stretched, saltBuffer)
   const base = decipher.update(Buffer.from(encrypted, "hex"))
   const final = decipher.final()
diff --git a/packages/types/src/documents/global/environmentVariables.ts b/packages/types/src/documents/global/environmentVariables.ts
index 62bd1e5633..f7255d8583 100644
--- a/packages/types/src/documents/global/environmentVariables.ts
+++ b/packages/types/src/documents/global/environmentVariables.ts
@@ -14,3 +14,7 @@ export type EnvironmentVariablesDecrypted = Record<
   string,
   EnvironmentVariableValue
 >
+
+export interface EnvironmentVariablesDocDecrypted extends Document {
+  variables: EnvironmentVariablesDecrypted
+}