OSDN Git Service

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