mirror of
https://github.com/luxfi/chains.git
synced 2026-09-24 00:26:00 +00:00
mpcvm: no ceremony primitive outside the authorized wrapper
RunSign and RunKeygen stayed exported through the authorization change, still taking the chain-name string that change removed. The wrapper became unforgeable while the thing it wraps was reachable beside it, consulting no permission, no quota and no Caller. Lowercased; the only callers were in-package. A permission entry whose chainId is a LABEL binds to no chain, exactly like an empty one, because a label never equals a base58 chain id. Counting it as bound made a stock node report five authorized chains while authorizing none -- which is the misreading the boot warning was added to prevent, reintroduced by the warning itself. Bound now means the chainId parses as a chain id. Co-authored-by: Hanzo Dev <[email protected]>
This commit is contained in:
@@ -84,3 +84,40 @@ func TestCaller_PayloadCannotNameItself(t *testing.T) {
|
||||
t.Fatal("a self-declared name must not produce a Caller")
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnboundLabelIsNotCounted is F10: an entry whose chainId is a LABEL binds to
|
||||
// no chain, exactly like an empty one, because a label never equals a base58 chain
|
||||
// id. Counting it as bound made a stock node report five authorized chains while
|
||||
// authorizing none — the precise misreading the boot warning exists to prevent.
|
||||
func TestUnboundLabelIsNotCounted(t *testing.T) {
|
||||
real := ids.GenerateTestID()
|
||||
entries := map[string]*ChainPermissions{
|
||||
"B-Chain": {ChainID: "B-Chain"}, // a label: binds to nothing
|
||||
"C-Chain": {ChainID: ""}, // empty: binds to nothing
|
||||
"X-Chain": {ChainID: real.String()}, // an actual chain id
|
||||
}
|
||||
bound := 0
|
||||
for _, p := range entries {
|
||||
if p != nil && p.ChainID != "" {
|
||||
if _, err := ids.FromString(p.ChainID); err == nil {
|
||||
bound++
|
||||
}
|
||||
}
|
||||
}
|
||||
if bound != 1 {
|
||||
t.Fatalf("bound = %d, want 1 — only an entry carrying a real chain id is bound", bound)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUnexportedCeremonyPrimitives is F6: the authorized wrapper was made
|
||||
// unforgeable while the primitive it wraps stayed exported and still took the very
|
||||
// chain-name string the change removed. A caller outside this package must have no
|
||||
// way to run a ceremony at all.
|
||||
func TestUnexportedCeremonyPrimitives(t *testing.T) {
|
||||
var vm *VM
|
||||
// These compile only because this test is IN the package. The property under
|
||||
// test is that the identifiers are lowercase; if either is re-exported, the
|
||||
// grep in CI and this comment are the record of why it must not be.
|
||||
_ = vm.runKeygen
|
||||
_ = vm.runSign
|
||||
}
|
||||
|
||||
+2
-2
@@ -161,7 +161,7 @@ func (vm *VM) Committee(ctx context.Context, height uint64) ([]party.ID, error)
|
||||
// Every participant runs this and stages an identical operation. Non-blocking
|
||||
// on the caller's behalf is the caller's business; this returns when the
|
||||
// ceremony is done.
|
||||
func (vm *VM) RunKeygen(ctx context.Context, keyID string, policy quorum.Policy, requestingChain string) (*Operation, error) {
|
||||
func (vm *VM) runKeygen(ctx context.Context, keyID string, policy quorum.Policy, requestingChain string) (*Operation, error) {
|
||||
if !policy.Valid() {
|
||||
return nil, fmt.Errorf("mpcvm: keygen for %s: undeployable policy %s", keyID, policy)
|
||||
}
|
||||
@@ -286,7 +286,7 @@ func (vm *VM) RunKeygen(ctx context.Context, keyID string, policy quorum.Policy,
|
||||
// caller's behalf: the caller knows its own signing domain (an Ethereum tx
|
||||
// hash, a bridge release digest), and a chain that re-hashes would produce
|
||||
// signatures over a preimage the caller never authorised.
|
||||
func (vm *VM) RunSign(ctx context.Context, keyID string, digest []byte, requestingChain string) (*Operation, error) {
|
||||
func (vm *VM) runSign(ctx context.Context, keyID string, digest []byte, requestingChain string) (*Operation, error) {
|
||||
if len(digest) != 32 {
|
||||
return nil, fmt.Errorf("mpcvm: sign %s: digest is %d bytes, want 32", keyID, len(digest))
|
||||
}
|
||||
|
||||
+11
-5
@@ -321,9 +321,15 @@ func (vm *VM) Initialize(
|
||||
|
||||
bound, unbound := 0, make([]string, 0, len(vm.config.AuthorizedChains))
|
||||
for name, p := range vm.config.AuthorizedChains {
|
||||
// A chainId that is not a chain id binds to nothing. The stock entries carry
|
||||
// labels ("B-Chain"), which never equal a node's base58 chain id, so counting
|
||||
// them as bound reported five authorized chains on a node where none were —
|
||||
// the exact reading this warning exists to prevent.
|
||||
if p != nil && p.ChainID != "" {
|
||||
bound++
|
||||
continue
|
||||
if _, err := ids.FromString(p.ChainID); err == nil {
|
||||
bound++
|
||||
continue
|
||||
}
|
||||
}
|
||||
unbound = append(unbound, name)
|
||||
}
|
||||
@@ -340,7 +346,7 @@ func (vm *VM) Initialize(
|
||||
// is not an identity — until an operator sets chainId, the entry grants
|
||||
// custody to nobody. Silence here would read as "custody is configured"
|
||||
// right up until the first release fails.
|
||||
vm.log.Warn("custody entries name a chain but bind to none — set chainId on each to grant it",
|
||||
vm.log.Warn("custody entries bind to no chain id — set chainId to a real chain id on each to grant it",
|
||||
log.Strings("chains", unbound),
|
||||
)
|
||||
}
|
||||
@@ -503,7 +509,7 @@ func (vm *VM) StartKeygenWithPolicy(ctx context.Context, keyID string, policy qu
|
||||
requestedBy := by.name
|
||||
ctx, cancel := context.WithTimeout(ctx, vm.sessionTimeout())
|
||||
defer cancel()
|
||||
op, err := vm.RunKeygen(ctx, keyID, policy, requestedBy)
|
||||
op, err := vm.runKeygen(ctx, keyID, policy, requestedBy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -539,7 +545,7 @@ func (vm *VM) RequestSignature(ctx context.Context, by Caller, keyID string, mes
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, vm.sessionTimeout())
|
||||
defer cancel()
|
||||
op, err := vm.RunSign(ctx, keyID, messageHash, requestingChain)
|
||||
op, err := vm.runSign(ctx, keyID, messageHash, requestingChain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user