diff --git a/go/delivery/enrolment.go b/go/delivery/enrolment.go index 82166e0..d93eced 100644 --- a/go/delivery/enrolment.go +++ b/go/delivery/enrolment.go @@ -58,6 +58,9 @@ type Enrolment struct { Issued time.Time `json:"issued"` Expires time.Time `json:"expires"` KeysDigest string `json:"keys-digest,omitempty"` + // OverlayKey is the machine's tunnel public key, given at issue (the operator's decision of 2026-11-02 01:05), + // base64 of 32 bytes: at every stage, since the node enters placement with it on the operator's yes. + OverlayKey string `json:"overlay-key"` } // EnrolmentAddress is one key `address.
`: the node the address is reserved for, and nothing else. @@ -101,6 +104,9 @@ func (e Enrolment) check() error { if strings.TrimSpace(e.Why) == "" || strings.TrimSpace(e.Caller) == "" { return fmt.Errorf("delivery: the enrolment of %s names no why or no caller", e.Node) } + if k, err := base64.StdEncoding.DecodeString(e.OverlayKey); err != nil || len(k) != 32 { + return fmt.Errorf("delivery: the enrolment of %s names no tunnel key of 32 bytes in base64", e.Node) + } switch e.Stage { case EnrolmentAsked, EnrolmentLive: if e.KeysDigest != "" { diff --git a/go/delivery/noderecord_test.go b/go/delivery/noderecord_test.go index 2cf9ec7..1aadc1c 100644 --- a/go/delivery/noderecord_test.go +++ b/go/delivery/noderecord_test.go @@ -1,6 +1,7 @@ package delivery import ( + "encoding/base64" "reflect" "regexp" "strings" @@ -72,11 +73,13 @@ func TestNodeRecord(t *testing.T) { // 774/S35/AC1: an enrolment key and an address key read back equal, and neither names a field like a secret: the // grants part reads them (ADR 0587 ยง2). issued := time.Date(2026, 11, 2, 1, 0, 0, 0, time.UTC) + // The tunnel key given at issue, at every stage. + wg := base64.StdEncoding.EncodeToString(make([]byte, 32)) for _, e := range []Enrolment{ {Node: "lab", Address: "10.0.0.7", Why: "a new machine", Caller: "mesh-enrolment", Ask: "a1", Stage: EnrolmentLive, - Issued: issued, Expires: issued.Add(DefaultTokenLife)}, + OverlayKey: wg, Issued: issued, Expires: issued.Add(DefaultTokenLife)}, {Node: "lab", Address: "10.0.0.7", Why: "a new machine", Caller: "mesh-enrolment", Ask: "a1", Stage: EnrolmentRedeeming, - Issued: issued, Expires: issued.Add(MostTokenLife), KeysDigest: KeysDigest("pub", "overlay", "sealing", "serving")}, + OverlayKey: wg, Issued: issued, Expires: issued.Add(MostTokenLife), KeysDigest: KeysDigest("pub", "overlay", "sealing", "serving")}, } { raw, err := EncodeEnrolment(e) if err != nil { @@ -95,11 +98,15 @@ func TestNodeRecord(t *testing.T) { } for name, e := range map[string]Enrolment{ "keys before redeem": {Node: "lab", Address: "10.0.0.7", Why: "w", Caller: "c", Stage: EnrolmentLive, - Issued: issued, Expires: issued.Add(time.Hour), KeysDigest: KeysDigest("a", "b", "c", "d")}, + OverlayKey: wg, Issued: issued, Expires: issued.Add(time.Hour), KeysDigest: KeysDigest("a", "b", "c", "d")}, "no keys at redeem": {Node: "lab", Address: "10.0.0.7", Why: "w", Caller: "c", Stage: EnrolmentRedeeming, - Issued: issued, Expires: issued.Add(time.Hour)}, - "no address": {Node: "lab", Why: "w", Caller: "c", Stage: EnrolmentAsked, Issued: issued, Expires: issued.Add(time.Hour)}, - "other stage": {Node: "lab", Address: "10.0.0.7", Why: "w", Caller: "c", Stage: "spent", Issued: issued, + OverlayKey: wg, Issued: issued, Expires: issued.Add(time.Hour)}, + "no address": {Node: "lab", Why: "w", Caller: "c", Stage: EnrolmentAsked, OverlayKey: wg, Issued: issued, Expires: issued.Add(time.Hour)}, + "no tunnel key": {Node: "lab", Address: "10.0.0.7", Why: "w", Caller: "c", Stage: EnrolmentAsked, Issued: issued, + Expires: issued.Add(time.Hour)}, + "a tunnel key not of 32 bytes": {Node: "lab", Address: "10.0.0.7", Why: "w", Caller: "c", Stage: EnrolmentAsked, + OverlayKey: base64.StdEncoding.EncodeToString(make([]byte, 31)), Issued: issued, Expires: issued.Add(time.Hour)}, + "other stage": {Node: "lab", Address: "10.0.0.7", Why: "w", Caller: "c", Stage: "spent", OverlayKey: wg, Issued: issued, Expires: issued.Add(time.Hour)}, } { if _, err := EncodeEnrolment(e); err == nil {