74 lines
3.2 KiB
Go
74 lines
3.2 KiB
Go
package meshvaultclient
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
seat "git.novox.be/novox/mesh-contracts/identity-and-access/go/seats/meshvault"
|
|
)
|
|
|
|
func TestSigningNodeLoginsAndOwnDrift(t *testing.T) {
|
|
pub, priv, _ := ed25519.GenerateKey(nil)
|
|
k := NewSignerKey(pub, time.Date(2026, 11, 5, 12, 0, 0, 0, time.UTC))
|
|
if got, err := SignerPublicKey(k); err != nil || !got.Equal(pub) {
|
|
t.Fatalf("the key round trips: %v", err)
|
|
}
|
|
k.Fingerprint = "sha256:00"
|
|
if _, err := SignerPublicKey(k); err == nil {
|
|
t.Error("a key with another fingerprint was taken")
|
|
}
|
|
ctx, err := SignContext(KindEnrolment)
|
|
if err != nil || ctx != "novox enrolment v1\n" {
|
|
t.Fatalf("SignContext = %q, %v", ctx, err)
|
|
}
|
|
if _, err := SignContext("other"); err == nil {
|
|
t.Error("an unknown kind has a context")
|
|
}
|
|
sig := SignMessage(priv, ctx, []byte("m"))
|
|
if s, err := VerifyBy([]Signer{{Name: SignerVault, Key: pub}}, ctx, []byte("m"), sig); err != nil || s.Name != SignerVault {
|
|
t.Errorf("VerifyBy = %v, %v", s, err)
|
|
}
|
|
other, _ := SignContext(KindAuthority)
|
|
if _, err := VerifyBy([]Signer{{Name: SignerVault, Key: pub}}, other, []byte("m"), sig); err == nil {
|
|
t.Error("a signature made for one kind verified as another's")
|
|
}
|
|
|
|
// A node's two logins: the first, the second, a rotation to the other only, a stage only with a rotation.
|
|
since := time.Date(2026, 11, 2, 1, 0, 0, 0, time.UTC)
|
|
for name, c := range map[string]struct {
|
|
l seat.NodeLoginsByNodeValue
|
|
ok bool
|
|
}{
|
|
"first login": {seat.NodeLoginsByNodeValue{Node: "g14", Login: "node.g14", Since: since}, true},
|
|
"rotating to other": {seat.NodeLoginsByNodeValue{Node: "g14", Login: "node.g14", Rotating: "node.g14--b", Stage: RotationBegun, Since: since}, true},
|
|
"rotating to itself": {seat.NodeLoginsByNodeValue{Node: "g14", Login: "node.g14", Rotating: "node.g14", Stage: RotationBegun, Since: since}, false},
|
|
"stage without rotate": {seat.NodeLoginsByNodeValue{Node: "g14", Login: "node.g14", Stage: RotationReady, Since: since}, false},
|
|
"another's login": {seat.NodeLoginsByNodeValue{Node: "g14", Login: "node.ace", Since: since}, false},
|
|
} {
|
|
if err := CheckNodeLogin(c.l); (err == nil) != c.ok {
|
|
t.Errorf("%s: %v", name, err)
|
|
}
|
|
}
|
|
if raw, err := EncodeNodeLogin(seat.NodeLoginsByNodeValue{Node: "g14", Login: "node.g14", Since: since}); err != nil {
|
|
t.Fatal(err)
|
|
} else if back, err := DecodeNodeLogin(raw); err != nil || Rotates(back) {
|
|
t.Errorf("round trip: %+v %v", back, err)
|
|
}
|
|
if _, err := DecodeNodeLogin([]byte(`{"v":9,"node":"g14","login":"node.g14","since":"` + since.Format(time.RFC3339) + `"}`)); err == nil {
|
|
t.Error("version 9 was taken")
|
|
}
|
|
if n, ok := LoginNode("node.g14--b"); !ok || n != "g14" {
|
|
t.Errorf("LoginNode = %q %v", n, ok)
|
|
}
|
|
|
|
masked := []ModuleBodies{{Module: "nats", Bodies: []Body{{ID: "nats.needs-jwt", Composed: "x " + MaskedAnotherValue}, {ID: "nats.conf", Running: MaskedAnotherValue}}}}
|
|
if own := OwnValuesDiffer(masked); len(own) != 1 || own[0] != (OwnValueDiffers{Module: "nats", Name: "jwt"}) || ValuesDiffer(masked) != 2 {
|
|
t.Errorf("OwnValuesDiffer = %v, ValuesDiffer = %d", own, ValuesDiffer(masked))
|
|
}
|
|
if !strings.Contains(MaskedAnotherValue, "another value") {
|
|
t.Error("the mask word changed")
|
|
}
|
|
}
|