diff --git a/mixing/dcnet.go b/mixing/dcnet.go index 23130cd00a..c242d7014e 100644 --- a/mixing/dcnet.go +++ b/mixing/dcnet.go @@ -1,3 +1,7 @@ +// Copyright (c) 2019-2024 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + package mixing import ( @@ -18,11 +22,11 @@ func SRMixPads(kp [][]byte, my uint32) []*big.Int { partialPad := new(big.Int) for j := uint32(0); int(j) < len(kp); j++ { pads[j] = new(big.Int) + binary.LittleEndian.PutUint64(scratch, uint64(j)+1) for i := uint32(0); int(i) < len(kp); i++ { if my == i { continue } - binary.LittleEndian.PutUint64(scratch, uint64(j)+1) h.Reset() h.Write(kp[i]) h.Write(scratch) @@ -151,7 +155,7 @@ func DCMixPads(kp []wire.MixVect, my uint32) Vec { if i == int(my) { continue } - pads.Xor(pads, (Vec)(kp[i])) + pads.Xor(pads, Vec(kp[i])) } return pads } diff --git a/mixing/errors.go b/mixing/errors.go index 3abbcf645d..579f264445 100644 --- a/mixing/errors.go +++ b/mixing/errors.go @@ -10,21 +10,19 @@ import ( ) var ( - ErrInvalidPROrder = errors.New("invalid pair request order") + errInvalidPROrder = errors.New("invalid pair request order") - ErrInvalidSessionID = errors.New("invalid session ID") + errInvalidSessionID = errors.New("invalid session ID") ) +// DecapsulateError identifies the unmixed peer position of a peer who +// submitted an undecryptable ciphertext. type DecapsulateError struct { SubmittingIndex uint32 - Err error } +// Error satisifies the error interface. func (e *DecapsulateError) Error() string { return fmt.Sprintf("decapsulate failure of ciphertext by peer %d", e.SubmittingIndex) } - -func (e *DecapsulateError) Unwrap() error { - return e.Err -} diff --git a/mixing/go.mod b/mixing/go.mod index 94dd5e7ca6..28f032fcd0 100644 --- a/mixing/go.mod +++ b/mixing/go.mod @@ -3,29 +3,17 @@ module github.com/decred/dcrd/mixing go 1.17 require ( - decred.org/cspp/v2 v2.1.1-0.20240506180342-3c816721a1d8 github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a - github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/chaincfg/chainhash v1.0.4 github.com/decred/dcrd/chaincfg/v3 v3.2.0 github.com/decred/dcrd/crypto/blake256 v1.0.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 - github.com/decred/dcrd/dcrutil/v4 v4.0.1 - github.com/decred/dcrd/txscript/v4 v4.1.0 github.com/decred/dcrd/wire v1.6.0 - github.com/decred/slog v1.2.0 golang.org/x/crypto v0.7.0 ) require ( - github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 // indirect - github.com/dchest/siphash v1.2.3 // indirect - github.com/decred/base58 v1.0.5 // indirect - github.com/decred/dcrd/crypto/ripemd160 v1.0.2 // indirect - github.com/decred/dcrd/dcrec v1.0.1 // indirect - github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect - golang.org/x/sync v0.7.0 // indirect golang.org/x/sys v0.6.0 // indirect lukechampine.com/blake3 v1.2.1 // indirect ) diff --git a/mixing/go.sum b/mixing/go.sum index 38a840328a..1742c4f78a 100644 --- a/mixing/go.sum +++ b/mixing/go.sum @@ -1,39 +1,17 @@ -decred.org/cspp/v2 v2.1.1-0.20240506180342-3c816721a1d8 h1:sMvVzvKkxl8S/kjSNV7o7HM3TO7hdVlZvkf8SZpXAYE= -decred.org/cspp/v2 v2.1.1-0.20240506180342-3c816721a1d8/go.mod h1:9nO3bfvCheOPIFZw5f6sRQ42CjBFB5RKSaJ9Iq6G4MA= -github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= -github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a h1:clYxJ3Os0EQUKDDVU8M0oipllX0EkuFNBfhVQuIfyF0= github.com/companyzero/sntrup4591761 v0.0.0-20220309191932-9e0f3af2f07a/go.mod h1:z/9Ck1EDixEbBbZ2KH2qNHekEmDLTOZ+FyoIPWWSVOI= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= -github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= -github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= -github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= github.com/decred/dcrd/chaincfg/chainhash v1.0.4 h1:zRCv6tdncLfLTKYqu7hrXvs7hW+8FO/NvwoFvGsrluU= github.com/decred/dcrd/chaincfg/chainhash v1.0.4/go.mod h1:hA86XxlBWwHivMvxzXTSD0ZCG/LoYsFdWnCekkTMCqY= github.com/decred/dcrd/chaincfg/v3 v3.2.0 h1:6WxA92AGBkycEuWvxtZMvA76FbzbkDRoK8OGbsR2muk= github.com/decred/dcrd/chaincfg/v3 v3.2.0/go.mod h1:2rHW1TKyFmwZTVBLoU/Cmf0oxcpBjUEegbSlBfrsriI= github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= -github.com/decred/dcrd/crypto/ripemd160 v1.0.2 h1:TvGTmUBHDU75OHro9ojPLK+Yv7gDl2hnUvRocRCjsys= -github.com/decred/dcrd/crypto/ripemd160 v1.0.2/go.mod h1:uGfjDyePSpa75cSQLzNdVmWlbQMBuiJkvXw/MNKRY4M= -github.com/decred/dcrd/dcrec v1.0.1 h1:gDzlndw0zYxM5BlaV17d7ZJV6vhRe9njPBFeg4Db2UY= -github.com/decred/dcrd/dcrec v1.0.1/go.mod h1:CO+EJd8eHFb8WHa84C7ZBkXsNUIywaTHb+UAuI5uo6o= -github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 h1:l/lhv2aJCUignzls81+wvga0TFlyoZx8QxRMQgXpZik= -github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3/go.mod h1:AKpV6+wZ2MfPRJnTbQ6NPgWrKzbe9RCIlCF/FKzMtM8= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 h1:8UrgZ3GkP4i/CLijOJx79Yu+etlyjdBU4sfcs2WYQMs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= -github.com/decred/dcrd/dcrutil/v4 v4.0.1 h1:E+d2TNbpOj0f1L9RqkZkEm1QolFjajvkzxWC5WOPf1s= -github.com/decred/dcrd/dcrutil/v4 v4.0.1/go.mod h1:7EXyHYj8FEqY+WzMuRkF0nh32ueLqhutZDoW4eQ+KRc= -github.com/decred/dcrd/txscript/v4 v4.1.0 h1:uEdcibIOl6BuWj3AqmXZ9xIK/qbo6lHY9aNk29FtkrU= -github.com/decred/dcrd/txscript/v4 v4.1.0/go.mod h1:OVguPtPc4YMkgssxzP8B6XEMf/J3MB6S1JKpxgGQqi0= github.com/decred/dcrd/wire v1.6.0 h1:YOGwPHk4nzGr6OIwUGb8crJYWDiVLpuMxfDBCCF7s/o= github.com/decred/dcrd/wire v1.6.0/go.mod h1:XQ8Xv/pN/3xaDcb7sH8FBLS9cdgVctT7HpBKKGsIACk= -github.com/decred/slog v1.2.0 h1:soHAxV52B54Di3WtKLfPum9OFfWqwtf/ygf9njdfnPM= -github.com/decred/slog v1.2.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= -github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/jrick/wsrpc/v2 v2.3.5/go.mod h1:7oBeDM/xMF6Yqy4GDAjpppuOf1hm6lWsaG3EaMrm+aA= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= @@ -51,13 +29,10 @@ golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= -golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ= diff --git a/mixing/keyagreement.go b/mixing/keyagreement.go index 7a4e7e09e3..f36c3b5c5b 100644 --- a/mixing/keyagreement.go +++ b/mixing/keyagreement.go @@ -52,14 +52,14 @@ type KX struct { } // NewKX generates a mixing identity's public and private keys for a interactive -// key exchange, with randomness read from a run's PRNG. -func NewKX(prng io.Reader) (*KX, error) { - ecdhPublic, ecdhPrivate, err := generateSecp256k1(prng) +// key exchange, with randomness read from a run's CSPRNG. +func NewKX(csprng io.Reader) (*KX, error) { + ecdhPublic, ecdhPrivate, err := generateSecp256k1(csprng) if err != nil { return nil, err } - pqPublic, pqPrivate, err := sntrup4591761.GenerateKey(prng) + pqPublic, pqPrivate, err := sntrup4591761.GenerateKey(csprng) if err != nil { return nil, err } @@ -89,7 +89,7 @@ func (kx *KX) pqSharedKey(ciphertext *PQCiphertext) ([]byte, error) { // Encapsulate performs encapsulation for sntrup4591761 key exchanges with each // other peer in the DC-net. It populates the PQCleartexts field of kx and -// return encrypted cyphertexts of these shared keys. +// returns encrypted cyphertexts of these shared keys. // // Encapsulation in the DC-net requires randomness from a CSPRNG seeded by a // committed secret; blame assignment is not possible otherwise. @@ -117,6 +117,8 @@ type RevealedKeys struct { MyIndex uint32 } +// SharedSecrets is a return value for the KX.SharedSecrets method, housing +// the slot reservation and XOR DC-Net shared secrets between two peers. type SharedSecrets struct { SRSecrets [][][]byte DCSecrets [][]wire.MixVect @@ -145,7 +147,6 @@ func (kx *KX) SharedSecrets(k *RevealedKeys, sid []byte, run uint32, mcounts []u mtot += mcounts[i] } - h := blake256.New() s.SRSecrets = make([][][]byte, mcount) s.DCSecrets = make([][]wire.MixVect, mcount) @@ -164,12 +165,11 @@ func (kx *KX) SharedSecrets(k *RevealedKeys, sid []byte, run uint32, mcounts []u if err != nil { err := &DecapsulateError{ SubmittingIndex: peer, - Err: err, } return s, err } - // XOR x25519 and both sntrup4591761 keys into a single + // XOR ECDH and both sntrup4591761 keys into a single // shared key. If sntrup4591761 is discovered to be // broken in the future, the security only reduces to // that of x25519. @@ -222,10 +222,8 @@ func (kx *KX) SharedSecrets(k *RevealedKeys, sid []byte, run uint32, mcounts []u } binary.LittleEndian.PutUint32(seedCounterBytes, seedCounter) - h.Reset() - h.Write(prngSeedPreimage) - prngSeed := h.Sum(nil) - prng := chacha20prng.New(prngSeed, nonce) + prngSeed := blake256.Sum256(prngSeedPreimage) + prng := chacha20prng.New(prngSeed[:], nonce) s.SRSecrets[i][m] = prng.Next(32) s.DCSecrets[i][m] = wire.MixVect(randVec(mtot, prng)) diff --git a/mixing/message.go b/mixing/message.go index 7b1c0b37ab..5dde451f62 100644 --- a/mixing/message.go +++ b/mixing/message.go @@ -1,4 +1,4 @@ -// Copyright (c) 2023 The Decred developers +// Copyright (c) 2023-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -11,11 +11,11 @@ import ( "github.com/decred/dcrd/wire" ) -// Message is a mixing message. In addition to the implementing wire encoding, +// Message is a mixing message. In addition to implementing wire encoding, // these messages are signed by an ephemeral mixing participant identity, // declare the previous messages that have been observed by a peer in a mixing -// session, and include expiry information to increase resilience to -// replay and denial-of-service attacks. +// session, and include expiry information to increase resilience to replay +// and denial-of-service attacks. // // All mixing messages satisify this interface, however, the pair request // message returns nil for some fields that do not apply, as it is the first diff --git a/mixing/scriptclass.go b/mixing/scriptclass.go index 849d67d8d4..7264be561f 100644 --- a/mixing/scriptclass.go +++ b/mixing/scriptclass.go @@ -1,3 +1,7 @@ +// Copyright (c) 2023-2024 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + package mixing // ScriptClass describes the type and format of scripts that can be used for diff --git a/mixing/sid.go b/mixing/sid.go index 5f33731c53..7394bf60cd 100644 --- a/mixing/sid.go +++ b/mixing/sid.go @@ -86,9 +86,10 @@ func (s *sortPRs) Swap(i, j int) { s.prXorSIDs[i], s.prXorSIDs[j] = s.prXorSIDs[j], s.prXorSIDs[i] } -// ValidateSession checks whether the original peer order unmixed order of a key -// exchange's pair request hashes is valid, and for a run-0 session starting at -// epoch. +// ValidateSession checks whether the original unmixed peer order of a key +// exchange's pair request hashes is validly sorted for the session ID, and +// for a run-0 KE, also checks that the session hash is derived from the +// specified pair requests and epoch. func ValidateSession(ke *wire.MsgMixKeyExchange) error { h := make([]chainhash.Hash, len(ke.SeenPRs)) copy(h, ke.SeenPRs) @@ -102,7 +103,7 @@ func ValidateSession(ke *wire.MsgMixKeyExchange) error { return bytes.Compare(h[i][:], h[j][:]) == -1 }) if !sorted { - return ErrInvalidPROrder + return errInvalidPROrder } // If this is a run-0 KE, validate the session hash. @@ -113,7 +114,7 @@ func ValidateSession(ke *wire.MsgMixKeyExchange) error { }) derivedSID := deriveSessionID(h, ke.Epoch) if derivedSID != ke.SessionID { - return ErrInvalidSessionID + return errInvalidSessionID } } diff --git a/mixing/sid_test.go b/mixing/sid_test.go index 2382d3dc5a..8225cb0683 100644 --- a/mixing/sid_test.go +++ b/mixing/sid_test.go @@ -84,15 +84,15 @@ func TestInvalidSession(t *testing.T) { } ke.SessionID[16] ^= 0xFF - if err := ValidateSession(ke); !errors.Is(err, ErrInvalidSessionID) { + if err := ValidateSession(ke); !errors.Is(err, errInvalidSessionID) { t.Errorf("ValidateSession unexpected error, got %v, want %v", - err, ErrInvalidSessionID) + err, errInvalidSessionID) } ke.SessionID = sid ke.SeenPRs[0], ke.SeenPRs[1] = ke.SeenPRs[1], ke.SeenPRs[0] - if err := ValidateSession(ke); !errors.Is(err, ErrInvalidPROrder) { + if err := ValidateSession(ke); !errors.Is(err, errInvalidPROrder) { t.Errorf("ValidateSession unexpected error, got %v, want %v", - err, ErrInvalidPROrder) + err, errInvalidPROrder) } } diff --git a/mixing/signatures.go b/mixing/signatures.go index 85a312d174..ef0006a771 100644 --- a/mixing/signatures.go +++ b/mixing/signatures.go @@ -33,6 +33,7 @@ func SignMessage(m Signed, priv *secp256k1.PrivateKey) error { if err != nil { return err } + // XXX: A SetSig method or similar would be less janky. copy(m.Sig(), sig) return nil } diff --git a/mixing/vec.go b/mixing/vec.go index a98e6d7905..2293057a08 100644 --- a/mixing/vec.go +++ b/mixing/vec.go @@ -1,13 +1,18 @@ +// Copyright (c) 2019-2024 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + package mixing import ( - "bytes" "fmt" "strings" "github.com/decred/dcrd/mixing/internal/chacha20prng" ) +// Msize is the size of the message being mixed. This is the size of a +// HASH160, which allows mixes to be create either all P2PKH or P2SH outputs. const Msize = 20 // Vec is a N-element vector of Msize []byte messages. @@ -27,19 +32,13 @@ func (v Vec) Equals(other Vec) bool { return false } for i := range other { - if !bytes.Equal((v)[i][:], other[i][:]) { + if v[i] != other[i] { return false } } return true } -// M returns the i'th message of the vector. -// XXX: still necessary? -// func (v Vec) M(i uint32) []byte { -// return v[i][:] -// } - func (v Vec) String() string { b := new(strings.Builder) b.Grow(2 + len(v)*(2*Msize+1))