Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
G
Geth-Modification
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
张蕾
Geth-Modification
Commits
51e01cce
Commit
51e01cce
authored
Feb 27, 2015
by
Felix Lange
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
p2p: encrypted and authenticated RLPx frame I/O
parent
936dd0f3
Changes
4
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
197 additions
and
172 deletions
+197
-172
handshake.go
p2p/handshake.go
+100
-57
handshake_test.go
p2p/handshake_test.go
+21
-79
rlpx.go
p2p/rlpx.go
+47
-19
rlpx_test.go
p2p/rlpx_test.go
+29
-17
No files found.
p2p/handshake.go
View file @
51e01cce
This diff is collapsed.
Click to expand it.
p2p/handshake_test.go
View file @
51e01cce
...
...
@@ -2,8 +2,6 @@ package p2p
import
(
"bytes"
"crypto/ecdsa"
"crypto/rand"
"net"
"reflect"
"testing"
...
...
@@ -69,102 +67,46 @@ func TestSharedSecret(t *testing.T) {
}
}
func
TestCryptoHandshake
(
t
*
testing
.
T
)
{
testCryptoHandshake
(
newkey
(),
newkey
(),
nil
,
t
)
}
func
TestCryptoHandshakeWithToken
(
t
*
testing
.
T
)
{
sessionToken
:=
make
([]
byte
,
shaLen
)
rand
.
Read
(
sessionToken
)
testCryptoHandshake
(
newkey
(),
newkey
(),
sessionToken
,
t
)
}
func
testCryptoHandshake
(
prv0
,
prv1
*
ecdsa
.
PrivateKey
,
sessionToken
[]
byte
,
t
*
testing
.
T
)
{
var
err
error
// pub0 := &prv0.PublicKey
pub1
:=
&
prv1
.
PublicKey
// pub0s := crypto.FromECDSAPub(pub0)
pub1s
:=
crypto
.
FromECDSAPub
(
pub1
)
// simulate handshake by feeding output to input
// initiator sends handshake 'auth'
auth
,
initNonce
,
randomPrivKey
,
err
:=
authMsg
(
prv0
,
pub1s
,
sessionToken
)
if
err
!=
nil
{
t
.
Errorf
(
"%v"
,
err
)
}
// t.Logf("-> %v", hexkey(auth))
// receiver reads auth and responds with response
response
,
remoteRecNonce
,
remoteInitNonce
,
_
,
remoteRandomPrivKey
,
remoteInitRandomPubKey
,
err
:=
authResp
(
auth
,
sessionToken
,
prv1
)
if
err
!=
nil
{
t
.
Errorf
(
"%v"
,
err
)
}
// t.Logf("<- %v\n", hexkey(response))
// initiator reads receiver's response and the key exchange completes
recNonce
,
remoteRandomPubKey
,
_
,
err
:=
completeHandshake
(
response
,
prv0
)
if
err
!=
nil
{
t
.
Errorf
(
"completeHandshake error: %v"
,
err
)
}
// now both parties should have the same session parameters
initSessionToken
,
err
:=
newSession
(
initNonce
,
recNonce
,
randomPrivKey
,
remoteRandomPubKey
)
if
err
!=
nil
{
t
.
Errorf
(
"newSession error: %v"
,
err
)
}
recSessionToken
,
err
:=
newSession
(
remoteInitNonce
,
remoteRecNonce
,
remoteRandomPrivKey
,
remoteInitRandomPubKey
)
if
err
!=
nil
{
t
.
Errorf
(
"newSession error: %v"
,
err
)
}
// fmt.Printf("\nauth (%v) %x\n\nresp (%v) %x\n\n", len(auth), auth, len(response), response)
// fmt.Printf("\nauth %x\ninitNonce %x\nresponse%x\nremoteRecNonce %x\nremoteInitNonce %x\nremoteRandomPubKey %x\nrecNonce %x\nremoteInitRandomPubKey %x\ninitSessionToken %x\n\n", auth, initNonce, response, remoteRecNonce, remoteInitNonce, remoteRandomPubKey, recNonce, remoteInitRandomPubKey, initSessionToken)
if
!
bytes
.
Equal
(
initNonce
,
remoteInitNonce
)
{
t
.
Errorf
(
"nonces do not match"
)
}
if
!
bytes
.
Equal
(
recNonce
,
remoteRecNonce
)
{
t
.
Errorf
(
"receiver nonces do not match"
)
}
if
!
bytes
.
Equal
(
initSessionToken
,
recSessionToken
)
{
t
.
Errorf
(
"session tokens do not match"
)
}
}
func
TestEncHandshake
(
t
*
testing
.
T
)
{
defer
testlog
(
t
)
.
detach
()
prv0
,
_
:=
crypto
.
GenerateKey
()
prv1
,
_
:=
crypto
.
GenerateKey
()
pub0s
,
_
:=
exportPublicKey
(
&
prv0
.
PublicKey
)
pub1s
,
_
:=
exportPublicKey
(
&
prv1
.
PublicKey
)
rw0
,
rw1
:=
net
.
Pipe
()
tokens
:=
make
(
chan
[]
byte
)
secrets
:=
make
(
chan
secrets
)
go
func
()
{
token
,
err
:=
outboundEncHandshake
(
rw0
,
prv0
,
pub1s
,
nil
)
pub1s
,
_
:=
exportPublicKey
(
&
prv1
.
PublicKey
)
s
,
err
:=
outboundEncHandshake
(
rw0
,
prv0
,
pub1s
,
nil
)
if
err
!=
nil
{
t
.
Errorf
(
"outbound side error: %v"
,
err
)
}
tokens
<-
token
id1
:=
discover
.
PubkeyID
(
&
prv1
.
PublicKey
)
if
s
.
RemoteID
!=
id1
{
t
.
Errorf
(
"outbound side remote ID mismatch"
)
}
secrets
<-
s
}()
go
func
()
{
token
,
remotePubkey
,
err
:=
inboundEncHandshake
(
rw1
,
prv1
,
nil
)
s
,
err
:=
inboundEncHandshake
(
rw1
,
prv1
,
nil
)
if
err
!=
nil
{
t
.
Errorf
(
"inbound side error: %v"
,
err
)
}
if
!
bytes
.
Equal
(
remotePubkey
,
pub0s
)
{
t
.
Errorf
(
"inbound side returned wrong remote pubkey
\n
got: %x
\n
want: %x"
,
remotePubkey
,
pub0s
)
id0
:=
discover
.
PubkeyID
(
&
prv0
.
PublicKey
)
if
s
.
RemoteID
!=
id0
{
t
.
Errorf
(
"inbound side remote ID mismatch"
)
}
tokens
<-
token
secrets
<-
s
}()
t1
,
t2
:=
<-
tokens
,
<-
tokens
if
!
bytes
.
Equal
(
t1
,
t2
)
{
t
.
Error
(
"session token mismatch"
)
// get computed secrets from both sides
t1
,
t2
:=
<-
secrets
,
<-
secrets
// don't compare remote node IDs
t1
.
RemoteID
,
t2
.
RemoteID
=
discover
.
NodeID
{},
discover
.
NodeID
{}
// flip MACs on one of them so they compare equal
t1
.
EgressMAC
,
t1
.
IngressMAC
=
t1
.
IngressMAC
,
t1
.
EgressMAC
if
!
reflect
.
DeepEqual
(
t1
,
t2
)
{
t
.
Errorf
(
"secrets mismatch:
\n
t1: %#v
\n
t2: %#v"
,
t1
,
t2
)
}
}
...
...
p2p/rlpx.go
View file @
51e01cce
...
...
@@ -13,24 +13,44 @@ import (
)
var
(
// this is used in place of actual frame header data.
// TODO: replace this when Msg contains the protocol type code.
zeroHeader
=
[]
byte
{
0xC2
,
0x80
,
0x80
}
zero16
=
make
([]
byte
,
16
)
// sixteen zero bytes
zero16
=
make
([]
byte
,
16
)
)
type
rlpxFrameRW
struct
{
conn
io
.
ReadWriter
enc
cipher
.
Stream
dec
cipher
.
Stream
macCipher
cipher
.
Block
egressMAC
hash
.
Hash
ingressMAC
hash
.
Hash
}
func
newRlpxFrameRW
(
conn
io
.
ReadWriter
,
macSecret
[]
byte
,
egressMAC
,
ingressMAC
hash
.
Hash
)
*
rlpxFrameRW
{
cipher
,
err
:=
aes
.
NewCipher
(
macSecret
)
func
newRlpxFrameRW
(
conn
io
.
ReadWriter
,
s
secrets
)
*
rlpxFrameRW
{
macc
,
err
:=
aes
.
NewCipher
(
s
.
MAC
)
if
err
!=
nil
{
panic
(
"invalid MAC secret: "
+
err
.
Error
())
}
encc
,
err
:=
aes
.
NewCipher
(
s
.
AES
)
if
err
!=
nil
{
panic
(
"invalid macSecret: "
+
err
.
Error
())
panic
(
"invalid AES secret: "
+
err
.
Error
())
}
// we use an all-zeroes IV for AES because the key used
// for encryption is ephemeral.
iv
:=
make
([]
byte
,
encc
.
BlockSize
())
return
&
rlpxFrameRW
{
conn
:
conn
,
enc
:
cipher
.
NewCTR
(
encc
,
iv
),
dec
:
cipher
.
NewCTR
(
encc
,
iv
),
macCipher
:
macc
,
egressMAC
:
s
.
EgressMAC
,
ingressMAC
:
s
.
IngressMAC
,
}
return
&
rlpxFrameRW
{
conn
:
conn
,
macCipher
:
cipher
,
egressMAC
:
egressMAC
,
ingressMAC
:
ingressMAC
}
}
func
(
rw
*
rlpxFrameRW
)
WriteMsg
(
msg
Msg
)
error
{
...
...
@@ -41,13 +61,14 @@ func (rw *rlpxFrameRW) WriteMsg(msg Msg) error {
fsize
:=
uint32
(
len
(
ptype
))
+
msg
.
Size
putInt24
(
fsize
,
headbuf
)
// TODO: check overflow
copy
(
headbuf
[
3
:
],
zeroHeader
)
rw
.
enc
.
XORKeyStream
(
headbuf
[
:
16
],
headbuf
[
:
16
])
// first half is now encrypted
copy
(
headbuf
[
16
:
],
updateHeaderMAC
(
rw
.
egressMAC
,
rw
.
macCipher
,
headbuf
[
:
16
]))
if
_
,
err
:=
rw
.
conn
.
Write
(
headbuf
);
err
!=
nil
{
return
err
}
// write frame, updating the egress MAC while writing to conn.
tee
:=
io
.
MultiWriter
(
rw
.
conn
,
rw
.
egressMAC
)
// write
encrypted
frame, updating the egress MAC while writing to conn.
tee
:=
cipher
.
StreamWriter
{
S
:
rw
.
enc
,
W
:
io
.
MultiWriter
(
rw
.
conn
,
rw
.
egressMAC
)}
if
_
,
err
:=
tee
.
Write
(
ptype
);
err
!=
nil
{
return
err
}
...
...
@@ -62,7 +83,8 @@ func (rw *rlpxFrameRW) WriteMsg(msg Msg) error {
// write packet-mac. egress MAC is up to date because
// frame content was written to it as well.
_
,
err
:=
rw
.
conn
.
Write
(
rw
.
egressMAC
.
Sum
(
nil
))
mac
:=
updateHeaderMAC
(
rw
.
egressMAC
,
rw
.
macCipher
,
rw
.
egressMAC
.
Sum
(
nil
))
_
,
err
:=
rw
.
conn
.
Write
(
mac
)
return
err
}
...
...
@@ -72,34 +94,40 @@ func (rw *rlpxFrameRW) ReadMsg() (msg Msg, err error) {
if
_
,
err
:=
io
.
ReadFull
(
rw
.
conn
,
headbuf
);
err
!=
nil
{
return
msg
,
err
}
fsize
:=
readInt24
(
headbuf
)
// ignore protocol type for now
// verify header mac
shouldMAC
:=
updateHeaderMAC
(
rw
.
ingressMAC
,
rw
.
macCipher
,
headbuf
[
:
16
])
if
!
hmac
.
Equal
(
shouldMAC
[
:
16
],
headbuf
[
16
:
])
{
return
msg
,
errors
.
New
(
"bad header MAC"
)
}
rw
.
dec
.
XORKeyStream
(
headbuf
[
:
16
],
headbuf
[
:
16
])
// first half is now decrypted
fsize
:=
readInt24
(
headbuf
)
// ignore protocol type for now
// read the frame content
framebuf
:=
make
([]
byte
,
fsize
)
var
rsize
=
fsize
// frame size rounded up to 16 byte boundary
if
padding
:=
fsize
%
16
;
padding
>
0
{
rsize
+=
16
-
padding
}
framebuf
:=
make
([]
byte
,
rsize
)
if
_
,
err
:=
io
.
ReadFull
(
rw
.
conn
,
framebuf
);
err
!=
nil
{
return
msg
,
err
}
rw
.
ingressMAC
.
Write
(
framebuf
)
if
padding
:=
fsize
%
16
;
padding
>
0
{
if
_
,
err
:=
io
.
CopyN
(
rw
.
ingressMAC
,
rw
.
conn
,
int64
(
16
-
padding
));
err
!=
nil
{
return
msg
,
err
}
}
// read and validate frame MAC. we can re-use headbuf for that.
rw
.
ingressMAC
.
Write
(
framebuf
)
if
_
,
err
:=
io
.
ReadFull
(
rw
.
conn
,
headbuf
);
err
!=
nil
{
return
msg
,
err
}
if
!
hmac
.
Equal
(
rw
.
ingressMAC
.
Sum
(
nil
),
headbuf
)
{
shouldMAC
=
updateHeaderMAC
(
rw
.
ingressMAC
,
rw
.
macCipher
,
rw
.
ingressMAC
.
Sum
(
nil
))
if
!
hmac
.
Equal
(
shouldMAC
,
headbuf
)
{
return
msg
,
errors
.
New
(
"bad frame MAC"
)
}
// decrypt frame content
rw
.
dec
.
XORKeyStream
(
framebuf
,
framebuf
)
// decode message code
content
:=
bytes
.
NewReader
(
framebuf
)
content
:=
bytes
.
NewReader
(
framebuf
[
:
fsize
]
)
if
err
:=
rlp
.
Decode
(
content
,
&
msg
.
Code
);
err
!=
nil
{
return
msg
,
err
}
...
...
p2p/rlpx_test.go
View file @
51e01cce
...
...
@@ -16,14 +16,18 @@ import (
func
TestRlpxFrameFake
(
t
*
testing
.
T
)
{
buf
:=
new
(
bytes
.
Buffer
)
secret
:=
crypto
.
Sha3
()
hash
:=
fakeHash
([]
byte
{
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
,
1
})
rw
:=
newRlpxFrameRW
(
buf
,
secret
,
hash
,
hash
)
rw
:=
newRlpxFrameRW
(
buf
,
secrets
{
AES
:
crypto
.
Sha3
(),
MAC
:
crypto
.
Sha3
(),
IngressMAC
:
hash
,
EgressMAC
:
hash
,
})
golden
:=
unhex
(
`
00
0006C2808000000000000000000000
00
828ddae471818bb0bfa6b551d1cb42
01010101010101010101010101010101
08C40102030400000000000000000000
ba628a4ba590cb43f7848f41c4382885
01010101010101010101010101010101
01010101010101010101010101010101
`
)
...
...
@@ -75,27 +79,35 @@ func unhex(str string) []byte {
func
TestRlpxFrameRW
(
t
*
testing
.
T
)
{
var
(
aesSecret
=
make
([]
byte
,
16
)
macSecret
=
make
([]
byte
,
16
)
egressMACinit
=
make
([]
byte
,
32
)
ingressMACinit
=
make
([]
byte
,
32
)
)
for
_
,
s
:=
range
[][]
byte
{
macSecret
,
egressMACinit
,
ingressMACinit
}
{
for
_
,
s
:=
range
[][]
byte
{
aesSecret
,
macSecret
,
egressMACinit
,
ingressMACinit
}
{
rand
.
Read
(
s
)
}
conn
:=
new
(
bytes
.
Buffer
)
em1
:=
sha3
.
NewKeccak256
()
em1
.
Write
(
egressMACinit
)
im1
:=
sha3
.
NewKeccak256
()
im1
.
Write
(
ingressMACinit
)
rw1
:=
newRlpxFrameRW
(
conn
,
macSecret
,
em1
,
im1
)
em2
:=
sha3
.
NewKeccak256
()
em2
.
Write
(
ingressMACinit
)
im2
:=
sha3
.
NewKeccak256
()
im2
.
Write
(
egressMACinit
)
rw2
:=
newRlpxFrameRW
(
conn
,
macSecret
,
em2
,
im2
)
s1
:=
secrets
{
AES
:
aesSecret
,
MAC
:
macSecret
,
EgressMAC
:
sha3
.
NewKeccak256
(),
IngressMAC
:
sha3
.
NewKeccak256
(),
}
s1
.
EgressMAC
.
Write
(
egressMACinit
)
s1
.
IngressMAC
.
Write
(
ingressMACinit
)
rw1
:=
newRlpxFrameRW
(
conn
,
s1
)
s2
:=
secrets
{
AES
:
aesSecret
,
MAC
:
macSecret
,
EgressMAC
:
sha3
.
NewKeccak256
(),
IngressMAC
:
sha3
.
NewKeccak256
(),
}
s2
.
EgressMAC
.
Write
(
ingressMACinit
)
s2
.
IngressMAC
.
Write
(
egressMACinit
)
rw2
:=
newRlpxFrameRW
(
conn
,
s2
)
// send some messages
for
i
:=
0
;
i
<
10
;
i
++
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment