mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 22:26:13 +00:00
fix(recovery): fingerprint a chosen secret with scrypt, not a fast hash
API_TOKEN_HMAC_KEY can be a phrase, and its fingerprint is shown on the admin page and written into dump manifests in the bucket; a fast hash there lets a guessed phrase be checked offline. Text secrets now use a scrypt fingerprint; the random encryption keys keep theirs. The kit's check of a pasted token key compares keyed digests that never leave the process.
This commit is contained in:
1 parent
135d5baf28
commit
ae0b8d604a
4 files changed
+44
-9
No files matched your search
@@ -26,7 +26,7 @@
|
||||
* writing still restores to exactly the counts it claims.
|
||||
*/
|
||||
import { spawn } from "node:child_process";
|
||||
import { createDecipheriv, createHash } from "node:crypto";
|
||||
import { createDecipheriv, createHash, scryptSync } from "node:crypto";
|
||||
import {
|
||||
accessSync,
|
||||
closeSync,
|
||||
@@ -452,6 +452,28 @@ export function fingerprintBytes(bytes) {
|
||||
return createHash("sha256").update(bytes).digest("hex").slice(0, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* The public fingerprint of a secret an operator chose as text, such as
|
||||
* `API_TOKEN_HMAC_KEY`. Unlike the encryption keys, which are 32 random bytes,
|
||||
* such a value may be a phrase, and a fast hash of it shown on the admin page
|
||||
* or written into a manifest in the bucket would let a guessed phrase be
|
||||
* checked offline. scrypt makes every guess cost tens of milliseconds; the
|
||||
* fixed salt keeps one fingerprint per value, which is what a fingerprint is
|
||||
* for. `src/lib/recovery/kit-rules.ts` uses this same function.
|
||||
*
|
||||
* @param {string} value
|
||||
*/
|
||||
export function fingerprintSecretText(value) {
|
||||
return scryptSync(value, "healthlog/secret-fingerprint/v1", 16, {
|
||||
N: 2 ** 15,
|
||||
r: 8,
|
||||
p: 1,
|
||||
maxmem: 64 * 1024 * 1024,
|
||||
})
|
||||
.toString("hex")
|
||||
.slice(0, 12);
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* encryption: {
|
||||
@@ -520,7 +542,7 @@ export function keyFingerprints(env = process.env) {
|
||||
backupEncryptionPreviousKeys: previous,
|
||||
// Used as a string by the app (`createHmac("sha256", key)`), so its
|
||||
// fingerprint is over the string's bytes.
|
||||
apiTokenHmacKey: hmac ? fingerprintBytes(Buffer.from(hmac, "utf8")) : null,
|
||||
apiTokenHmacKey: hmac ? fingerprintSecretText(hmac) : null,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user