Files
zooqueen 861db0f621 a request nobody hears is not a quorum problem, and a secret needs a way in
Two halves of opening a secret were missing, and both showed up the first time
this ran against the real ring rather than an in-process one.

The request reached no party. Parties subscribed to "mpc.reveal_request.*" and
the requester published to "mpc.reveal_request.<session>". This transport
delivers by exact map lookup on the topic — it has no wildcards — so the
subscription matched nothing, every node stayed silent, and the opening failed
as "0 of 2 answered" with all three shares present and nobody having been asked.
That reads like a quorum fault and is a routing one. Both sides name one literal
now; the session rides in the body, where a reader wants it anyway. The pattern
came from the NATS-era topics next door, where wildcards do work.

And nothing could seal. mpcrek requires a sealed root to bring and no tool in
the estate produced one, so the protocol had no beginning. reveal.Seal takes the
published group key and a secret — no share, no node, no round, because writing
to the committee needs none of that — and `mpcd seal` is that from a shell, with
the secret on stdin so it never becomes an argument other processes can read.

Sealing to the identity is refused. It decodes as a valid point, so the decoder
lets it through, and every answer verifies against it: a secret sealed there is
readable by anyone. A test asked for that and the first version did not do it.
2026-08-21 00:57:48 -07:00

116 lines
3.0 KiB
Go

// Copyright © 2026 Lux Industries Inc. All rights reserved.
package reveal
import (
"bytes"
"errors"
"testing"
"github.com/luxfi/threshold/pkg/math/curve"
)
// The point of Seal is that it needs nothing but the published key. This holds
// it to that: the sealer is given 32 bytes and no share, no store and no node,
// and the committee opens what it wrote.
func TestASealerWithOnlyThePublishedKeyWritesWhatTheCommitteeOpens(t *testing.T) {
stores, cfg := committee(t, 2)
published, err := cfg.PublicKey.MarshalBinary()
if err != nil {
t.Fatal(err)
}
if len(published) != PublicKeySize {
t.Fatalf("a published group key is %d bytes, want %d", len(published), PublicKeySize)
}
secret := []byte("the root key, which no one node may hold")
sealed, err := Seal(published, secret)
if err != nil {
t.Fatalf("seal: %v", err)
}
if bytes.Contains(sealed, secret) {
t.Fatal("the ciphertext carries the secret in the clear")
}
var answers [][]byte
var combiner *shelf
for _, s := range stores {
a, err := Answer(s, testOrg, testWallet, sealed)
if err != nil {
t.Fatalf("answer: %v", err)
}
answers = append(answers, a)
combiner = s
}
got, err := Combine(combiner, testOrg, testWallet, sealed, answers)
if err != nil {
t.Fatalf("combine: %v", err)
}
if !bytes.Equal(got, secret) {
t.Fatalf("opened %q, want %q", got, secret)
}
}
// Two seals of one secret share no bytes. Encrypt draws a fresh scalar each
// time, so a repeated enrolment does not announce that it repeated.
func TestSealingTwiceWritesTwoDifferentThings(t *testing.T) {
_, cfg := committee(t, 2)
published, err := cfg.PublicKey.MarshalBinary()
if err != nil {
t.Fatal(err)
}
secret := []byte("same secret, twice")
a, err := Seal(published, secret)
if err != nil {
t.Fatal(err)
}
b, err := Seal(published, secret)
if err != nil {
t.Fatal(err)
}
if bytes.Equal(a, b) {
t.Fatal("two seals of one secret are identical")
}
}
// A key that is not a point never becomes one. The identity matters most: every
// answer verifies against it, so a secret sealed there is readable by anyone,
// and the decoder is where that has to stop.
func TestSealRefusesAnythingThatIsNotAGroupKey(t *testing.T) {
identity, err := curve.Ed25519{}.NewPoint().MarshalBinary()
if err != nil {
t.Fatal(err)
}
for _, c := range []struct {
name string
key []byte
}{
{"identity", identity},
{"off the curve", bytes.Repeat([]byte{0xff}, PublicKeySize)},
{"too short", make([]byte, PublicKeySize-1)},
{"too long", make([]byte, PublicKeySize+1)},
{"nothing", nil},
} {
t.Run(c.name, func(t *testing.T) {
if _, err := Seal(c.key, []byte("secret")); !errors.Is(err, ErrBadPublicKey) {
t.Fatalf("err = %v, want ErrBadPublicKey", err)
}
})
}
}
func TestSealRefusesAnEmptySecret(t *testing.T) {
_, cfg := committee(t, 2)
published, err := cfg.PublicKey.MarshalBinary()
if err != nil {
t.Fatal(err)
}
if _, err := Seal(published, nil); err == nil {
t.Fatal("sealed nothing without complaint")
}
}