A composition is applied only when the controller or the operator signed it; the node agent's integration tests on an embedded bus (R6-A A9, A1, A5)

This commit is contained in:
2026-10-24 16:42:11 +02:00
parent 3303797bfb
commit b2003e7a38
15 changed files with 515 additions and 58 deletions
+19 -16
View File
@@ -12,6 +12,7 @@ package agent
import (
"bytes"
"context"
"crypto/ed25519"
"encoding/json"
"fmt"
"sort"
@@ -87,10 +88,11 @@ type Env struct {
// Kept is the resources the node last applied for a module from the declaration it kept before the switch (ADR 0539
// §9), as JSON, and whether it held the module. Nil when the node kept none.
Kept func(module string) ([]json.RawMessage, bool)
// Verify judges a composition's bytes before anything of it is read: a composition whose signer is not trusted is
// refused (ADR 0539 §3). Nil trusts the bucket, which only the controller and the operator can write.
Verify func(key string, raw []byte) error
Say func(string)
// Trusted is the public keys a composition may be signed by: the controller's and the operator's (ADR 0539 §3). A
// composition unsigned, signed by another key, or changed after it was signed is refused and nothing of it is
// applied. Nil trusts no key, so nothing is applied.
Trusted func() []ed25519.PublicKey
Say func(string)
}
// Agent follows the wants.
@@ -281,23 +283,24 @@ func (a *Agent) takeComposition(u Update) string {
return module
}
now := compNow{Raw: u.Value, Rev: u.Revision}
if a.env.Verify != nil {
if err := a.env.Verify(u.Key, u.Value); err != nil {
now.Err = fmt.Errorf("refused: %w", err)
a.comps[u.Key] = now
a.sayOnce("verify:"+u.Key, fmt.Sprintf("the composition %s is refused and nothing of it is applied: %v", u.Key, err))
return module
}
}
c, err := delivery.DecodeComposed(u.Value)
if err == nil {
var trusted []ed25519.PublicKey
if a.env.Trusted != nil {
trusted = a.env.Trusted()
}
// The signature before anything of the composition is used: its digest is what the want names.
now.C = c
now.Dig, err = delivery.ComposedDigestOf(c)
}
now.Err = err
if err != nil {
now.Dig, err = delivery.VerifyComposed(c, trusted)
if err != nil {
now.C = delivery.Composed{}
a.sayOnce("verify:"+u.Key, fmt.Sprintf("the composition %s is refused and nothing of it is applied: %v", u.Key, err))
err = fmt.Errorf("refused: %w", err)
}
} else {
a.sayOnce("decode:"+u.Key, fmt.Sprintf("the composition %s cannot be read, and nothing of it is applied: %v", u.Key, err))
}
now.Err = err
a.comps[u.Key] = now
return module
}