github.com/bytom/bytom@v1.1.2-0.20221014091027-bbcba3df6075/p2p/connection/secret_connection.go (about)

     1  package connection
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/ed25519"
     6  	crand "crypto/rand"
     7  	"crypto/sha256"
     8  	"encoding/binary"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"net"
    13  	"time"
    14  
    15  	log "github.com/sirupsen/logrus"
    16  	"github.com/bytom/bytom/crypto/ed25519/chainkd"
    17  	"golang.org/x/crypto/nacl/box"
    18  	"golang.org/x/crypto/nacl/secretbox"
    19  	"golang.org/x/crypto/ripemd160"
    20  
    21  	"github.com/tendermint/go-wire"
    22  	cmn "github.com/tendermint/tmlibs/common"
    23  )
    24  
    25  const (
    26  	dataLenSize     = 2 // uint16 to describe the length, is <= dataMaxSize
    27  	dataMaxSize     = 1024
    28  	totalFrameSize  = dataMaxSize + dataLenSize
    29  	sealedFrameSize = totalFrameSize + secretbox.Overhead
    30  	authSigMsgSize  = 100 // fixed size (length prefixed) byte arrays
    31  )
    32  
    33  type authSigMessage struct {
    34  	Key []byte
    35  	Sig []byte
    36  }
    37  
    38  // SecretConnection implements net.Conn
    39  type SecretConnection struct {
    40  	conn       io.ReadWriteCloser
    41  	recvBuffer []byte
    42  	recvNonce  *[24]byte
    43  	sendNonce  *[24]byte
    44  	remPubKey  ed25519.PublicKey
    45  	shrSecret  *[32]byte // shared secret
    46  }
    47  
    48  // MakeSecretConnection performs handshake and returns a new authenticated SecretConnection.
    49  func MakeSecretConnection(conn io.ReadWriteCloser, locPrivKey chainkd.XPrv) (*SecretConnection, error) {
    50  	locPubKey := locPrivKey.XPub().PublicKey()
    51  
    52  	// Generate ephemeral keys for perfect forward secrecy.
    53  	locEphPub, locEphPriv := genEphKeys()
    54  
    55  	// Write local ephemeral pubkey and receive one too.
    56  	// NOTE: every 32-byte string is accepted as a Curve25519 public key
    57  	// (see DJB's Curve25519 paper: http://cr.yp.to/ecdh/curve25519-20060209.pdf)
    58  	remEphPub, err := shareEphPubKey(conn, locEphPub)
    59  	if err != nil {
    60  		return nil, err
    61  	}
    62  
    63  	// Compute common shared secret.
    64  	shrSecret := computeSharedSecret(remEphPub, locEphPriv)
    65  
    66  	// Sort by lexical order.
    67  	loEphPub, hiEphPub := sort32(locEphPub, remEphPub)
    68  
    69  	// Generate nonces to use for secretbox.
    70  	recvNonce, sendNonce := genNonces(loEphPub, hiEphPub, locEphPub == loEphPub)
    71  
    72  	// Generate common challenge to sign.
    73  	challenge := genChallenge(loEphPub, hiEphPub)
    74  
    75  	// Construct SecretConnection.
    76  	sc := &SecretConnection{
    77  		conn:       conn,
    78  		recvBuffer: nil,
    79  		recvNonce:  recvNonce,
    80  		sendNonce:  sendNonce,
    81  		shrSecret:  shrSecret,
    82  	}
    83  
    84  	// Sign the challenge bytes for authentication.
    85  	locSignature := signChallenge(challenge, locPrivKey)
    86  
    87  	// Share (in secret) each other's pubkey & challenge signature
    88  	authSigMsg, err := shareAuthSignature(sc, locPubKey, locSignature)
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	remPubKey, remSignature := authSigMsg.Key, authSigMsg.Sig
    94  	if !ed25519.Verify(remPubKey, challenge[:], remSignature) {
    95  		return nil, errors.New("Challenge verification failed")
    96  	}
    97  
    98  	sc.remPubKey = remPubKey
    99  	return sc, nil
   100  }
   101  
   102  // CONTRACT: data smaller than dataMaxSize is read atomically.
   103  func (sc *SecretConnection) Read(data []byte) (n int, err error) {
   104  	if 0 < len(sc.recvBuffer) {
   105  		n_ := copy(data, sc.recvBuffer)
   106  		sc.recvBuffer = sc.recvBuffer[n_:]
   107  		return
   108  	}
   109  
   110  	sealedFrame := make([]byte, sealedFrameSize)
   111  	if _, err = io.ReadFull(sc.conn, sealedFrame); err != nil {
   112  		return
   113  	}
   114  
   115  	// decrypt the frame
   116  	frame := make([]byte, totalFrameSize)
   117  	if _, ok := secretbox.Open(frame[:0], sealedFrame, sc.recvNonce, sc.shrSecret); !ok {
   118  		return n, errors.New("Failed to decrypt SecretConnection")
   119  	}
   120  
   121  	incr2Nonce(sc.recvNonce)
   122  	chunkLength := binary.BigEndian.Uint16(frame) // read the first two bytes
   123  	if chunkLength > dataMaxSize {
   124  		return 0, errors.New("chunkLength is greater than dataMaxSize")
   125  	}
   126  
   127  	chunk := frame[dataLenSize : dataLenSize+chunkLength]
   128  	n = copy(data, chunk)
   129  	sc.recvBuffer = chunk[n:]
   130  	return
   131  }
   132  
   133  // RemotePubKey returns authenticated remote pubkey
   134  func (sc *SecretConnection) RemotePubKey() ed25519.PublicKey {
   135  	return sc.remPubKey
   136  }
   137  
   138  // Writes encrypted frames of `sealedFrameSize`
   139  // CONTRACT: data smaller than dataMaxSize is read atomically.
   140  func (sc *SecretConnection) Write(data []byte) (n int, err error) {
   141  	for 0 < len(data) {
   142  		var chunk []byte
   143  		frame := make([]byte, totalFrameSize)
   144  		if dataMaxSize < len(data) {
   145  			chunk = data[:dataMaxSize]
   146  			data = data[dataMaxSize:]
   147  		} else {
   148  			chunk = data
   149  			data = nil
   150  		}
   151  		binary.BigEndian.PutUint16(frame, uint16(len(chunk)))
   152  		copy(frame[dataLenSize:], chunk)
   153  
   154  		// encrypt the frame
   155  		sealedFrame := make([]byte, sealedFrameSize)
   156  		secretbox.Seal(sealedFrame[:0], frame, sc.sendNonce, sc.shrSecret)
   157  		incr2Nonce(sc.sendNonce)
   158  
   159  		if _, err := sc.conn.Write(sealedFrame); err != nil {
   160  			return n, err
   161  		}
   162  
   163  		n += len(chunk)
   164  	}
   165  	return
   166  }
   167  
   168  // Close implements net.Conn
   169  func (sc *SecretConnection) Close() error { return sc.conn.Close() }
   170  
   171  // LocalAddr implements net.Conn
   172  func (sc *SecretConnection) LocalAddr() net.Addr { return sc.conn.(net.Conn).LocalAddr() }
   173  
   174  // RemoteAddr implements net.Conn
   175  func (sc *SecretConnection) RemoteAddr() net.Addr { return sc.conn.(net.Conn).RemoteAddr() }
   176  
   177  // SetDeadline implements net.Conn
   178  func (sc *SecretConnection) SetDeadline(t time.Time) error { return sc.conn.(net.Conn).SetDeadline(t) }
   179  
   180  // SetReadDeadline implements net.Conn
   181  func (sc *SecretConnection) SetReadDeadline(t time.Time) error {
   182  	return sc.conn.(net.Conn).SetReadDeadline(t)
   183  }
   184  
   185  // SetWriteDeadline implements net.Conn
   186  func (sc *SecretConnection) SetWriteDeadline(t time.Time) error {
   187  	return sc.conn.(net.Conn).SetWriteDeadline(t)
   188  }
   189  
   190  func computeSharedSecret(remPubKey, locPrivKey *[32]byte) (shrSecret *[32]byte) {
   191  	shrSecret = new([32]byte)
   192  	box.Precompute(shrSecret, remPubKey, locPrivKey)
   193  	return
   194  }
   195  
   196  func genChallenge(loPubKey, hiPubKey *[32]byte) (challenge *[32]byte) {
   197  	return hash32(append(loPubKey[:], hiPubKey[:]...))
   198  }
   199  
   200  // increment nonce big-endian by 2 with wraparound.
   201  func incr2Nonce(nonce *[24]byte) {
   202  	incrNonce(nonce)
   203  	incrNonce(nonce)
   204  }
   205  
   206  // increment nonce big-endian by 1 with wraparound.
   207  func incrNonce(nonce *[24]byte) {
   208  	for i := 23; 0 <= i; i-- {
   209  		nonce[i]++
   210  		if nonce[i] != 0 {
   211  			return
   212  		}
   213  	}
   214  }
   215  
   216  func genEphKeys() (ephPub, ephPriv *[32]byte) {
   217  	var err error
   218  	ephPub, ephPriv, err = box.GenerateKey(crand.Reader)
   219  	if err != nil {
   220  		log.Panic("Could not generate ephemeral keypairs")
   221  	}
   222  	return
   223  }
   224  
   225  func genNonces(loPubKey, hiPubKey *[32]byte, locIsLo bool) (*[24]byte, *[24]byte) {
   226  	nonce1 := hash24(append(loPubKey[:], hiPubKey[:]...))
   227  	nonce2 := new([24]byte)
   228  	copy(nonce2[:], nonce1[:])
   229  	nonce2[len(nonce2)-1] ^= 0x01
   230  	if locIsLo {
   231  		return nonce1, nonce2
   232  	}
   233  	return nonce2, nonce1
   234  }
   235  
   236  func signChallenge(challenge *[32]byte, locPrivKey chainkd.XPrv) []byte {
   237  	return locPrivKey.Sign(challenge[:])
   238  }
   239  
   240  func shareAuthSignature(sc *SecretConnection, pubKey, signature []byte) (*authSigMessage, error) {
   241  	var recvMsg authSigMessage
   242  
   243  	wTask := func(i int) (res interface{}, err error, abort bool) {
   244  		msgBytes := wire.BinaryBytes(authSigMessage{pubKey, signature})
   245  		_, err = sc.Write(msgBytes)
   246  		return nil, err, false
   247  	}
   248  
   249  	rTask := func(i int) (res interface{}, err error, abort bool) {
   250  		readBuffer := make([]byte, authSigMsgSize)
   251  		_, err = io.ReadFull(sc, readBuffer)
   252  		if err != nil {
   253  			return nil, err, false
   254  		}
   255  
   256  		n := int(0) // not used.
   257  		recvMsg = wire.ReadBinary(authSigMessage{}, bytes.NewBuffer(readBuffer), authSigMsgSize, &n, &err).(authSigMessage)
   258  		return nil, err, false
   259  	}
   260  
   261  	trs, ok := cmn.Parallel(wTask, rTask)
   262  	if !ok {
   263  		return nil, errors.New("Parallel task run failed")
   264  	}
   265  
   266  	for i := 0; i < 2; i++ {
   267  		res, ok := trs.LatestResult(i)
   268  		if !ok {
   269  			return nil, fmt.Errorf("Task %d did not complete", i)
   270  		}
   271  
   272  		if res.Error != nil {
   273  			return nil, fmt.Errorf("Task %d should not has error but god %v", i, res.Error)
   274  		}
   275  	}
   276  
   277  	return &recvMsg, nil
   278  }
   279  
   280  func shareEphPubKey(conn io.ReadWriteCloser, locEphPub *[32]byte) (remEphPub *[32]byte, err error) {
   281  	var err1, err2 error
   282  	cmn.Parallel(
   283  		func(i int) (res interface{}, err error, abort bool) {
   284  			_, err = conn.Write(locEphPub[:])
   285  			return nil, err, false
   286  		},
   287  		func(i int) (res interface{}, err error, abort bool) {
   288  			remEphPub = new([32]byte)
   289  			_, err = io.ReadFull(conn, remEphPub[:])
   290  			return nil, err, false
   291  		},
   292  	)
   293  
   294  	// TODO:
   295  	if err1 != nil {
   296  		return nil, err1
   297  	}
   298  	if err2 != nil {
   299  		return nil, err2
   300  	}
   301  	return remEphPub, nil
   302  }
   303  
   304  func sort32(foo, bar *[32]byte) (*[32]byte, *[32]byte) {
   305  	if bytes.Compare(foo[:], bar[:]) < 0 {
   306  		return foo, bar
   307  	}
   308  	return bar, foo
   309  }
   310  
   311  // sha256
   312  func hash32(input []byte) (res *[32]byte) {
   313  	hasher := sha256.New()
   314  	hasher.Write(input) // does not error
   315  	resSlice := hasher.Sum(nil)
   316  	res = new([32]byte)
   317  	copy(res[:], resSlice)
   318  	return
   319  }
   320  
   321  // We only fill in the first 20 bytes with ripemd160
   322  func hash24(input []byte) (res *[24]byte) {
   323  	hasher := ripemd160.New()
   324  	hasher.Write(input) // does not error
   325  	resSlice := hasher.Sum(nil)
   326  	res = new([24]byte)
   327  	copy(res[:], resSlice)
   328  	return
   329  }