package machine_identity import ( "crypto/ed25519" "crypto/rand" "crypto/tls" "encoding/base64" "encoding/json" "errors" "os" "path/filepath" "testing" "time" ) type crossLanguageVector struct { PublicKey string `json:"public_key_base64url"` Token string `json:"token"` Now int64 `json:"now"` Audience string `json:"audience"` Scope string `json:"required_scope"` Method string `json:"method"` Path string `json:"path"` Body string `json:"body_base64"` } func testIdentity(t *testing.T) (Signer, *Registry, time.Time) { t.Helper() publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) if err != nil { t.Fatal(err) } now := time.Unix(1_800_000_000, 0).UTC() registry, err := NewRegistry(KeyRecord{Principal: "yv:sense:site-a", KeyID: "sense-key-0001", PublicKey: publicKey, Audience: "yovision-brain", Scopes: []string{"source-config:write"}, Enabled: true}) if err != nil { t.Fatal(err) } return Signer{Principal: "yv:sense:site-a", KeyID: "sense-key-0001", PrivateKey: privateKey, Now: func() time.Time { return now }}, registry, now } func errorCode(t *testing.T, err error) string { t.Helper() var coded *Error if !errors.As(err, &coded) { t.Fatalf("expected coded error, got %v", err) } return coded.Code } func TestMintAndVerifyRequestBoundToken(t *testing.T) { signer, registry, now := testIdentity(t) body := []byte(`{"revision":7}`) token, err := signer.Mint("yovision-brain", []string{"source-config:write"}, "POST", "/machine/v1/source-config", body) if err != nil { t.Fatal(err) } verifier := Verifier{Registry: registry, Replay: NewReplayStore(), Now: func() time.Time { return now }} claims, err := verifier.Verify(token, "yovision-brain", "source-config:write", "POST", "/machine/v1/source-config", body) if err != nil { t.Fatal(err) } if claims.Issuer != signer.Principal || claims.Subject != signer.Principal || claims.ExpiresAt-claims.IssuedAt != 300 { t.Fatalf("unexpected claims: %+v", claims) } } func TestBearerTokenHasNoCookieOrQueryFallback(t *testing.T) { if token, err := BearerToken("Bearer compact.token.value"); err != nil || token != "compact.token.value" { t.Fatal(err) } for _, value := range []string{"", "compact.token.value", "Bearer", "Bearer one two", "Cookie compact.token.value"} { if _, err := BearerToken(value); errorCode(t, err) != "machine_token_missing" { t.Fatalf("accepted %q", value) } } } func TestRejectsReplayWrongAudienceScopeAndRequest(t *testing.T) { signer, registry, now := testIdentity(t) body := []byte(`{"revision":7}`) mint := func() string { token, err := signer.Mint("yovision-brain", []string{"source-config:write"}, "POST", "/machine/v1/source-config", body) if err != nil { t.Fatal(err) } return token } verifier := Verifier{Registry: registry, Replay: NewReplayStore(), Now: func() time.Time { return now }} token := mint() if _, err := verifier.Verify(token, "yovision-brain", "source-config:write", "POST", "/machine/v1/source-config", body); err != nil { t.Fatal(err) } if _, err := verifier.Verify(token, "yovision-brain", "source-config:write", "POST", "/machine/v1/source-config", body); errorCode(t, err) != "machine_token_replayed" { t.Fatal(err) } if _, err := verifier.Verify(mint(), "yovision-bell", "source-config:write", "POST", "/machine/v1/source-config", body); errorCode(t, err) != "machine_audience_denied" { t.Fatal(err) } if _, err := verifier.Verify(mint(), "yovision-brain", "events:ingest", "POST", "/machine/v1/source-config", body); errorCode(t, err) != "machine_scope_denied" { t.Fatal(err) } if _, err := verifier.Verify(mint(), "yovision-brain", "source-config:write", "POST", "/machine/v1/source-config", []byte("changed")); errorCode(t, err) != "machine_token_invalid" { t.Fatal(err) } } func TestExpiryRevocationAndRotation(t *testing.T) { signer, registry, now := testIdentity(t) body := []byte("{}") token, _ := signer.Mint("yovision-brain", []string{"source-config:write"}, "POST", "/machine/v1/source-config", body) expired := Verifier{Registry: registry, Replay: NewReplayStore(), Now: func() time.Time { return now.Add(6 * time.Minute) }} if _, err := expired.Verify(token, "yovision-brain", "source-config:write", "POST", "/machine/v1/source-config", body); errorCode(t, err) != "machine_token_expired" { t.Fatal(err) } oldPublic, oldPrivate, _ := ed25519.GenerateKey(rand.Reader) newPublic, newPrivate, _ := ed25519.GenerateKey(rand.Reader) rotation, err := NewRegistry( KeyRecord{Principal: "yv:brain:node-a", KeyID: "brain-old-0001", PublicKey: oldPublic, Audience: "yovision-sense", Scopes: []string{"runtime-status:write"}, Enabled: true}, KeyRecord{Principal: "yv:brain:node-a", KeyID: "brain-new-0002", PublicKey: newPublic, Audience: "yovision-sense", Scopes: []string{"runtime-status:write"}, Enabled: true}, ) if err != nil { t.Fatal(err) } oldSigner := Signer{Principal: "yv:brain:node-a", KeyID: "brain-old-0001", PrivateKey: oldPrivate, Now: func() time.Time { return now }} newSigner := Signer{Principal: "yv:brain:node-a", KeyID: "brain-new-0002", PrivateKey: newPrivate, Now: func() time.Time { return now }} oldToken, _ := oldSigner.Mint("yovision-sense", []string{"runtime-status:write"}, "POST", "/machine/v1/runtime-status", body) newToken, _ := newSigner.Mint("yovision-sense", []string{"runtime-status:write"}, "POST", "/machine/v1/runtime-status", body) verify := Verifier{Registry: rotation, Replay: NewReplayStore(), Now: func() time.Time { return now }} if _, err = verify.Verify(oldToken, "yovision-sense", "runtime-status:write", "POST", "/machine/v1/runtime-status", body); err != nil { t.Fatal(err) } if _, err = verify.Verify(newToken, "yovision-sense", "runtime-status:write", "POST", "/machine/v1/runtime-status", body); err != nil { t.Fatal(err) } if !rotation.Revoke("brain-old-0001") { t.Fatal("old key was not revoked") } oldAfterRevoke, _ := oldSigner.Mint("yovision-sense", []string{"runtime-status:write"}, "POST", "/machine/v1/runtime-status", body) if _, err = verify.Verify(oldAfterRevoke, "yovision-sense", "runtime-status:write", "POST", "/machine/v1/runtime-status", body); errorCode(t, err) != "machine_identity_revoked" { t.Fatal(err) } } func TestTransportPolicyRejectsUnsafeTLS(t *testing.T) { safe := TransportPolicy{TLSMinVersion: tls.VersionTLS12, VerifyCertificate: true, VerifyHostname: true, ConnectTimeout: time.Second, ResponseHeaderTimeout: time.Second, RequestTimeout: 2 * time.Second, MaxRequestBytes: 1024} if err := safe.Validate(); err != nil { t.Fatal(err) } unsafe := safe unsafe.VerifyHostname = false if err := unsafe.Validate(); err == nil { t.Fatal("unsafe hostname policy accepted") } unsafe = safe unsafe.TLSMinVersion = tls.VersionTLS11 if err := unsafe.Validate(); err == nil { t.Fatal("TLS 1.1 accepted") } } func TestVerifiesCrossLanguageVector(t *testing.T) { vectorPath := filepath.Join("..", "..", "..", "..", "..", "..", "contracts", "tests", "machine-identity-v1", "cross-language-vector.json") raw, err := os.ReadFile(vectorPath) if err != nil { t.Fatal(err) } var vector crossLanguageVector if err = json.Unmarshal(raw, &vector); err != nil { t.Fatal(err) } publicKey, err := base64.RawURLEncoding.DecodeString(vector.PublicKey) if err != nil { t.Fatal(err) } body, err := base64.StdEncoding.DecodeString(vector.Body) if err != nil { t.Fatal(err) } registry, err := NewRegistry(KeyRecord{Principal: "yv:brain:vector", KeyID: "brain-vector-0001", PublicKey: ed25519.PublicKey(publicKey), Audience: vector.Audience, Scopes: []string{vector.Scope}, Enabled: true}) if err != nil { t.Fatal(err) } verifier := Verifier{Registry: registry, Replay: NewReplayStore(), Now: func() time.Time { return time.Unix(vector.Now, 0) }} claims, err := verifier.Verify(vector.Token, vector.Audience, vector.Scope, vector.Method, vector.Path, body) if err != nil { t.Fatal(err) } if claims.Issuer != "yv:brain:vector" { t.Fatalf("unexpected issuer: %s", claims.Issuer) } } func TestLoadsExternalPublicRegistryAndRejectsWrongAudience(t *testing.T) { publicKey, _, _ := ed25519.GenerateKey(rand.Reader) document := map[string]any{ "version": "yovision.machine-principal-registry/v1", "audience": "yovision-bell", "principals": []any{map[string]any{"principal_id": "yv:sense:site-a", "enabled": true, "keys": []any{map[string]any{ "kid": "sense-key-0001", "public_key_base64url": base64.RawURLEncoding.EncodeToString(publicKey), "status": "active", "scopes": []string{"events:ingest"}, }}}}, } raw, _ := json.Marshal(document) file := filepath.Join(t.TempDir(), "principals.json") if err := os.WriteFile(file, raw, 0o600); err != nil { t.Fatal(err) } registry, err := LoadRegistry(file, "yovision-bell") if err != nil { t.Fatal(err) } if record, ok := registry.Lookup("sense-key-0001"); !ok || record.Principal != "yv:sense:site-a" { t.Fatal("registry record missing") } if _, err = LoadRegistry(file, "yovision-sense"); err == nil { t.Fatal("wrong registry audience accepted") } }