All the UI fixes for now
This commit is contained in:
71
src/lib/crypto.ts
Normal file
71
src/lib/crypto.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* AES-256-GCM encryption for tenant package credentials.
|
||||
*
|
||||
* Credentials are encrypted before storage in tenant_requests.encrypted_secrets
|
||||
* and decrypted only during admin approval to write to OpenBao tenant paths.
|
||||
*
|
||||
* Format: [12-byte IV][ciphertext][16-byte auth tag] as a single Buffer.
|
||||
*
|
||||
* Provision the key:
|
||||
* bao kv put pieced/portal/encryption-key key="$(openssl rand -hex 32)"
|
||||
*/
|
||||
|
||||
import { randomBytes, createCipheriv, createDecipheriv } from "crypto";
|
||||
|
||||
const ALGORITHM = "aes-256-gcm";
|
||||
const IV_LENGTH = 12;
|
||||
const TAG_LENGTH = 16;
|
||||
|
||||
let cachedKey: Buffer | null = null;
|
||||
|
||||
async function getEncryptionKey(): Promise<Buffer> {
|
||||
if (cachedKey) return cachedKey;
|
||||
|
||||
const { readSecret } = await import("./openbao");
|
||||
const data = await readSecret("pieced/portal/encryption-key");
|
||||
const hex = data?.key;
|
||||
if (!hex || typeof hex !== "string" || hex.length !== 64) {
|
||||
throw new Error(
|
||||
"Invalid encryption key at secret/data/pieced/portal/encryption-key"
|
||||
);
|
||||
}
|
||||
cachedKey = Buffer.from(hex, "hex");
|
||||
return cachedKey;
|
||||
}
|
||||
|
||||
export async function encryptSecrets(
|
||||
secrets: Record<string, Record<string, string>>
|
||||
): Promise<Buffer> {
|
||||
const key = await getEncryptionKey();
|
||||
const iv = randomBytes(IV_LENGTH);
|
||||
const cipher = createCipheriv(ALGORITHM, key, iv);
|
||||
|
||||
const plaintext = JSON.stringify(secrets);
|
||||
const encrypted = Buffer.concat([
|
||||
cipher.update(plaintext, "utf8"),
|
||||
cipher.final(),
|
||||
]);
|
||||
const tag = cipher.getAuthTag();
|
||||
|
||||
return Buffer.concat([iv, encrypted, tag]);
|
||||
}
|
||||
|
||||
export async function decryptSecrets(
|
||||
blob: Buffer
|
||||
): Promise<Record<string, Record<string, string>>> {
|
||||
const key = await getEncryptionKey();
|
||||
|
||||
const iv = blob.subarray(0, IV_LENGTH);
|
||||
const tag = blob.subarray(blob.length - TAG_LENGTH);
|
||||
const ciphertext = blob.subarray(IV_LENGTH, blob.length - TAG_LENGTH);
|
||||
|
||||
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
||||
decipher.setAuthTag(tag);
|
||||
|
||||
const decrypted = Buffer.concat([
|
||||
decipher.update(ciphertext),
|
||||
decipher.final(),
|
||||
]);
|
||||
|
||||
return JSON.parse(decrypted.toString("utf8"));
|
||||
}
|
||||
Reference in New Issue
Block a user