package protocol_test import ( "bytes" "fmt" "git.n1ko.dev/Niko/niko_trust/pkg/identity/signer" "git.n1ko.dev/Niko/niko_trust/pkg/protocol" "git.n1ko.dev/Niko/niko_trust/pkg/tce" ) // ExampleClaimLifecycle is the whole client-side story for one claim, with no // server in the loop: generate keys, build the canonical bytes, sign them, and // let any verifier decode and verify locally. The signature is computed over // the exact TCE bytes, so it stays valid no matter which relay later serves // them (PROTOCOL.md section 7.3). func Example_claimLifecycle() { // In production use signer.Generate(); fixed seeds keep this example // deterministic so its output can be asserted. issuer, _ := signer.FromSeed(bytes.Repeat([]byte{0x01}, 32)) subject, _ := signer.FromSeed(bytes.Repeat([]byte{0x02}, 32)) c := &protocol.Claim{ Issuer: issuer.Public(), Subject: subject.Public(), Claims: map[string]tce.Value{"example.flag": tce.Bool(true)}, CreatedAt: 1_700_000_000, ExpiresAt: 1_700_086_400, Serial: 1, Nonce: bytes.Repeat([]byte{0x10}, tce.NonceSize), } // 1. Client builds and signs the canonical bytes. tceBytes, err := protocol.EncodeClaim(c) if err != nil { fmt.Println("encode:", err) return } sig := issuer.Sign(tceBytes) // 2. Any verifier, anywhere, with only the bytes + signature. got, err := protocol.DecodeClaim(tceBytes) if err != nil { fmt.Println("decode:", err) return } if _, err := protocol.VerifyClaim(tceBytes, sig); err != nil { fmt.Println("verify:", err) return } v, ok := got.Claims["example.flag"].Bool() fmt.Println("verified", got.CreatedAt == c.CreatedAt, ok && v) // Output: verified true true } // ExampleAuthHandshake is the login flow (PROTOCOL.md section 8.6). The server // mints a single-use challenge; the client proves possession of its key by // signing an AuthAssertion bound to that challenge and to the server's // audience. The server verifies the assertion but never sees a private key. func Example_authHandshake() { client, _ := signer.FromSeed(bytes.Repeat([]byte{0x03}, 32)) // Server side: issue a 32-byte challenge (CSPRNG in reality). challenge := bytes.Repeat([]byte{0x07}, tce.ChallengeSize) audience := "trust.n1ko.dev" // Client side: build, sign, return. a := &protocol.AuthAssertion{ PubKey: client.Public(), Challenge: challenge, Scope: "read:claims", Audience: audience, CreatedAt: 1_700_000_000, } b, err := protocol.EncodeAuthAssertion(a) if err != nil { fmt.Println("encode:", err) return } sig := client.Sign(b) // Server side: verify the assertion for this exact audience. if _, err := protocol.VerifyAuthAssertion(b, sig, audience); err != nil { fmt.Println("auth failed:", err) return } fmt.Println("auth ok") // Output: auth ok }