Files
vantage-shared/backup/fingerprint.go
T
mrhid6 088ebe9848 feat: Add key fingerprinting for backup archives
Fingerprint hashes the raw key bytes rather than the hex string, so the
same key written in different cases fingerprints identically.
2026-09-07 11:03:48 +00:00

58 lines
1.8 KiB
Go

// Package backup dumps and restores a whole Vantage MongoDB database.
//
// The archive never contains KEY_ENCRYPTION_KEY. It contains a fingerprint of
// it, which is enough to answer "will this archive restore into this
// deployment" and is not a hint at the value.
package backup
import (
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"gitea.hostxtra.co.uk/mrhid6/vantage/shared/cryptobox"
)
// ErrNoKey is returned when no key was supplied at all. It is distinct from
// ErrBadKey because the operator remedies are different: one is "set the
// variable", the other is "the value you set is wrong".
var ErrNoKey = errors.New("KEY_ENCRYPTION_KEY is not set")
// ErrBadKey is returned when a key was supplied but is not 64 hex characters.
var ErrBadKey = errors.New("KEY_ENCRYPTION_KEY must be a 64-character hex string (32 bytes)")
// ParseKey decodes the hex form used by KEY_ENCRYPTION_KEY.
func ParseKey(hexKey string) ([]byte, error) {
if hexKey == "" {
return nil, ErrNoKey
}
key, err := hex.DecodeString(hexKey)
if err != nil {
return nil, fmt.Errorf("%w: not hexadecimal", ErrBadKey)
}
if len(key) != cryptobox.KeySize {
return nil, fmt.Errorf("%w: decoded to %d bytes", ErrBadKey, len(key))
}
return key, nil
}
// Fingerprint is the SHA-256 of the raw key bytes, hex encoded.
//
// Of the raw bytes rather than of the hex string, so an operator who writes the
// key in uppercase in one deployment and lowercase in another still gets one
// fingerprint for one key.
func Fingerprint(key []byte) string {
sum := sha256.Sum256(key)
return hex.EncodeToString(sum[:])
}
// FingerprintHex parses and fingerprints in one step.
func FingerprintHex(hexKey string) (string, error) {
key, err := ParseKey(hexKey)
if err != nil {
return "", err
}
return Fingerprint(key), nil
}