- ect library: create, verify, DAG validation, ledger interface - In-memory ledger and ECTStore for full ledger mode - Test vectors and unit tests; two-agent demo (cmd/demo) - README: document refimpl scope and usage Co-authored-by: Cursor <cursoragent@cursor.com>
100 lines
2.3 KiB
Go
100 lines
2.3 KiB
Go
package ect
|
|
|
|
import (
|
|
"crypto/ecdsa"
|
|
"encoding/json"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestCreateRoundtrip(t *testing.T) {
|
|
key, err := GenerateKey()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
now := time.Now()
|
|
payload := &Payload{
|
|
Iss: "spiffe://example.com/agent/a",
|
|
Aud: []string{"spiffe://example.com/agent/b"},
|
|
Iat: now.Unix(),
|
|
Exp: now.Add(10 * time.Minute).Unix(),
|
|
Jti: "e4f5a6b7-c8d9-0123-ef01-234567890abc",
|
|
Tid: "550e8400-e29b-41d4-a716-446655440001",
|
|
ExecAct: "review_spec",
|
|
Par: []string{},
|
|
Pol: "spec_review_policy_v2",
|
|
PolDecision: PolDecisionApproved,
|
|
}
|
|
compact, err := Create(payload, key, CreateOptions{KeyID: "agent-a-key-1"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if compact == "" {
|
|
t.Fatal("expected non-empty compact JWS")
|
|
}
|
|
|
|
// Verify with same key
|
|
resolver := func(kid string) (*ecdsa.PublicKey, error) {
|
|
if kid != "agent-a-key-1" {
|
|
return nil, nil
|
|
}
|
|
return &key.PublicKey, nil
|
|
}
|
|
opts := VerifyOptions{
|
|
VerifierID: "spiffe://example.com/agent/b",
|
|
ResolveKey: resolver,
|
|
Now: now,
|
|
IATMaxAge: 15 * time.Minute,
|
|
IATMaxFuture: 30 * time.Second,
|
|
}
|
|
parsed, err := Verify(compact, opts)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if parsed.Payload.Tid != payload.Tid || parsed.Payload.ExecAct != payload.ExecAct {
|
|
t.Errorf("payload mismatch: got tid=%q exec_act=%q", parsed.Payload.Tid, parsed.Payload.ExecAct)
|
|
}
|
|
}
|
|
|
|
func TestCreateWithTestVector(t *testing.T) {
|
|
data, err := os.ReadFile("testdata/valid_root_ect_payload.json")
|
|
if err != nil {
|
|
t.Skipf("test vector not found: %v", err)
|
|
return
|
|
}
|
|
var p Payload
|
|
if err := json.Unmarshal(data, &p); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
key, err := GenerateKey()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// Override timestamps for verification
|
|
now := time.Now()
|
|
p.Iat = now.Unix()
|
|
p.Exp = now.Add(10 * time.Minute).Unix()
|
|
|
|
compact, err := Create(&p, key, CreateOptions{KeyID: "test-kid"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resolver := func(kid string) (*ecdsa.PublicKey, error) {
|
|
if kid != "test-kid" {
|
|
return nil, nil
|
|
}
|
|
return &key.PublicKey, nil
|
|
}
|
|
_, err = Verify(compact, VerifyOptions{
|
|
VerifierID: p.Aud[0],
|
|
ResolveKey: resolver,
|
|
Now: now,
|
|
IATMaxAge: 15 * time.Minute,
|
|
IATMaxFuture: 30 * time.Second,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|