A fingerprint comparison proves two archives agree about a key. Only opening real ciphertext from the target proves the key in hand reads the data, which is the question an operator actually has.
157 lines
4.0 KiB
Go
157 lines
4.0 KiB
Go
package backup
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gitea.hostxtra.co.uk/mrhid6/vantage/shared/cryptobox"
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
)
|
|
|
|
func TestVerifyMatchingKey(t *testing.T) {
|
|
client, _ := testDB(t)
|
|
archive := archiveOf(t, client, validKeyHex, false)
|
|
|
|
rep, err := Verify(context.Background(), VerifyOptions{Archive: archive, KeyHex: validKeyHex})
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if !rep.KeyMatchesArchive {
|
|
t.Fatal("matching key reported as a mismatch")
|
|
}
|
|
if rep.ProbeAttempted {
|
|
t.Fatal("probe ran with no client supplied")
|
|
}
|
|
if !rep.OK() {
|
|
t.Fatalf("report not OK: %v", rep.Problems)
|
|
}
|
|
}
|
|
|
|
func TestVerifyMismatchedKeyIsNotOK(t *testing.T) {
|
|
client, _ := testDB(t)
|
|
archive := archiveOf(t, client, validKeyHex, false)
|
|
other := "0000000000000000000000000000000000000000000000000000000000000002"
|
|
|
|
rep, err := Verify(context.Background(), VerifyOptions{Archive: archive, KeyHex: other})
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if rep.KeyMatchesArchive {
|
|
t.Fatal("mismatched key reported as matching")
|
|
}
|
|
if rep.OK() {
|
|
t.Fatal("a mismatch must not report OK")
|
|
}
|
|
}
|
|
|
|
func TestVerifyProbeDecryptsLiveCiphertext(t *testing.T) {
|
|
client, dbName := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
key, err := ParseKey(validKeyHex)
|
|
if err != nil {
|
|
t.Fatalf("ParseKey: %v", err)
|
|
}
|
|
sealed, err := cryptobox.Seal(key, "s3cret")
|
|
if err != nil {
|
|
t.Fatalf("Seal: %v", err)
|
|
}
|
|
if _, err := client.Database(dbName).Collection("secrets").InsertOne(ctx, bson.M{
|
|
"instance_id": "i1",
|
|
"values": bson.M{"TOKEN": sealed},
|
|
}); err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
path, _ := dumpToFile(t, DumpOptions{Client: client, Database: dbName, KeyHex: validKeyHex})
|
|
archive, err := Open(path)
|
|
if err != nil {
|
|
t.Fatalf("Open: %v", err)
|
|
}
|
|
defer archive.Close()
|
|
|
|
rep, err := Verify(ctx, VerifyOptions{
|
|
Archive: archive, KeyHex: validKeyHex, Client: client, Database: dbName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if !rep.ProbeAttempted {
|
|
t.Fatal("probe did not run with a client supplied")
|
|
}
|
|
if !rep.ProbeDecrypted {
|
|
t.Fatalf("probe failed to decrypt live ciphertext: %v", rep.Problems)
|
|
}
|
|
if rep.ProbeCollection != "secrets" {
|
|
t.Fatalf("probe collection %q, want secrets", rep.ProbeCollection)
|
|
}
|
|
if !rep.OK() {
|
|
t.Fatalf("report not OK: %v", rep.Problems)
|
|
}
|
|
}
|
|
|
|
func TestVerifyProbeFailsWithWrongKey(t *testing.T) {
|
|
client, dbName := testDB(t)
|
|
ctx := context.Background()
|
|
|
|
key, err := ParseKey(validKeyHex)
|
|
if err != nil {
|
|
t.Fatalf("ParseKey: %v", err)
|
|
}
|
|
sealed, err := cryptobox.Seal(key, "s3cret")
|
|
if err != nil {
|
|
t.Fatalf("Seal: %v", err)
|
|
}
|
|
if _, err := client.Database(dbName).Collection("secrets").InsertOne(ctx, bson.M{
|
|
"values": bson.M{"TOKEN": sealed},
|
|
}); err != nil {
|
|
t.Fatalf("insert: %v", err)
|
|
}
|
|
|
|
other := "0000000000000000000000000000000000000000000000000000000000000002"
|
|
path, _ := dumpToFile(t, DumpOptions{Client: client, Database: dbName, KeyHex: other})
|
|
archive, err := Open(path)
|
|
if err != nil {
|
|
t.Fatalf("Open: %v", err)
|
|
}
|
|
defer archive.Close()
|
|
|
|
rep, err := Verify(ctx, VerifyOptions{
|
|
Archive: archive, KeyHex: other, Client: client, Database: dbName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if rep.ProbeDecrypted {
|
|
t.Fatal("probe decrypted with the wrong key")
|
|
}
|
|
if rep.OK() {
|
|
t.Fatal("a failed probe must not report OK")
|
|
}
|
|
}
|
|
|
|
func TestVerifyProbeAbsentCiphertextIsNotAFailure(t *testing.T) {
|
|
client, dbName := testDB(t)
|
|
seed(t, client, dbName)
|
|
|
|
path, _ := dumpToFile(t, DumpOptions{Client: client, Database: dbName, KeyHex: validKeyHex})
|
|
archive, err := Open(path)
|
|
if err != nil {
|
|
t.Fatalf("Open: %v", err)
|
|
}
|
|
defer archive.Close()
|
|
|
|
rep, err := Verify(context.Background(), VerifyOptions{
|
|
Archive: archive, KeyHex: validKeyHex, Client: client, Database: dbName,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if rep.ProbeAttempted {
|
|
t.Fatal("probe claims to have run against a database with no ciphertext")
|
|
}
|
|
if !rep.OK() {
|
|
t.Fatalf("a database storing no secrets must still verify: %v", rep.Problems)
|
|
}
|
|
}
|