OSDN Git Service

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