mirror of
https://github.com/tendermint/tendermint.git
synced 2026-01-09 22:47:24 +00:00
secret connection: check for low order points (#3040)
> Implement a check for the blacklisted low order points, ala the X25519 has_small_order() function in libsodium (#3010 (comment)) resolves first half of #3010
This commit is contained in:
committed by
Anton Kaliaev
parent
0b3a87a323
commit
6dd817cbbc
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
crand "crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"io"
|
||||
@@ -28,6 +29,8 @@ const aeadSizeOverhead = 16 // overhead of poly 1305 authentication tag
|
||||
const aeadKeySize = chacha20poly1305.KeySize
|
||||
const aeadNonceSize = chacha20poly1305.NonceSize
|
||||
|
||||
var ErrSmallOrderRemotePubKey = errors.New("detected low order point from remote peer")
|
||||
|
||||
// SecretConnection implements net.Conn.
|
||||
// It is an implementation of the STS protocol.
|
||||
// See https://github.com/tendermint/tendermint/blob/0.1/docs/sts-final.pdf for
|
||||
@@ -251,6 +254,9 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3
|
||||
if err2 != nil {
|
||||
return nil, err2, true // abort
|
||||
}
|
||||
if hasSmallOrder(_remEphPub) {
|
||||
return nil, ErrSmallOrderRemotePubKey, true
|
||||
}
|
||||
return _remEphPub, nil, false
|
||||
},
|
||||
)
|
||||
@@ -266,6 +272,52 @@ func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[3
|
||||
return &_remEphPub, nil
|
||||
}
|
||||
|
||||
// use the samne blacklist as lib sodium (see https://eprint.iacr.org/2017/806.pdf for reference):
|
||||
// https://github.com/jedisct1/libsodium/blob/536ed00d2c5e0c65ac01e29141d69a30455f2038/src/libsodium/crypto_scalarmult/curve25519/ref10/x25519_ref10.c#L11-L17
|
||||
var blacklist = [][32]byte{
|
||||
// 0 (order 4)
|
||||
{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
// 1 (order 1)
|
||||
{0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
||||
// 325606250916557431795983626356110631294008115727848805560023387167927233504
|
||||
// (order 8)
|
||||
{0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3,
|
||||
0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09, 0x8d, 0xeb, 0x9c, 0x32,
|
||||
0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00},
|
||||
// 39382357235489614581723060781553021112529911719440698176882885853963445705823
|
||||
// (order 8)
|
||||
{0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1,
|
||||
0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c,
|
||||
0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57},
|
||||
// p-1 (order 2)
|
||||
{0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
|
||||
// p (=0, order 4)
|
||||
{0xed, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
|
||||
// p+1 (=1, order 1)
|
||||
{0xee, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
|
||||
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f},
|
||||
}
|
||||
|
||||
func hasSmallOrder(pubKey [32]byte) bool {
|
||||
isSmallOrderPoint := false
|
||||
for _, bl := range blacklist {
|
||||
if subtle.ConstantTimeCompare(pubKey[:], bl[:]) == 1 {
|
||||
isSmallOrderPoint = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return isSmallOrderPoint
|
||||
}
|
||||
|
||||
func deriveSecretAndChallenge(dhSecret *[32]byte, locIsLeast bool) (recvSecret, sendSecret *[aeadKeySize]byte, challenge *[32]byte) {
|
||||
hash := sha256.New
|
||||
hkdf := hkdf.New(hash, dhSecret[:], nil, []byte("TENDERMINT_SECRET_CONNECTION_KEY_AND_CHALLENGE_GEN"))
|
||||
|
||||
@@ -100,6 +100,32 @@ func TestSecretConnectionHandshake(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestShareLowOrderPubkey(t *testing.T) {
|
||||
var fooConn, barConn = makeKVStoreConnPair()
|
||||
locEphPub, _ := genEphKeys()
|
||||
|
||||
// all blacklisted low order points:
|
||||
for _, remLowOrderPubKey := range blacklist {
|
||||
_, _ = cmn.Parallel(
|
||||
func(_ int) (val interface{}, err error, abort bool) {
|
||||
_, err = shareEphPubKey(fooConn, locEphPub)
|
||||
|
||||
require.Error(t, err)
|
||||
require.Equal(t, err, ErrSmallOrderRemotePubKey)
|
||||
|
||||
return nil, nil, false
|
||||
},
|
||||
func(_ int) (val interface{}, err error, abort bool) {
|
||||
readRemKey, err := shareEphPubKey(barConn, &remLowOrderPubKey)
|
||||
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, locEphPub, readRemKey)
|
||||
|
||||
return nil, nil, false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConcurrentWrite(t *testing.T) {
|
||||
fooSecConn, barSecConn := makeSecretConnPair(t)
|
||||
fooWriteText := cmn.RandStr(dataMaxSize)
|
||||
|
||||
Reference in New Issue
Block a user