import { KEY_ID_PATTERN, isSealedWithCurrentKey, loadKeyRing, openLegacyCiphertext, openWithRing, sealWithRing, warnLegacyRead, } from '../common/crypto/key-ring'; // Webhook signing secrets must stay reversible (the server signs with them), // so they are sealed with the webhook key ring (WEBHOOK_ENCRYPTION_KEY, see // common/crypto/key-ring.ts). Two older forms stay readable until the rekey // job reseals them: "enc:v1:" + a ciphertext under sha256(JWT_ACCESS_SECRET), // and, from before encryption, the plain secret without any prefix. const LEGACY_PREFIX = 'enc:v1:'; export function sealWebhookSecret(secret: string): string { return sealWithRing(loadKeyRing('webhook'), secret); } export function openWebhookSecret(stored: string): string { if (KEY_ID_PATTERN.test(stored)) { return openWithRing(loadKeyRing('webhook'), stored).plaintext; } if (stored.startsWith(LEGACY_PREFIX)) { return openLegacyCiphertext('webhook', stored.slice(LEGACY_PREFIX.length)); } warnLegacyRead('webhook'); return stored; } /** True for secrets the rekey job should reseal with the current key. */ export function webhookSecretNeedsRekey(stored: string): boolean { return !isSealedWithCurrentKey(loadKeyRing('webhook'), stored); }