Skip to content

Commit

Permalink
wire: add previous revealed secrets hashes to RS message
Browse files Browse the repository at this point in the history
For proper blaming behavior, peers who publish their secrets are blamed and
removed from the following run if all secrets were revealed, but no other
misbehavior was detected.

In order to correctly pin the blame on the peers who disrupted the mix by
initially revealing their secrets messages, this blaming will only be
triggered when a reveal secrets message is received that does not reference
any other received secrets messages.

Because the secrets message hash now commits to these previous hashes by its
signature, an additional method is added to return the commitment hash (to be
published and verified in key exchange messages) that does not hash the
previous messages (as they do not exist at the time of creating the key
exchange).

While here, an issue was discovered and corrected in the serialization of the
MixVect type. When the vector has zero length, deserializing would return
early after reading the first 0 dimension, but serialization was writing both
the count and message size dimensions. This was corrected by changing the
serialization method to return early if the count is zero.
  • Loading branch information
jrick authored and davecgh committed May 8, 2024
1 parent db7785f commit 565a184
Show file tree
Hide file tree
Showing 2 changed files with 79 additions and 9 deletions.
69 changes: 62 additions & 7 deletions wire/msgmixsecrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ type MsgMixSecrets struct {
Seed [32]byte
SlotReserveMsgs [][]byte
DCNetMsgs MixVect
SeenSecrets []chainhash.Hash

// hash records the hash of the message. It is a member of the
// message for convenience and performance, but is never automatically
Expand All @@ -43,6 +44,9 @@ func writeMixVect(op string, w io.Writer, pver uint32, vec MixVect) error {
if err != nil {
return err
}
if len(vec) == 0 {
return nil
}
err = WriteVarInt(w, pver, MixMsgSize)
if err != nil {
return err
Expand Down Expand Up @@ -138,6 +142,25 @@ func (msg *MsgMixSecrets) BtcDecode(r io.Reader, pver uint32) error {
}
msg.DCNetMsgs = dcnetMsgs

count, err := ReadVarInt(r, pver)
if err != nil {
return err
}
if count > MaxMixPeers {
msg := fmt.Sprintf("too many previous referenced messages [count %v, max %v]",
count, MaxMixPeers)
return messageError(op, ErrTooManyPrevMixMsgs, msg)
}

seen := make([]chainhash.Hash, count)
for i := range seen {
err := readElement(r, &seen[i])
if err != nil {
return err
}
}
msg.SeenSecrets = seen

return nil
}

Expand Down Expand Up @@ -173,6 +196,17 @@ func (msg *MsgMixSecrets) Hash() chainhash.Hash {
return msg.hash
}

// Commitment returns a hash committing to the contents of the reveal secrets
// message without committing to any previous seen messages or the message
// signature. This is the hash that is referenced by peers' key exchange
// messages.
func (msg *MsgMixSecrets) Commitment(h hash.Hash) chainhash.Hash {
msgCopy := *msg
msgCopy.SeenSecrets = nil
msgCopy.WriteHash(h)
return msgCopy.hash
}

// WriteHash serializes the message to a hasher and records the sum in the
// message's Hash field.
//
Expand All @@ -196,6 +230,14 @@ func (msg *MsgMixSecrets) WriteHash(h hash.Hash) {
//
// This method never errors for invalid message construction.
func (msg *MsgMixSecrets) writeMessageNoSignature(op string, w io.Writer, pver uint32) error {
// Limit to max previous messages hashes.
count := len(msg.SeenSecrets)
if count > MaxMixPeers {
msg := fmt.Sprintf("too many previous referenced messages [count %v, max %v]",
count, MaxMixPeers)
return messageError(op, ErrTooManyPrevMixMsgs, msg)
}

err := writeElements(w, &msg.Identity, &msg.SessionID, msg.Run, &msg.Seed)
if err != nil {
return err
Expand All @@ -217,6 +259,17 @@ func (msg *MsgMixSecrets) writeMessageNoSignature(op string, w io.Writer, pver u
return err
}

err = WriteVarInt(w, pver, uint64(count))
if err != nil {
return err
}
for i := range msg.SeenSecrets {
err := writeElement(w, &msg.SeenSecrets[i])
if err != nil {
return err
}
}

return nil
}

Expand All @@ -242,7 +295,7 @@ func (msg *MsgMixSecrets) MaxPayloadLength(pver uint32) uint32 {
}

// See tests for this calculation
return 54444
return 70831
}

// Pub returns the message sender's public key identity.
Expand All @@ -255,13 +308,14 @@ func (msg *MsgMixSecrets) Sig() []byte {
return msg.Signature[:]
}

// PrevMsgs returns nil. Previous messages are not needed to perform blame
// assignment, because of the assumption that all previous messages must have
// been received for a blame stage to be necessary. Additionally, a
// commitment to the secrets message is included in the key exchange, and
// future message hashes are not available at that time.
// PrevMsgs returns previously revealed secrets messages by other peers. An
// honest peer who needs to report blame assignment does not need to reference
// any previous secrets messages, and a secrets message with other referenced
// secrets is necessary to begin blame assignment. Dishonest peers who
// initially reveal their secrets without blame assignment being necessary are
// themselves removed in future runs.
func (msg *MsgMixSecrets) PrevMsgs() []chainhash.Hash {
return nil
return msg.SeenSecrets
}

// Sid returns the session ID.
Expand All @@ -287,5 +341,6 @@ func NewMsgMixSecrets(identity [33]byte, sid [32]byte, run uint32,
Seed: seed,
SlotReserveMsgs: slotReserveMsgs,
DCNetMsgs: dcNetMsgs,
SeenSecrets: make([]chainhash.Hash, 0),
}
}
19 changes: 17 additions & 2 deletions wire/msgmixsecrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"testing"

"github.com/davecgh/go-spew/spew"
"github.com/decred/dcrd/chaincfg/chainhash"
)

func newTestMixSecrets() *MsgMixSecrets {
Expand All @@ -34,7 +35,13 @@ func newTestMixSecrets() *MsgMixSecrets {
copy(m[b-0x89][:], repeat(b, 20))
}

seenRSs := make([]chainhash.Hash, 4)
for b := byte(0x8D); b < 0x91; b++ {
copy(seenRSs[b-0x8D][:], repeat(b, 32))
}

rs := NewMsgMixSecrets(id, sid, run, seed, sr, m)
rs.SeenSecrets = seenRSs
rs.Signature = sig

return rs
Expand Down Expand Up @@ -67,12 +74,18 @@ func TestMsgMixSecretsWire(t *testing.T) {
expected = append(expected, repeat(0x87, 32)...)
expected = append(expected, 0x20)
expected = append(expected, repeat(0x88, 32)...)
// Four slot reservation mixed messages (repeating 20 bytes of 0x89, 0x8a, 0x8b, 0x8c)
// Four xor dc-net mixed messages (repeating 20 bytes of 0x89, 0x8a, 0x8b, 0x8c)
expected = append(expected, 0x04, 0x14)
expected = append(expected, repeat(0x89, 20)...)
expected = append(expected, repeat(0x8a, 20)...)
expected = append(expected, repeat(0x8b, 20)...)
expected = append(expected, repeat(0x8c, 20)...)
// Four seen RSs (repeating 32 bytes of 0x8d, 0x8e, 0x8f, 0x90)
expected = append(expected, 0x04)
expected = append(expected, repeat(0x8d, 32)...)
expected = append(expected, repeat(0x8e, 32)...)
expected = append(expected, repeat(0x8f, 32)...)
expected = append(expected, repeat(0x90, 32)...)

expectedSerializationEqual(t, buf.Bytes(), expected)

Expand Down Expand Up @@ -168,7 +181,9 @@ func TestMsgMixSecretsMaxPayloadLength(t *testing.T) {
MaxMixMcount*varBytesLen(MaxMixFieldValLen) + // Unpadded SR values
uint32(VarIntSerializeSize(MaxMixMcount)) + // DC-net message count
uint32(VarIntSerializeSize(MixMsgSize)) + // DC-net message size
MaxMixMcount*MixMsgSize // DC-net messages
MaxMixMcount*MixMsgSize + // DC-net messages
uint32(VarIntSerializeSize(MaxMixPeers)) + // RS count
32*MaxMixPeers // RS hashes

tests := []struct {
name string
Expand Down

0 comments on commit 565a184

Please sign in to comment.