- server: relay storing signed objects (PUT/GET), per-IP rate limiting, per-subject quota (1000), one-response-per-request, pagination, /v1/healthz /v1/readyz /v1/metrics - verify: signature-verifying trust evaluator; every object is checked via env.Verify(), approvals via VerifyApprovalResponse, revocations via VerifyRevocationOf; k-of-n approval quorum - docs: TRUST-MODEL.md and API.md describing issuer-anchored signatures and the endpoint/status-code contract - tests: server, verify, and ratelimit packages
118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
package protocol_test
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"encoding/json"
|
|
"os"
|
|
"testing"
|
|
|
|
"git.n1ko.dev/Niko/niko_trust/internal/identity/signer"
|
|
)
|
|
|
|
const vectorsPath = "../../testdata/vectors/tce_vectors.json"
|
|
|
|
// vectorFile mirrors the frozen reference file. Only the fields the protocol
|
|
// tests need are declared.
|
|
type vectorFile struct {
|
|
Parties map[string]struct {
|
|
SeedHex string `json:"seed_hex"`
|
|
PubkeyHex string `json:"pubkey_hex"`
|
|
Address string `json:"address"`
|
|
} `json:"parties"`
|
|
Vectors []vectorEntry `json:"vectors"`
|
|
Rejects []rejectEntry `json:"rejects"`
|
|
}
|
|
|
|
type vectorEntry struct {
|
|
Name string `json:"name"`
|
|
Description string `json:"description"`
|
|
TCEHex string `json:"tce_hex"`
|
|
TCELen int `json:"tce_len"`
|
|
ObjectIDHex string `json:"object_id_hex"`
|
|
SignerPubkey string `json:"signer_pubkey_hex"`
|
|
SignerAddress string `json:"signer_address"`
|
|
SignatureHex string `json:"signature_hex"`
|
|
RequestIDHex string `json:"request_id_hex"`
|
|
JSON json.RawMessage `json:"json"`
|
|
}
|
|
|
|
type rejectEntry struct {
|
|
Name string `json:"name"`
|
|
TCEHex *string `json:"tce_hex"`
|
|
}
|
|
|
|
func loadVectors(t *testing.T) *vectorFile {
|
|
t.Helper()
|
|
b, err := os.ReadFile(vectorsPath)
|
|
if err != nil {
|
|
t.Fatalf("read vectors: %v", err)
|
|
}
|
|
var vf vectorFile
|
|
if err := json.Unmarshal(b, &vf); err != nil {
|
|
t.Fatalf("parse vectors: %v", err)
|
|
}
|
|
return &vf
|
|
}
|
|
|
|
func mustHex(t *testing.T, s string) []byte {
|
|
t.Helper()
|
|
b, err := hex.DecodeString(s)
|
|
if err != nil {
|
|
t.Fatalf("bad hex %q: %v", s, err)
|
|
}
|
|
return b
|
|
}
|
|
|
|
func mustSigner(t *testing.T) *signer.Signer {
|
|
t.Helper()
|
|
s, err := signer.Generate()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// seedSigner builds a signer from a 32-byte seed.
|
|
func seedSigner(t *testing.T, seed []byte) *signer.Signer {
|
|
t.Helper()
|
|
s, err := signer.FromSeed(seed)
|
|
if err != nil {
|
|
t.Fatalf("FromSeed: %v", err)
|
|
}
|
|
return s
|
|
}
|
|
|
|
// nonce returns a 16-byte nonce filled with a repeating byte.
|
|
func nonce(b byte) []byte {
|
|
out := make([]byte, 16)
|
|
for i := range out {
|
|
out[i] = b
|
|
}
|
|
return out
|
|
}
|
|
|
|
// signerFixtures holds two independent signers for building and signing test
|
|
// objects. Signing lives in signer, which the package under test never
|
|
// imports; tests may.
|
|
type signerFixtures struct {
|
|
alice *signer.Signer
|
|
bob *signer.Signer
|
|
}
|
|
|
|
func newSignerFixtures(t *testing.T) *signerFixtures {
|
|
t.Helper()
|
|
return &signerFixtures{alice: mustSigner(t), bob: mustSigner(t)}
|
|
}
|
|
|
|
// byName returns the vector entry with the given name, failing the test if it
|
|
// is absent.
|
|
func byName(t *testing.T, vf *vectorFile, name string) *vectorEntry {
|
|
t.Helper()
|
|
for i := range vf.Vectors {
|
|
if vf.Vectors[i].Name == name {
|
|
return &vf.Vectors[i]
|
|
}
|
|
}
|
|
t.Fatalf("vector %q not found", name)
|
|
return nil
|
|
}
|