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 }