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
7d815571
Commit
7d815571
authored
Dec 23, 2015
by
Felix Lange
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
p2p: EIP-8 changes
parent
ee1debda
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
443 additions
and
149 deletions
+443
-149
message_test.go
p2p/message_test.go
+2
-1
peer.go
p2p/peer.go
+3
-0
rlpx.go
p2p/rlpx.go
+209
-142
rlpx_test.go
p2p/rlpx_test.go
+229
-6
No files found.
p2p/message_test.go
View file @
7d815571
...
...
@@ -143,7 +143,8 @@ func TestEOFSignal(t *testing.T) {
}
func
unhex
(
str
string
)
[]
byte
{
b
,
err
:=
hex
.
DecodeString
(
strings
.
Replace
(
str
,
"
\n
"
,
""
,
-
1
))
r
:=
strings
.
NewReplacer
(
"
\t
"
,
""
,
" "
,
""
,
"
\n
"
,
""
)
b
,
err
:=
hex
.
DecodeString
(
r
.
Replace
(
str
))
if
err
!=
nil
{
panic
(
fmt
.
Sprintf
(
"invalid hex string: %q"
,
str
))
}
...
...
p2p/peer.go
View file @
7d815571
...
...
@@ -56,6 +56,9 @@ type protoHandshake struct {
Caps
[]
Cap
ListenPort
uint64
ID
discover
.
NodeID
// Ignore additional fields (for forward compatibility).
Rest
[]
rlp
.
RawValue
`rlp:"tail"`
}
// Peer represents a connected remote node.
...
...
p2p/rlpx.go
View file @
7d815571
...
...
@@ -24,11 +24,14 @@ import (
"crypto/elliptic"
"crypto/hmac"
"crypto/rand"
"encoding/binary"
"errors"
"fmt"
"hash"
"io"
mrand
"math/rand"
"net"
"os"
"sync"
"time"
...
...
@@ -51,9 +54,10 @@ const (
authMsgLen
=
sigLen
+
shaLen
+
pubLen
+
shaLen
+
1
authRespLen
=
pubLen
+
shaLen
+
1
eciesBytes
=
65
+
16
+
32
encAuthMsgLen
=
authMsgLen
+
eciesBytes
// size of the final ECIES payload sent as initiator's handshake
encAuthRespLen
=
authRespLen
+
eciesBytes
// size of the final ECIES payload sent as receiver's handshake
eciesOverhead
=
65
/* pubkey */
+
16
/* IV */
+
32
/* MAC */
encAuthMsgLen
=
authMsgLen
+
eciesOverhead
// size of encrypted pre-EIP-8 initiator handshake
encAuthRespLen
=
authRespLen
+
eciesOverhead
// size of encrypted pre-EIP-8 handshake reply
// total timeout for encryption handshake and protocol
// handshake in both directions.
...
...
@@ -151,10 +155,6 @@ func readProtocolHandshake(rw MsgReader, our *protoHandshake) (*protoHandshake,
if
err
:=
msg
.
Decode
(
&
hs
);
err
!=
nil
{
return
nil
,
err
}
// validate handshake info
if
hs
.
Version
!=
our
.
Version
{
return
nil
,
DiscIncompatibleVersion
}
if
(
hs
.
ID
==
discover
.
NodeID
{})
{
return
nil
,
DiscInvalidIdentity
}
...
...
@@ -200,6 +200,29 @@ type secrets struct {
Token
[]
byte
}
// RLPx v4 handshake auth (defined in EIP-8).
type
authMsgV4
struct
{
gotPlain
bool
// whether read packet had plain format.
Signature
[
sigLen
]
byte
InitiatorPubkey
[
pubLen
]
byte
Nonce
[
shaLen
]
byte
Version
uint
// Ignore additional fields (forward-compatibility)
Rest
[]
rlp
.
RawValue
`rlp:"tail"`
}
// RLPx v4 handshake response (defined in EIP-8).
type
authRespV4
struct
{
RandomPubkey
[
pubLen
]
byte
Nonce
[
shaLen
]
byte
Version
uint
// Ignore additional fields (forward-compatibility)
Rest
[]
rlp
.
RawValue
`rlp:"tail"`
}
// secrets is called after the handshake is completed.
// It extracts the connection secrets from the handshake values.
func
(
h
*
encHandshake
)
secrets
(
auth
,
authResp
[]
byte
)
(
secrets
,
error
)
{
...
...
@@ -215,7 +238,6 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
RemoteID
:
h
.
remoteID
,
AES
:
aesSecret
,
MAC
:
crypto
.
Sha3
(
ecdheSecret
,
aesSecret
),
Token
:
crypto
.
Sha3
(
sharedSecret
),
}
// setup sha3 instances for the MACs
...
...
@@ -234,114 +256,89 @@ func (h *encHandshake) secrets(auth, authResp []byte) (secrets, error) {
return
s
,
nil
}
func
(
h
*
encHandshake
)
ecdhShared
(
prv
*
ecdsa
.
PrivateKey
)
([]
byte
,
error
)
{
// staticSharedSecret returns the static shared secret, the result
// of key agreement between the local and remote static node key.
func
(
h
*
encHandshake
)
staticSharedSecret
(
prv
*
ecdsa
.
PrivateKey
)
([]
byte
,
error
)
{
return
ecies
.
ImportECDSA
(
prv
)
.
GenerateShared
(
h
.
remotePub
,
sskLen
,
sskLen
)
}
var
configSendEIP
=
os
.
Getenv
(
"RLPX_EIP8"
)
!=
""
// initiatorEncHandshake negotiates a session token on conn.
// it should be called on the dialing side of the connection.
//
// prv is the local client's private key.
// token is the token from a previous session with this node.
func
initiatorEncHandshake
(
conn
io
.
ReadWriter
,
prv
*
ecdsa
.
PrivateKey
,
remoteID
discover
.
NodeID
,
token
[]
byte
)
(
s
secrets
,
err
error
)
{
h
,
err
:=
newInitiatorHandshake
(
remoteID
)
h
:=
&
encHandshake
{
initiator
:
true
,
remoteID
:
remoteID
}
authMsg
,
err
:=
h
.
makeAuthMsg
(
prv
,
token
)
if
err
!=
nil
{
return
s
,
err
}
auth
,
err
:=
h
.
authMsg
(
prv
,
token
)
var
authPacket
[]
byte
if
configSendEIP
{
authPacket
,
err
=
sealEIP8
(
authMsg
,
h
)
}
else
{
authPacket
,
err
=
authMsg
.
sealPlain
(
h
)
}
if
err
!=
nil
{
return
s
,
err
}
if
_
,
err
=
conn
.
Write
(
auth
);
err
!=
nil
{
if
_
,
err
=
conn
.
Write
(
auth
Packet
);
err
!=
nil
{
return
s
,
err
}
response
:=
make
([]
byte
,
encAuthRespLen
)
if
_
,
err
=
io
.
ReadFull
(
conn
,
response
);
err
!=
nil
{
authRespMsg
:=
new
(
authRespV4
)
authRespPacket
,
err
:=
readHandshakeMsg
(
authRespMsg
,
encAuthRespLen
,
prv
,
conn
)
if
err
!=
nil
{
return
s
,
err
}
if
err
:=
h
.
decodeAuthResp
(
response
,
prv
);
err
!=
nil
{
if
err
:=
h
.
handleAuthResp
(
authRespMsg
);
err
!=
nil
{
return
s
,
err
}
return
h
.
secrets
(
auth
,
response
)
return
h
.
secrets
(
auth
Packet
,
authRespPacket
)
}
func
newInitiatorHandshake
(
remoteID
discover
.
NodeID
)
(
*
encHandshake
,
error
)
{
rpub
,
err
:=
remoteID
.
Pubkey
()
// makeAuthMsg creates the initiator handshake message.
func
(
h
*
encHandshake
)
makeAuthMsg
(
prv
*
ecdsa
.
PrivateKey
,
token
[]
byte
)
(
*
authMsgV4
,
error
)
{
rpub
,
err
:=
h
.
remoteID
.
Pubkey
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"bad remoteID: %v"
,
err
)
}
// generate random initiator nonce
n
:=
make
([]
byte
,
shaLen
)
if
_
,
err
:=
rand
.
Read
(
n
);
err
!=
nil
{
h
.
remotePub
=
ecies
.
ImportECDSAPublic
(
rpub
)
// Generate random initiator nonce.
h
.
initNonce
=
make
([]
byte
,
shaLen
)
if
_
,
err
:=
rand
.
Read
(
h
.
initNonce
);
err
!=
nil
{
return
nil
,
err
}
//
generate random keypair to use for signing
randpriv
,
err
:
=
ecies
.
GenerateKey
(
rand
.
Reader
,
secp256k1
.
S256
(),
nil
)
//
Generate random keypair to for ECDH.
h
.
randomPrivKey
,
err
=
ecies
.
GenerateKey
(
rand
.
Reader
,
secp256k1
.
S256
(),
nil
)
if
err
!=
nil
{
return
nil
,
err
}
h
:=
&
encHandshake
{
initiator
:
true
,
remoteID
:
remoteID
,
remotePub
:
ecies
.
ImportECDSAPublic
(
rpub
),
initNonce
:
n
,
randomPrivKey
:
randpriv
,
}
return
h
,
nil
}
// authMsg creates an encrypted initiator handshake message.
func
(
h
*
encHandshake
)
authMsg
(
prv
*
ecdsa
.
PrivateKey
,
token
[]
byte
)
([]
byte
,
error
)
{
var
tokenFlag
byte
if
token
==
nil
{
// no session token found means we need to generate shared secret.
// ecies shared secret is used as initial session token for new peers
// generate shared key from prv and remote pubkey
var
err
error
if
token
,
err
=
h
.
ecdhShared
(
prv
);
err
!=
nil
{
return
nil
,
err
}
}
else
{
// for known peers, we use stored token from the previous session
tokenFlag
=
0x01
}
// sign known message:
// ecdh-shared-secret^nonce for new peers
// token^nonce for old peers
// Sign known message: static-shared-secret ^ nonce
token
,
err
=
h
.
staticSharedSecret
(
prv
)
if
err
!=
nil
{
return
nil
,
err
}
signed
:=
xor
(
token
,
h
.
initNonce
)
signature
,
err
:=
crypto
.
Sign
(
signed
,
h
.
randomPrivKey
.
ExportECDSA
())
if
err
!=
nil
{
return
nil
,
err
}
// encode auth message
// signature || sha3(ecdhe-random-pubk) || pubk || nonce || token-flag
msg
:=
make
([]
byte
,
authMsgLen
)
n
:=
copy
(
msg
,
signature
)
n
+=
copy
(
msg
[
n
:
],
crypto
.
Sha3
(
exportPubkey
(
&
h
.
randomPrivKey
.
PublicKey
)))
n
+=
copy
(
msg
[
n
:
],
crypto
.
FromECDSAPub
(
&
prv
.
PublicKey
)[
1
:
])
n
+=
copy
(
msg
[
n
:
],
h
.
initNonce
)
msg
[
n
]
=
tokenFlag
// encrypt auth message using remote-pubk
return
ecies
.
Encrypt
(
rand
.
Reader
,
h
.
remotePub
,
msg
,
nil
,
nil
)
msg
:=
new
(
authMsgV4
)
copy
(
msg
.
Signature
[
:
],
signature
)
copy
(
msg
.
InitiatorPubkey
[
:
],
crypto
.
FromECDSAPub
(
&
prv
.
PublicKey
)[
1
:
])
copy
(
msg
.
Nonce
[
:
],
h
.
initNonce
)
msg
.
Version
=
4
return
msg
,
nil
}
// decodeAuthResp decode an encrypted authentication response message.
func
(
h
*
encHandshake
)
decodeAuthResp
(
auth
[]
byte
,
prv
*
ecdsa
.
PrivateKey
)
error
{
msg
,
err
:=
crypto
.
Decrypt
(
prv
,
auth
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"could not decrypt auth response (%v)"
,
err
)
}
h
.
respNonce
=
msg
[
pubLen
:
pubLen
+
shaLen
]
h
.
remoteRandomPub
,
err
=
importPublicKey
(
msg
[
:
pubLen
])
if
err
!=
nil
{
return
err
}
// ignore token flag for now
return
nil
func
(
h
*
encHandshake
)
handleAuthResp
(
msg
*
authRespV4
)
(
err
error
)
{
h
.
respNonce
=
msg
.
Nonce
[
:
]
h
.
remoteRandomPub
,
err
=
importPublicKey
(
msg
.
RandomPubkey
[
:
])
return
err
}
// receiverEncHandshake negotiates a session token on conn.
...
...
@@ -350,99 +347,165 @@ func (h *encHandshake) decodeAuthResp(auth []byte, prv *ecdsa.PrivateKey) error
// prv is the local client's private key.
// token is the token from a previous session with this node.
func
receiverEncHandshake
(
conn
io
.
ReadWriter
,
prv
*
ecdsa
.
PrivateKey
,
token
[]
byte
)
(
s
secrets
,
err
error
)
{
// read remote auth sent by initiator.
auth
:=
make
([]
byte
,
encAuthMsgLe
n
)
if
_
,
err
:=
io
.
ReadFull
(
conn
,
auth
);
err
!=
nil
{
authMsg
:=
new
(
authMsgV4
)
auth
Packet
,
err
:=
readHandshakeMsg
(
authMsg
,
encAuthMsgLen
,
prv
,
con
n
)
if
err
!=
nil
{
return
s
,
err
}
h
,
err
:=
decodeAuthMsg
(
prv
,
token
,
auth
)
if
err
!=
nil
{
h
:=
new
(
encHandshake
)
if
err
:=
h
.
handleAuthMsg
(
authMsg
,
prv
);
err
!=
nil
{
return
s
,
err
}
// send auth response
resp
,
err
:=
h
.
authResp
(
prv
,
token
)
authRespMsg
,
err
:=
h
.
makeAuthResp
()
if
err
!=
nil
{
return
s
,
err
}
if
_
,
err
=
conn
.
Write
(
resp
);
err
!=
nil
{
return
s
,
err
var
authRespPacket
[]
byte
if
authMsg
.
gotPlain
{
authRespPacket
,
err
=
authRespMsg
.
sealPlain
(
h
)
}
else
{
authRespPacket
,
err
=
sealEIP8
(
authRespMsg
,
h
)
}
return
h
.
secrets
(
auth
,
resp
)
}
func
decodeAuthMsg
(
prv
*
ecdsa
.
PrivateKey
,
token
[]
byte
,
auth
[]
byte
)
(
*
encHandshake
,
error
)
{
var
err
error
h
:=
new
(
encHandshake
)
// generate random keypair for session
h
.
randomPrivKey
,
err
=
ecies
.
GenerateKey
(
rand
.
Reader
,
secp256k1
.
S256
(),
nil
)
if
err
!=
nil
{
return
nil
,
err
}
// generate random nonce
h
.
respNonce
=
make
([]
byte
,
shaLen
)
if
_
,
err
=
rand
.
Read
(
h
.
respNonce
);
err
!=
nil
{
return
nil
,
err
return
s
,
err
}
msg
,
err
:=
crypto
.
Decrypt
(
prv
,
auth
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"could not decrypt auth message (%v)"
,
err
)
if
_
,
err
=
conn
.
Write
(
authRespPacket
);
err
!=
nil
{
return
s
,
err
}
return
h
.
secrets
(
authPacket
,
authRespPacket
)
}
// decode message parameters
//
signature || sha3(ecdhe-random-pubk) || pubk || nonce || token-flag
h
.
initNonce
=
msg
[
authMsgLen
-
shaLen
-
1
:
authMsgLen
-
1
]
copy
(
h
.
remoteID
[
:
],
msg
[
sigLen
+
shaLen
:
sigLen
+
shaLen
+
pubLen
])
func
(
h
*
encHandshake
)
handleAuthMsg
(
msg
*
authMsgV4
,
prv
*
ecdsa
.
PrivateKey
)
error
{
//
Import the remote identity.
h
.
initNonce
=
msg
.
Nonce
[
:
]
h
.
remoteID
=
msg
.
InitiatorPubkey
rpub
,
err
:=
h
.
remoteID
.
Pubkey
()
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"bad remoteID: %#v"
,
err
)
return
fmt
.
Errorf
(
"bad remoteID: %#v"
,
err
)
}
h
.
remotePub
=
ecies
.
ImportECDSAPublic
(
rpub
)
// recover remote random pubkey from signed message.
if
token
==
nil
{
// TODO: it is an error if the initiator has a token and we don't. check that.
// no session token means we need to generate shared secret.
// ecies shared secret is used as initial session token for new peers.
// generate shared key from prv and remote pubkey.
if
token
,
err
=
h
.
ecdhShared
(
prv
);
err
!=
nil
{
return
nil
,
err
// Generate random keypair for ECDH.
// If a private key is already set, use it instead of generating one (for testing).
if
h
.
randomPrivKey
==
nil
{
h
.
randomPrivKey
,
err
=
ecies
.
GenerateKey
(
rand
.
Reader
,
secp256k1
.
S256
(),
nil
)
if
err
!=
nil
{
return
err
}
}
// Check the signature.
token
,
err
:=
h
.
staticSharedSecret
(
prv
)
if
err
!=
nil
{
return
err
}
signedMsg
:=
xor
(
token
,
h
.
initNonce
)
remoteRandomPub
,
err
:=
secp256k1
.
RecoverPubkey
(
signedMsg
,
msg
[
:
sigLen
])
remoteRandomPub
,
err
:=
secp256k1
.
RecoverPubkey
(
signedMsg
,
msg
.
Signature
[
:
])
if
err
!=
nil
{
return
err
}
h
.
remoteRandomPub
,
_
=
importPublicKey
(
remoteRandomPub
)
return
nil
}
func
(
h
*
encHandshake
)
makeAuthResp
()
(
msg
*
authRespV4
,
err
error
)
{
// Generate random nonce.
h
.
respNonce
=
make
([]
byte
,
shaLen
)
if
_
,
err
=
rand
.
Read
(
h
.
respNonce
);
err
!=
nil
{
return
nil
,
err
}
// validate the sha3 of recovered pubkey
remoteRandomPubMAC
:=
msg
[
sigLen
:
sigLen
+
shaLen
]
shaRemoteRandomPub
:=
crypto
.
Sha3
(
remoteRandomPub
[
1
:
])
if
!
bytes
.
Equal
(
remoteRandomPubMAC
,
shaRemoteRandomPub
)
{
return
nil
,
fmt
.
Errorf
(
"sha3 of recovered ephemeral pubkey does not match checksum in auth message"
)
msg
=
new
(
authRespV4
)
copy
(
msg
.
Nonce
[
:
],
h
.
respNonce
)
copy
(
msg
.
RandomPubkey
[
:
],
exportPubkey
(
&
h
.
randomPrivKey
.
PublicKey
))
msg
.
Version
=
4
return
msg
,
nil
}
func
(
msg
*
authMsgV4
)
sealPlain
(
h
*
encHandshake
)
([]
byte
,
error
)
{
buf
:=
make
([]
byte
,
authMsgLen
)
n
:=
copy
(
buf
,
msg
.
Signature
[
:
])
n
+=
copy
(
buf
[
n
:
],
crypto
.
Sha3
(
exportPubkey
(
&
h
.
randomPrivKey
.
PublicKey
)))
n
+=
copy
(
buf
[
n
:
],
msg
.
InitiatorPubkey
[
:
])
n
+=
copy
(
buf
[
n
:
],
msg
.
Nonce
[
:
])
buf
[
n
]
=
0
// token-flag
return
ecies
.
Encrypt
(
rand
.
Reader
,
h
.
remotePub
,
buf
,
nil
,
nil
)
}
func
(
msg
*
authMsgV4
)
decodePlain
(
input
[]
byte
)
{
n
:=
copy
(
msg
.
Signature
[
:
],
input
)
n
+=
shaLen
// skip sha3(initiator-ephemeral-pubk)
n
+=
copy
(
msg
.
InitiatorPubkey
[
:
],
input
[
n
:
])
n
+=
copy
(
msg
.
Nonce
[
:
],
input
[
n
:
])
msg
.
Version
=
4
msg
.
gotPlain
=
true
}
func
(
msg
*
authRespV4
)
sealPlain
(
hs
*
encHandshake
)
([]
byte
,
error
)
{
buf
:=
make
([]
byte
,
authRespLen
)
n
:=
copy
(
buf
,
msg
.
RandomPubkey
[
:
])
n
+=
copy
(
buf
[
n
:
],
msg
.
Nonce
[
:
])
return
ecies
.
Encrypt
(
rand
.
Reader
,
hs
.
remotePub
,
buf
,
nil
,
nil
)
}
func
(
msg
*
authRespV4
)
decodePlain
(
input
[]
byte
)
{
n
:=
copy
(
msg
.
RandomPubkey
[
:
],
input
)
n
+=
copy
(
msg
.
Nonce
[
:
],
input
[
n
:
])
msg
.
Version
=
4
}
var
padSpace
=
make
([]
byte
,
300
)
func
sealEIP8
(
msg
interface
{},
h
*
encHandshake
)
([]
byte
,
error
)
{
buf
:=
new
(
bytes
.
Buffer
)
if
err
:=
rlp
.
Encode
(
buf
,
msg
);
err
!=
nil
{
return
nil
,
err
}
// pad with random amount of data. the amount needs to be at least 100 bytes to make
// the message distinguishable from pre-EIP-8 handshakes.
pad
:=
padSpace
[
:
mrand
.
Intn
(
len
(
padSpace
)
-
100
)
+
100
]
buf
.
Write
(
pad
)
prefix
:=
make
([]
byte
,
2
)
binary
.
BigEndian
.
PutUint16
(
prefix
,
uint16
(
buf
.
Len
()
+
eciesOverhead
))
h
.
remoteRandomPub
,
_
=
importPublicKey
(
remoteRandomPub
)
return
h
,
nil
}
// authResp generates the encrypted authentication response message.
func
(
h
*
encHandshake
)
authResp
(
prv
*
ecdsa
.
PrivateKey
,
token
[]
byte
)
([]
byte
,
error
)
{
// responder auth message
// E(remote-pubk, ecdhe-random-pubk || nonce || 0x0)
resp
:=
make
([]
byte
,
authRespLen
)
n
:=
copy
(
resp
,
exportPubkey
(
&
h
.
randomPrivKey
.
PublicKey
))
n
+=
copy
(
resp
[
n
:
],
h
.
respNonce
)
if
token
==
nil
{
resp
[
n
]
=
0
}
else
{
resp
[
n
]
=
1
enc
,
err
:=
ecies
.
Encrypt
(
rand
.
Reader
,
h
.
remotePub
,
buf
.
Bytes
(),
nil
,
prefix
)
return
append
(
prefix
,
enc
...
),
err
}
type
plainDecoder
interface
{
decodePlain
([]
byte
)
}
func
readHandshakeMsg
(
msg
plainDecoder
,
plainSize
int
,
prv
*
ecdsa
.
PrivateKey
,
r
io
.
Reader
)
([]
byte
,
error
)
{
buf
:=
make
([]
byte
,
plainSize
)
if
_
,
err
:=
io
.
ReadFull
(
r
,
buf
);
err
!=
nil
{
return
buf
,
err
}
// Attempt decoding pre-EIP-8 "plain" format.
key
:=
ecies
.
ImportECDSA
(
prv
)
if
dec
,
err
:=
key
.
Decrypt
(
rand
.
Reader
,
buf
,
nil
,
nil
);
err
==
nil
{
msg
.
decodePlain
(
dec
)
return
buf
,
nil
}
// encrypt using remote-pubk
return
ecies
.
Encrypt
(
rand
.
Reader
,
h
.
remotePub
,
resp
,
nil
,
nil
)
// Could be EIP-8 format, try that.
prefix
:=
buf
[
:
2
]
size
:=
binary
.
BigEndian
.
Uint16
(
prefix
)
if
size
<
uint16
(
plainSize
)
{
return
buf
,
fmt
.
Errorf
(
"size underflow, need at least %d bytes"
,
plainSize
)
}
buf
=
append
(
buf
,
make
([]
byte
,
size
-
uint16
(
plainSize
)
+
2
)
...
)
if
_
,
err
:=
io
.
ReadFull
(
r
,
buf
[
plainSize
:
]);
err
!=
nil
{
return
buf
,
err
}
dec
,
err
:=
key
.
Decrypt
(
rand
.
Reader
,
buf
[
2
:
],
nil
,
prefix
)
if
err
!=
nil
{
return
buf
,
err
}
// Can't use rlp.DecodeBytes here because it rejects
// trailing data (forward-compatibility).
s
:=
rlp
.
NewStream
(
bytes
.
NewReader
(
dec
),
0
)
return
buf
,
s
.
Decode
(
msg
)
}
// importPublicKey unmarshals 512 bit public keys.
...
...
@@ -458,7 +521,11 @@ func importPublicKey(pubKey []byte) (*ecies.PublicKey, error) {
return
nil
,
fmt
.
Errorf
(
"invalid public key length %v (expect 64/65)"
,
len
(
pubKey
))
}
// TODO: fewer pointless conversions
return
ecies
.
ImportECDSAPublic
(
crypto
.
ToECDSAPub
(
pubKey65
)),
nil
pub
:=
crypto
.
ToECDSAPub
(
pubKey65
)
if
pub
.
X
==
nil
{
return
nil
,
fmt
.
Errorf
(
"invalid public key"
)
}
return
ecies
.
ImportECDSAPublic
(
pub
),
nil
}
func
exportPubkey
(
pub
*
ecies
.
PublicKey
)
[]
byte
{
...
...
p2p/rlpx_test.go
View file @
7d815571
...
...
@@ -21,6 +21,7 @@ import (
"crypto/rand"
"errors"
"fmt"
"io"
"io/ioutil"
"net"
"reflect"
...
...
@@ -162,6 +163,7 @@ func TestProtocolHandshake(t *testing.T) {
wg
.
Add
(
2
)
go
func
()
{
defer
wg
.
Done
()
defer
fd1
.
Close
()
rlpx
:=
newRLPX
(
fd0
)
remid
,
err
:=
rlpx
.
doEncHandshake
(
prv0
,
node1
)
if
err
!=
nil
{
...
...
@@ -178,6 +180,7 @@ func TestProtocolHandshake(t *testing.T) {
t
.
Errorf
(
"dial side proto handshake error: %v"
,
err
)
return
}
phs
.
Rest
=
nil
if
!
reflect
.
DeepEqual
(
phs
,
hs1
)
{
t
.
Errorf
(
"dial side proto handshake mismatch:
\n
got: %s
\n
want: %s
\n
"
,
spew
.
Sdump
(
phs
),
spew
.
Sdump
(
hs1
))
return
...
...
@@ -186,6 +189,7 @@ func TestProtocolHandshake(t *testing.T) {
}()
go
func
()
{
defer
wg
.
Done
()
defer
fd1
.
Close
()
rlpx
:=
newRLPX
(
fd1
)
remid
,
err
:=
rlpx
.
doEncHandshake
(
prv1
,
nil
)
if
err
!=
nil
{
...
...
@@ -202,6 +206,7 @@ func TestProtocolHandshake(t *testing.T) {
t
.
Errorf
(
"listen side proto handshake error: %v"
,
err
)
return
}
phs
.
Rest
=
nil
if
!
reflect
.
DeepEqual
(
phs
,
hs0
)
{
t
.
Errorf
(
"listen side proto handshake mismatch:
\n
got: %s
\n
want: %s
\n
"
,
spew
.
Sdump
(
phs
),
spew
.
Sdump
(
hs0
))
return
...
...
@@ -216,7 +221,6 @@ func TestProtocolHandshake(t *testing.T) {
func
TestProtocolHandshakeErrors
(
t
*
testing
.
T
)
{
our
:=
&
protoHandshake
{
Version
:
3
,
Caps
:
[]
Cap
{{
"foo"
,
2
},
{
"bar"
,
3
}},
Name
:
"quux"
}
id
:=
randomID
()
tests
:=
[]
struct
{
code
uint64
msg
interface
{}
...
...
@@ -242,11 +246,6 @@ func TestProtocolHandshakeErrors(t *testing.T) {
msg
:
[]
byte
{
1
,
2
,
3
},
err
:
newPeerError
(
errInvalidMsg
,
"(code 0) (size 4) rlp: expected input list for p2p.protoHandshake"
),
},
{
code
:
handshakeMsg
,
msg
:
&
protoHandshake
{
Version
:
9944
,
ID
:
id
},
err
:
DiscIncompatibleVersion
,
},
{
code
:
handshakeMsg
,
msg
:
&
protoHandshake
{
Version
:
3
},
...
...
@@ -374,3 +373,227 @@ func TestRLPXFrameRW(t *testing.T) {
}
}
}
type
handshakeAuthTest
struct
{
input
string
isPlain
bool
wantVersion
uint
wantRest
[]
rlp
.
RawValue
}
var
eip8HandshakeAuthTests
=
[]
handshakeAuthTest
{
// (Auth₁) RLPx v4 plain encoding
{
input
:
`
048ca79ad18e4b0659fab4853fe5bc58eb83992980f4c9cc147d2aa31532efd29a3d3dc6a3d89eaf
913150cfc777ce0ce4af2758bf4810235f6e6ceccfee1acc6b22c005e9e3a49d6448610a58e98744
ba3ac0399e82692d67c1f58849050b3024e21a52c9d3b01d871ff5f210817912773e610443a9ef14
2e91cdba0bd77b5fdf0769b05671fc35f83d83e4d3b0b000c6b2a1b1bba89e0fc51bf4e460df3105
c444f14be226458940d6061c296350937ffd5e3acaceeaaefd3c6f74be8e23e0f45163cc7ebd7622
0f0128410fd05250273156d548a414444ae2f7dea4dfca2d43c057adb701a715bf59f6fb66b2d1d2
0f2c703f851cbf5ac47396d9ca65b6260bd141ac4d53e2de585a73d1750780db4c9ee4cd4d225173
a4592ee77e2bd94d0be3691f3b406f9bba9b591fc63facc016bfa8
`
,
isPlain
:
true
,
wantVersion
:
4
,
},
// (Auth₂) EIP-8 encoding
{
input
:
`
01b304ab7578555167be8154d5cc456f567d5ba302662433674222360f08d5f1534499d3678b513b
0fca474f3a514b18e75683032eb63fccb16c156dc6eb2c0b1593f0d84ac74f6e475f1b8d56116b84
9634a8c458705bf83a626ea0384d4d7341aae591fae42ce6bd5c850bfe0b999a694a49bbbaf3ef6c
da61110601d3b4c02ab6c30437257a6e0117792631a4b47c1d52fc0f8f89caadeb7d02770bf999cc
147d2df3b62e1ffb2c9d8c125a3984865356266bca11ce7d3a688663a51d82defaa8aad69da39ab6
d5470e81ec5f2a7a47fb865ff7cca21516f9299a07b1bc63ba56c7a1a892112841ca44b6e0034dee
70c9adabc15d76a54f443593fafdc3b27af8059703f88928e199cb122362a4b35f62386da7caad09
c001edaeb5f8a06d2b26fb6cb93c52a9fca51853b68193916982358fe1e5369e249875bb8d0d0ec3
6f917bc5e1eafd5896d46bd61ff23f1a863a8a8dcd54c7b109b771c8e61ec9c8908c733c0263440e
2aa067241aaa433f0bb053c7b31a838504b148f570c0ad62837129e547678c5190341e4f1693956c
3bf7678318e2d5b5340c9e488eefea198576344afbdf66db5f51204a6961a63ce072c8926c
`
,
wantVersion
:
4
,
wantRest
:
[]
rlp
.
RawValue
{},
},
// (Auth₃) RLPx v4 EIP-8 encoding with version 56, additional list elements
{
input
:
`
01b8044c6c312173685d1edd268aa95e1d495474c6959bcdd10067ba4c9013df9e40ff45f5bfd6f7
2471f93a91b493f8e00abc4b80f682973de715d77ba3a005a242eb859f9a211d93a347fa64b597bf
280a6b88e26299cf263b01b8dfdb712278464fd1c25840b995e84d367d743f66c0e54a586725b7bb
f12acca27170ae3283c1073adda4b6d79f27656993aefccf16e0d0409fe07db2dc398a1b7e8ee93b
cd181485fd332f381d6a050fba4c7641a5112ac1b0b61168d20f01b479e19adf7fdbfa0905f63352
bfc7e23cf3357657455119d879c78d3cf8c8c06375f3f7d4861aa02a122467e069acaf513025ff19
6641f6d2810ce493f51bee9c966b15c5043505350392b57645385a18c78f14669cc4d960446c1757
1b7c5d725021babbcd786957f3d17089c084907bda22c2b2675b4378b114c601d858802a55345a15
116bc61da4193996187ed70d16730e9ae6b3bb8787ebcaea1871d850997ddc08b4f4ea668fbf3740
7ac044b55be0908ecb94d4ed172ece66fd31bfdadf2b97a8bc690163ee11f5b575a4b44e36e2bfb2
f0fce91676fd64c7773bac6a003f481fddd0bae0a1f31aa27504e2a533af4cef3b623f4791b2cca6
d490
`
,
wantVersion
:
56
,
wantRest
:
[]
rlp
.
RawValue
{{
0x01
},
{
0x02
},
{
0xC2
,
0x04
,
0x05
}},
},
}
type
handshakeAckTest
struct
{
input
string
wantVersion
uint
wantRest
[]
rlp
.
RawValue
}
var
eip8HandshakeRespTests
=
[]
handshakeAckTest
{
// (Ack₁) RLPx v4 plain encoding
{
input
:
`
049f8abcfa9c0dc65b982e98af921bc0ba6e4243169348a236abe9df5f93aa69d99cadddaa387662
b0ff2c08e9006d5a11a278b1b3331e5aaabf0a32f01281b6f4ede0e09a2d5f585b26513cb794d963
5a57563921c04a9090b4f14ee42be1a5461049af4ea7a7f49bf4c97a352d39c8d02ee4acc416388c
1c66cec761d2bc1c72da6ba143477f049c9d2dde846c252c111b904f630ac98e51609b3b1f58168d
dca6505b7196532e5f85b259a20c45e1979491683fee108e9660edbf38f3add489ae73e3dda2c71b
d1497113d5c755e942d1
`
,
wantVersion
:
4
,
},
// (Ack₂) EIP-8 encoding
{
input
:
`
01ea0451958701280a56482929d3b0757da8f7fbe5286784beead59d95089c217c9b917788989470
b0e330cc6e4fb383c0340ed85fab836ec9fb8a49672712aeabbdfd1e837c1ff4cace34311cd7f4de
05d59279e3524ab26ef753a0095637ac88f2b499b9914b5f64e143eae548a1066e14cd2f4bd7f814
c4652f11b254f8a2d0191e2f5546fae6055694aed14d906df79ad3b407d94692694e259191cde171
ad542fc588fa2b7333313d82a9f887332f1dfc36cea03f831cb9a23fea05b33deb999e85489e645f
6aab1872475d488d7bd6c7c120caf28dbfc5d6833888155ed69d34dbdc39c1f299be1057810f34fb
e754d021bfca14dc989753d61c413d261934e1a9c67ee060a25eefb54e81a4d14baff922180c395d
3f998d70f46f6b58306f969627ae364497e73fc27f6d17ae45a413d322cb8814276be6ddd13b885b
201b943213656cde498fa0e9ddc8e0b8f8a53824fbd82254f3e2c17e8eaea009c38b4aa0a3f306e8
797db43c25d68e86f262e564086f59a2fc60511c42abfb3057c247a8a8fe4fb3ccbadde17514b7ac
8000cdb6a912778426260c47f38919a91f25f4b5ffb455d6aaaf150f7e5529c100ce62d6d92826a7
1778d809bdf60232ae21ce8a437eca8223f45ac37f6487452ce626f549b3b5fdee26afd2072e4bc7
5833c2464c805246155289f4
`
,
wantVersion
:
4
,
wantRest
:
[]
rlp
.
RawValue
{},
},
// (Ack₃) EIP-8 encoding with version 57, additional list elements
{
input
:
`
01f004076e58aae772bb101ab1a8e64e01ee96e64857ce82b1113817c6cdd52c09d26f7b90981cd7
ae835aeac72e1573b8a0225dd56d157a010846d888dac7464baf53f2ad4e3d584531fa203658fab0
3a06c9fd5e35737e417bc28c1cbf5e5dfc666de7090f69c3b29754725f84f75382891c561040ea1d
dc0d8f381ed1b9d0d4ad2a0ec021421d847820d6fa0ba66eaf58175f1b235e851c7e2124069fbc20
2888ddb3ac4d56bcbd1b9b7eab59e78f2e2d400905050f4a92dec1c4bdf797b3fc9b2f8e84a482f3
d800386186712dae00d5c386ec9387a5e9c9a1aca5a573ca91082c7d68421f388e79127a5177d4f8
590237364fd348c9611fa39f78dcdceee3f390f07991b7b47e1daa3ebcb6ccc9607811cb17ce51f1
c8c2c5098dbdd28fca547b3f58c01a424ac05f869f49c6a34672ea2cbbc558428aa1fe48bbfd6115
8b1b735a65d99f21e70dbc020bfdface9f724a0d1fb5895db971cc81aa7608baa0920abb0a565c9c
436e2fd13323428296c86385f2384e408a31e104670df0791d93e743a3a5194ee6b076fb6323ca59
3011b7348c16cf58f66b9633906ba54a2ee803187344b394f75dd2e663a57b956cb830dd7a908d4f
39a2336a61ef9fda549180d4ccde21514d117b6c6fd07a9102b5efe710a32af4eeacae2cb3b1dec0
35b9593b48b9d3ca4c13d245d5f04169b0b1
`
,
wantVersion
:
57
,
wantRest
:
[]
rlp
.
RawValue
{{
0x06
},
{
0xC2
,
0x07
,
0x08
},
{
0x81
,
0xFA
}},
},
}
func
TestHandshakeForwardCompatibility
(
t
*
testing
.
T
)
{
var
(
keyA
,
_
=
crypto
.
HexToECDSA
(
"49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee"
)
keyB
,
_
=
crypto
.
HexToECDSA
(
"b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291"
)
pubA
=
crypto
.
FromECDSAPub
(
&
keyA
.
PublicKey
)[
1
:
]
pubB
=
crypto
.
FromECDSAPub
(
&
keyB
.
PublicKey
)[
1
:
]
ephA
,
_
=
crypto
.
HexToECDSA
(
"869d6ecf5211f1cc60418a13b9d870b22959d0c16f02bec714c960dd2298a32d"
)
ephB
,
_
=
crypto
.
HexToECDSA
(
"e238eb8e04fee6511ab04c6dd3c89ce097b11f25d584863ac2b6d5b35b1847e4"
)
ephPubA
=
crypto
.
FromECDSAPub
(
&
ephA
.
PublicKey
)[
1
:
]
ephPubB
=
crypto
.
FromECDSAPub
(
&
ephB
.
PublicKey
)[
1
:
]
nonceA
=
unhex
(
"7e968bba13b6c50e2c4cd7f241cc0d64d1ac25c7f5952df231ac6a2bda8ee5d6"
)
nonceB
=
unhex
(
"559aead08264d5795d3909718cdd05abd49572e84fe55590eef31a88a08fdffd"
)
_
,
_
,
_
,
_
=
pubA
,
pubB
,
ephPubA
,
ephPubB
authSignature
=
unhex
(
"299ca6acfd35e3d72d8ba3d1e2b60b5561d5af5218eb5bc182045769eb4226910a301acae3b369fffc4a4899d6b02531e89fd4fe36a2cf0d93607ba470b50f7800"
)
_
=
authSignature
)
makeAuth
:=
func
(
test
handshakeAuthTest
)
*
authMsgV4
{
msg
:=
&
authMsgV4
{
Version
:
test
.
wantVersion
,
Rest
:
test
.
wantRest
,
gotPlain
:
test
.
isPlain
}
copy
(
msg
.
Signature
[
:
],
authSignature
)
copy
(
msg
.
InitiatorPubkey
[
:
],
pubA
)
copy
(
msg
.
Nonce
[
:
],
nonceA
)
return
msg
}
makeAck
:=
func
(
test
handshakeAckTest
)
*
authRespV4
{
msg
:=
&
authRespV4
{
Version
:
test
.
wantVersion
,
Rest
:
test
.
wantRest
}
copy
(
msg
.
RandomPubkey
[
:
],
ephPubB
)
copy
(
msg
.
Nonce
[
:
],
nonceB
)
return
msg
}
// check auth msg parsing
for
_
,
test
:=
range
eip8HandshakeAuthTests
{
r
:=
bytes
.
NewReader
(
unhex
(
test
.
input
))
msg
:=
new
(
authMsgV4
)
ciphertext
,
err
:=
readHandshakeMsg
(
msg
,
encAuthMsgLen
,
keyB
,
r
)
if
err
!=
nil
{
t
.
Errorf
(
"error for input %x:
\n
%v"
,
unhex
(
test
.
input
),
err
)
continue
}
if
!
bytes
.
Equal
(
ciphertext
,
unhex
(
test
.
input
))
{
t
.
Errorf
(
"wrong ciphertext for input %x:
\n
%x"
,
unhex
(
test
.
input
),
ciphertext
)
}
want
:=
makeAuth
(
test
)
if
!
reflect
.
DeepEqual
(
msg
,
want
)
{
t
.
Errorf
(
"wrong msg for input %x:
\n
got %s
\n
want %s"
,
unhex
(
test
.
input
),
spew
.
Sdump
(
msg
),
spew
.
Sdump
(
want
))
}
}
// check auth resp parsing
for
_
,
test
:=
range
eip8HandshakeRespTests
{
input
:=
unhex
(
test
.
input
)
r
:=
bytes
.
NewReader
(
input
)
msg
:=
new
(
authRespV4
)
ciphertext
,
err
:=
readHandshakeMsg
(
msg
,
encAuthRespLen
,
keyA
,
r
)
if
err
!=
nil
{
t
.
Errorf
(
"error for input %x:
\n
%v"
,
input
,
err
)
continue
}
if
!
bytes
.
Equal
(
ciphertext
,
input
)
{
t
.
Errorf
(
"wrong ciphertext for input %x:
\n
%x"
,
input
,
err
)
}
want
:=
makeAck
(
test
)
if
!
reflect
.
DeepEqual
(
msg
,
want
)
{
t
.
Errorf
(
"wrong msg for input %x:
\n
got %s
\n
want %s"
,
input
,
spew
.
Sdump
(
msg
),
spew
.
Sdump
(
want
))
}
}
// check derivation for (Auth₂, Ack₂) on recipient side
var
(
hs
=
&
encHandshake
{
initiator
:
false
,
respNonce
:
nonceB
,
randomPrivKey
:
ecies
.
ImportECDSA
(
ephB
),
}
authCiphertext
=
unhex
(
eip8HandshakeAuthTests
[
1
]
.
input
)
authRespCiphertext
=
unhex
(
eip8HandshakeRespTests
[
1
]
.
input
)
authMsg
=
makeAuth
(
eip8HandshakeAuthTests
[
1
])
wantAES
=
unhex
(
"80e8632c05fed6fc2a13b0f8d31a3cf645366239170ea067065aba8e28bac487"
)
wantMAC
=
unhex
(
"2ea74ec5dae199227dff1af715362700e989d889d7a493cb0639691efb8e5f98"
)
wantFooIngressHash
=
unhex
(
"0c7ec6340062cc46f5e9f1e3cf86f8c8c403c5a0964f5df0ebd34a75ddc86db5"
)
)
if
err
:=
hs
.
handleAuthMsg
(
authMsg
,
keyB
);
err
!=
nil
{
t
.
Fatalf
(
"handleAuthMsg: %v"
,
err
)
}
derived
,
err
:=
hs
.
secrets
(
authCiphertext
,
authRespCiphertext
)
if
err
!=
nil
{
t
.
Fatalf
(
"secrets: %v"
,
err
)
}
if
!
bytes
.
Equal
(
derived
.
AES
,
wantAES
)
{
t
.
Errorf
(
"aes-secret mismatch:
\n
got %x
\n
want %x"
,
derived
.
AES
,
wantAES
)
}
if
!
bytes
.
Equal
(
derived
.
MAC
,
wantMAC
)
{
t
.
Errorf
(
"mac-secret mismatch:
\n
got %x
\n
want %x"
,
derived
.
MAC
,
wantMAC
)
}
io
.
WriteString
(
derived
.
IngressMAC
,
"foo"
)
fooIngressHash
:=
derived
.
IngressMAC
.
Sum
(
nil
)
if
!
bytes
.
Equal
(
fooIngressHash
,
wantFooIngressHash
)
{
t
.
Errorf
(
"ingress-mac('foo') mismatch:
\n
got %x
\n
want %x"
,
fooIngressHash
,
wantFooIngressHash
)
}
}
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