package crypto import ( "bytes" "crypto/ed25519" "crypto/rand" "strings" "testing" "github.com/hiddeco/sshsig" "golang.org/x/crypto/ssh" ) // testKey generates an ephemeral ed25519 key pair and returns the ssh.Signer // and the public key in authorized_keys format. func testKey(t *testing.T) (ssh.Signer, []byte) { t.Helper() _, priv, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatalf("generate ed25519 key: %v", err) } signer, err := ssh.NewSignerFromKey(priv) if err != nil { t.Fatalf("create signer: %v", err) } return signer, ssh.MarshalAuthorizedKey(signer.PublicKey()) } // testSign signs payload with signer using the same parameters as VerifySignature // expects (SHA-512, "git" namespace) and returns the armored signature. func testSign(t *testing.T, signer ssh.Signer, payload []byte) []byte { t.Helper() sig, err := sshsig.Sign(bytes.NewReader(payload), signer, sshsig.HashSHA512, "git") if err != nil { t.Fatalf("sign payload: %v", err) } return sshsig.Armor(sig) } func TestSSHFingerprint(t *testing.T) { t.Run("valid key returns SHA256 fingerprint", func(t *testing.T) { _, pubKeyBytes := testKey(t) fp, err := SSHFingerprint(string(pubKeyBytes)) if err != nil { t.Fatalf("unexpected error: %v", err) } if !strings.HasPrefix(fp, "SHA256:") { t.Errorf("fingerprint %q does not start with SHA256:", fp) } }) t.Run("same key returns identical fingerprint", func(t *testing.T) { _, pubKeyBytes := testKey(t) fp1, _ := SSHFingerprint(string(pubKeyBytes)) fp2, _ := SSHFingerprint(string(pubKeyBytes)) if fp1 != fp2 { t.Errorf("fingerprint not deterministic: %q != %q", fp1, fp2) } }) t.Run("different keys return different fingerprints", func(t *testing.T) { _, pub1 := testKey(t) _, pub2 := testKey(t) fp1, _ := SSHFingerprint(string(pub1)) fp2, _ := SSHFingerprint(string(pub2)) if fp1 == fp2 { t.Error("different keys produced the same fingerprint") } }) t.Run("malformed key returns error", func(t *testing.T) { _, err := SSHFingerprint("not a valid ssh public key") if err == nil { t.Error("expected error for malformed key") } }) } func TestVerifySignature(t *testing.T) { signer, pubKeyBytes := testKey(t) payload := []byte("test payload") armoredSig := testSign(t, signer, payload) t.Run("valid signature verifies successfully", func(t *testing.T) { err, ok := VerifySignature(pubKeyBytes, armoredSig, payload) if err != nil { t.Errorf("unexpected error: %v", err) } if !ok { t.Error("expected ok=true for valid signature") } }) t.Run("malformed public key returns error", func(t *testing.T) { err, ok := VerifySignature([]byte("not a valid key"), armoredSig, payload) if err == nil { t.Error("expected error for malformed public key") } if ok { t.Error("expected ok=false") } }) t.Run("malformed signature returns error", func(t *testing.T) { err, ok := VerifySignature(pubKeyBytes, []byte("not a valid signature"), payload) if err == nil { t.Error("expected error for malformed signature") } if ok { t.Error("expected ok=false") } }) t.Run("tampered payload fails verification", func(t *testing.T) { err, ok := VerifySignature(pubKeyBytes, armoredSig, []byte("tampered")) if err == nil { t.Error("expected error for tampered payload") } if ok { t.Error("expected ok=false for tampered payload") } }) t.Run("wrong public key fails verification", func(t *testing.T) { _, otherPubKey := testKey(t) err, ok := VerifySignature(otherPubKey, armoredSig, payload) if err == nil { t.Error("expected error for wrong public key") } if ok { t.Error("expected ok=false for wrong public key") } }) }