Files
backrest/internal/cryptoutil/pairingtoken_test.go
2026-05-02 22:29:39 -07:00

118 lines
3.3 KiB
Go

package cryptoutil
import (
"testing"
)
func TestFormatAndParsePairingToken(t *testing.T) {
keyID := "ecdsa.abc123"
secret := "deadbeef1234567890abcdef"
instanceID := "my-server"
token := FormatPairingToken(keyID, secret, instanceID)
want := "ecdsa.abc123:deadbeef1234567890abcdef#my-server"
if token != want {
t.Fatalf("FormatPairingToken() = %q, want %q", token, want)
}
parsed, err := ParsePairingToken(token)
if err != nil {
t.Fatalf("ParsePairingToken() error: %v", err)
}
if parsed.KeyID != keyID {
t.Errorf("KeyID = %q, want %q", parsed.KeyID, keyID)
}
if parsed.Secret != secret {
t.Errorf("Secret = %q, want %q", parsed.Secret, secret)
}
if parsed.InstanceID != instanceID {
t.Errorf("InstanceID = %q, want %q", parsed.InstanceID, instanceID)
}
}
func TestParsePairingTokenWithColonsInKeyID(t *testing.T) {
// Key IDs contain base64url which shouldn't have colons, but test robustness
token := "ecdsa.key:secretvalue#server-1"
parsed, err := ParsePairingToken(token)
if err != nil {
t.Fatalf("ParsePairingToken() error: %v", err)
}
if parsed.KeyID != "ecdsa.key" {
t.Errorf("KeyID = %q, want %q", parsed.KeyID, "ecdsa.key")
}
if parsed.Secret != "secretvalue" {
t.Errorf("Secret = %q, want %q", parsed.Secret, "secretvalue")
}
if parsed.InstanceID != "server-1" {
t.Errorf("InstanceID = %q, want %q", parsed.InstanceID, "server-1")
}
}
func TestParsePairingTokenWithHashInInstanceID(t *testing.T) {
// Instance ID with a '#' — we use LastIndex so the last '#' is the delimiter
token := "ecdsa.key:secret#inst#2"
parsed, err := ParsePairingToken(token)
if err != nil {
t.Fatalf("ParsePairingToken() error: %v", err)
}
if parsed.InstanceID != "2" {
t.Errorf("InstanceID = %q, want %q", parsed.InstanceID, "2")
}
}
func TestParsePairingTokenErrors(t *testing.T) {
tests := []struct {
name string
token string
}{
{"missing hash", "ecdsa.key:secret"},
{"missing colon", "ecdsa.keysecret#server"},
{"empty key ID", ":secret#server"},
{"empty secret", "ecdsa.key:#server"},
{"empty instance ID", "ecdsa.key:secret#"},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
_, err := ParsePairingToken(tc.token)
if err == nil {
t.Errorf("ParsePairingToken(%q) should have returned error", tc.token)
}
})
}
}
func TestGeneratePairingSecret(t *testing.T) {
secret, err := GeneratePairingSecret()
if err != nil {
t.Fatalf("GeneratePairingSecret() error: %v", err)
}
if len(secret) != pairingSecretBytes*2 { // hex encoding doubles length
t.Errorf("secret length = %d, want %d", len(secret), pairingSecretBytes*2)
}
// Ensure two secrets are different (probabilistic but extremely reliable)
secret2, _ := GeneratePairingSecret()
if secret == secret2 {
t.Error("two generated secrets should not be equal")
}
}
func TestRoundTrip(t *testing.T) {
secret, err := GeneratePairingSecret()
if err != nil {
t.Fatalf("GeneratePairingSecret() error: %v", err)
}
keyID := "ecdsa.test-key-id-1234"
instanceID := "my-backrest-server"
token := FormatPairingToken(keyID, secret, instanceID)
parsed, err := ParsePairingToken(token)
if err != nil {
t.Fatalf("ParsePairingToken() error: %v", err)
}
if parsed.KeyID != keyID || parsed.Secret != secret || parsed.InstanceID != instanceID {
t.Errorf("round trip failed: got %+v", parsed)
}
}