ProofedMesh/services/signing-service/internal/signer/signer_test.go
CTO Agent d9fa1c55b9
All checks were successful
validate / Rego (opa fmt + opa test) (push) Successful in 14s
validate / JSON Schema (ajv) (push) Successful in 19s
validate / Go (vet + test) (push) Successful in 18s
services: add signing-service (real Ed25519 DSSE, in-memory keyring, AllowAll+Static validators as DEV; OpenBao/OPA/Pocket-ID as stubs). Interop with admin-api attest.HTTPSigner verified: 19 Go-Tests + end-to-end content_hash equality.
2026-10-25 18:02:44 +00:00

99 lines
2.9 KiB
Go

package signer
import (
"context"
"crypto/ed25519"
"crypto/rand"
"encoding/base64"
"errors"
"testing"
"basicstack.de/proofedmesh/services/signing-service/internal/canonical"
)
func newKey(t *testing.T) ed25519.PrivateKey {
t.Helper()
_, priv, err := ed25519.GenerateKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
return priv
}
func TestInMemory_SignRoundtrip(t *testing.T) {
priv := newKey(t)
m := NewInMemory()
m.AddKey("test-key", priv)
stmt := Statement{
"_type": "https://in-toto.io/Statement/v1",
"predicateType": "https://basicstack.de/attestations/rate_change/v1",
"subject": []any{map[string]any{"name": "per_diem_rate/PDR-1", "digest": map[string]any{"sha256": "deadbeef"}}},
"predicate": map[string]any{"change_kind": "create"},
}
s, err := SignStatement(context.Background(), m, "test-key", stmt)
if err != nil {
t.Fatalf("sign: %v", err)
}
if s.Envelope.PayloadType != "application/vnd.in-toto+json" {
t.Errorf("payload type: %q", s.Envelope.PayloadType)
}
if len(s.Envelope.Signatures) != 1 {
t.Fatalf("signatures: %d", len(s.Envelope.Signatures))
}
if s.Envelope.Signatures[0].KeyID != "test-key" {
t.Errorf("keyid: %q", s.Envelope.Signatures[0].KeyID)
}
// Verify the signature verifies against the public key.
pub := priv.Public().(ed25519.PublicKey)
payloadBytes, err := base64.RawURLEncoding.DecodeString(s.Envelope.Payload)
if err != nil {
t.Fatalf("decode payload: %v", err)
}
pae := encodePAE("application/vnd.in-toto+json", payloadBytes)
sig, err := base64.RawURLEncoding.DecodeString(s.Envelope.Signatures[0].Sig)
if err != nil {
t.Fatalf("decode sig: %v", err)
}
if !ed25519.Verify(pub, pae, sig) {
t.Fatal("signature does not verify against public key")
}
// content_hash should be reproducible from the envelope.
envBytes, _ := canonical.Marshal(s.Envelope)
expected := "sha256:" + canonical.SHA256Hex(envBytes)
if s.ContentHash != expected {
t.Errorf("content_hash mismatch:\n got: %s\nwant: %s", s.ContentHash, expected)
}
}
func TestInMemory_UnknownKeyRef(t *testing.T) {
m := NewInMemory()
_, _, err := m.Sign(context.Background(), "nope", []byte("x"))
if !errors.Is(err, ErrUnknownKey) {
t.Fatalf("expected ErrUnknownKey, got %v", err)
}
}
func TestOpenBao_StubReturnsNotWired(t *testing.T) {
_, _, err := OpenBao{}.Sign(context.Background(), "k", []byte("x"))
if !errors.Is(err, ErrNotWired) {
t.Fatalf("expected ErrNotWired, got %v", err)
}
}
func TestEncodePAE_FormatIsExact(t *testing.T) {
got := encodePAE("application/vnd.in-toto+json", []byte("hello"))
// "application/vnd.in-toto+json" is 28 bytes, "hello" is 5 bytes.
want := []byte("DSSEv1 28 application/vnd.in-toto+json 5 hello")
if string(got) != string(want) {
t.Errorf("PAE mismatch:\n got: %q\nwant: %q", got, want)
}
}
func TestDecodeHexEd25519Key_RejectsWrongLength(t *testing.T) {
if _, err := DecodeHexEd25519Key("deadbeef"); err == nil {
t.Fatal("expected error for short key")
}
}