Files
vaultik/internal/snapshot/remotekey.go

42 lines
1.6 KiB
Go

package snapshot
import (
"crypto/sha256"
"encoding/hex"
)
// remoteKeyPrefix is mixed into the snapshot ID hash so the resulting
// hex digest is domain-separated from any other "double SHA256 of a
// string" identifier the user might also use. Keeping this stable is a
// hard compatibility requirement: changing it invalidates every
// existing snapshot's remote storage path.
const remoteKeyPrefix = "vaultik|"
// RemoteSnapshotKey returns the storage-side identifier for a snapshot
// given its human snapshot ID. It is hex(SHA256(SHA256(prefix + id))).
// The two SHA256 rounds match Bitcoin's "hash256" convention so the
// output looks like a 64-character hex blob with no exploitable
// structure visible to a remote observer.
//
// We use this in three places:
//
// - the "metadata/<remote-key>/..." subdirectory on the storage
// backend so a directory listing of the bucket / file:// dest
// doesn't reveal hostnames, configured snapshot names, or backup
// timestamps;
// - the `snapshot_id` field of the unencrypted manifest.json.zst
// for the same reason;
// - any code path that needs to translate a known local snapshot ID
// into the path it would occupy on remote storage.
//
// The human ID stays the user-visible handle everywhere else — local
// database joins, CLI arguments, summary lines, log fields — because
// it's never written to the public bytes once this function gates
// every storage-path construction.
func RemoteSnapshotKey(snapshotID string) string {
first := sha256.Sum256([]byte(remoteKeyPrefix + snapshotID))
second := sha256.Sum256(first[:])
return hex.EncodeToString(second[:])
}