mirror of https://github.com/ethereum/go-ethereum
parent
d227f6184e
commit
4e52adb84a
@ -0,0 +1,174 @@ |
|||||||
|
package p2p |
||||||
|
|
||||||
|
import ( |
||||||
|
"bytes" |
||||||
|
"crypto/ecdsa" |
||||||
|
"crypto/rand" |
||||||
|
"fmt" |
||||||
|
"io" |
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto" |
||||||
|
"github.com/obscuren/ecies" |
||||||
|
"github.com/obscuren/secp256k1-go" |
||||||
|
) |
||||||
|
|
||||||
|
var ( |
||||||
|
skLen int = 32 // ecies.MaxSharedKeyLength(pubKey) / 2
|
||||||
|
sigLen int = 32 // elliptic S256
|
||||||
|
pubKeyLen int = 32 // ECDSA
|
||||||
|
msgLen int = sigLen + 1 + pubKeyLen + skLen // 97
|
||||||
|
) |
||||||
|
|
||||||
|
//, aesSecret, macSecret, egressMac, ingress
|
||||||
|
type secretRW struct { |
||||||
|
aesSecret, macSecret, egressMac, ingressMac []byte |
||||||
|
} |
||||||
|
|
||||||
|
type cryptoId struct { |
||||||
|
prvKey *ecdsa.PrivateKey |
||||||
|
pubKey *ecdsa.PublicKey |
||||||
|
pubKeyR io.ReaderAt |
||||||
|
} |
||||||
|
|
||||||
|
func newCryptoId(id ClientIdentity) (self *cryptoId, err error) { |
||||||
|
// will be at server init
|
||||||
|
var prvKeyDER []byte = id.PrivKey() |
||||||
|
if prvKeyDER == nil { |
||||||
|
err = fmt.Errorf("no private key for client") |
||||||
|
return |
||||||
|
} |
||||||
|
// initialise ecies private key via importing DER encoded keys (known via our own clientIdentity)
|
||||||
|
var prvKey = crypto.ToECDSA(prvKeyDER) |
||||||
|
if prvKey == nil { |
||||||
|
err = fmt.Errorf("invalid private key for client") |
||||||
|
return |
||||||
|
} |
||||||
|
self = &cryptoId{ |
||||||
|
prvKey: prvKey, |
||||||
|
// initialise public key from the imported private key
|
||||||
|
pubKey: &prvKey.PublicKey, |
||||||
|
// to be created at server init shared between peers and sessions
|
||||||
|
// for reuse, call wth ReadAt, no reset seek needed
|
||||||
|
} |
||||||
|
self.pubKeyR = bytes.NewReader(id.Pubkey()) |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
//
|
||||||
|
func (self *cryptoId) setupAuth(remotePubKeyDER, sessionToken []byte) (auth []byte, nonce []byte, sharedKnowledge []byte, err error) { |
||||||
|
// session init, common to both parties
|
||||||
|
var remotePubKey = crypto.ToECDSAPub(remotePubKeyDER) |
||||||
|
if remotePubKey == nil { |
||||||
|
err = fmt.Errorf("invalid remote public key") |
||||||
|
return |
||||||
|
} |
||||||
|
var sharedSecret []byte |
||||||
|
// generate shared key from prv and remote pubkey
|
||||||
|
sharedSecret, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remotePubKey), skLen, skLen) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
// check previous session token
|
||||||
|
if sessionToken == nil { |
||||||
|
err = fmt.Errorf("no session token for peer") |
||||||
|
return |
||||||
|
} |
||||||
|
// allocate msgLen long message
|
||||||
|
var msg []byte = make([]byte, msgLen) |
||||||
|
// generate skLen long nonce at the end
|
||||||
|
nonce = msg[msgLen-skLen:] |
||||||
|
if _, err = rand.Read(nonce); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
// create known message
|
||||||
|
// should use
|
||||||
|
// cipher.xorBytes from crypto/cipher/xor.go for fast xor
|
||||||
|
sharedKnowledge = Xor(sharedSecret, sessionToken) |
||||||
|
var signedMsg = Xor(sharedKnowledge, nonce) |
||||||
|
|
||||||
|
// generate random keypair to use for signing
|
||||||
|
var ecdsaRandomPrvKey *ecdsa.PrivateKey |
||||||
|
if ecdsaRandomPrvKey, err = crypto.GenerateKey(); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
// var ecdsaRandomPubKey *ecdsa.PublicKey
|
||||||
|
// ecdsaRandomPubKey= &ecdsaRandomPrvKey.PublicKey
|
||||||
|
|
||||||
|
// message known to both parties ecdh-shared-secret^nonce^token
|
||||||
|
var signature []byte |
||||||
|
// signature = sign(ecdhe-random, ecdh-shared-secret^nonce^token)
|
||||||
|
// uses secp256k1.Sign
|
||||||
|
if signature, err = crypto.Sign(signedMsg, ecdsaRandomPrvKey); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
// msg = signature || 0x80 || pubk || nonce
|
||||||
|
copy(msg, signature) |
||||||
|
msg[sigLen] = 0x80 |
||||||
|
self.pubKeyR.ReadAt(msg[sigLen+1:], int64(pubKeyLen)) // gives pubKeyLen, io.EOF (since we dont read onto the nonce)
|
||||||
|
|
||||||
|
// auth = eciesEncrypt(remote-pubk, msg)
|
||||||
|
if auth, err = crypto.Encrypt(remotePubKey, msg); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionToken []byte, rw *secretRW, err error) { |
||||||
|
var msg []byte |
||||||
|
// they prove that msg is meant for me,
|
||||||
|
// I prove I possess private key if i can read it
|
||||||
|
if msg, err = crypto.Decrypt(self.prvKey, auth); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
var remoteNonce []byte = msg[msgLen-skLen:] |
||||||
|
// I prove that i possess prv key (to derive shared secret, and read nonce off encrypted msg) and that I posessed the earlier one , our shared history
|
||||||
|
// they prove they possess their private key to derive the same shared secret, plus the same shared history (previous session token)
|
||||||
|
var signedMsg = Xor(sharedKnowledge, remoteNonce) |
||||||
|
var remoteRandomPubKeyDER []byte |
||||||
|
if remoteRandomPubKeyDER, err = secp256k1.RecoverPubkey(signedMsg, msg[:32]); err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
var remoteRandomPubKey = crypto.ToECDSAPub(remoteRandomPubKeyDER) |
||||||
|
if remoteRandomPubKey == nil { |
||||||
|
err = fmt.Errorf("invalid remote public key") |
||||||
|
return |
||||||
|
} |
||||||
|
// 3) Now we can trust ecdhe-random-pubk to derive keys
|
||||||
|
//ecdhe-shared-secret = ecdh.agree(ecdhe-random, remote-ecdhe-random-pubk)
|
||||||
|
var dhSharedSecret []byte |
||||||
|
dhSharedSecret, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remoteRandomPubKey), skLen, skLen) |
||||||
|
if err != nil { |
||||||
|
return |
||||||
|
} |
||||||
|
// shared-secret = crypto.Sha3(ecdhe-shared-secret || crypto.Sha3(nonce || initiator-nonce))
|
||||||
|
var sharedSecret []byte = crypto.Sha3(append(dhSharedSecret, crypto.Sha3(append(nonce, remoteNonce...))...)) |
||||||
|
// token = crypto.Sha3(shared-secret)
|
||||||
|
sessionToken = crypto.Sha3(sharedSecret) |
||||||
|
// aes-secret = crypto.Sha3(ecdhe-shared-secret || shared-secret)
|
||||||
|
var aesSecret = crypto.Sha3(append(dhSharedSecret, sharedSecret...)) |
||||||
|
// # destroy shared-secret
|
||||||
|
// mac-secret = crypto.Sha3(ecdhe-shared-secret || aes-secret)
|
||||||
|
var macSecret = crypto.Sha3(append(dhSharedSecret, aesSecret...)) |
||||||
|
// # destroy ecdhe-shared-secret
|
||||||
|
// egress-mac = crypto.Sha3(mac-secret^nonce || auth)
|
||||||
|
var egressMac = crypto.Sha3(append(Xor(macSecret, nonce), auth...)) |
||||||
|
// # destroy nonce
|
||||||
|
// ingress-mac = crypto.Sha3(mac-secret^initiator-nonce || auth),
|
||||||
|
var ingressMac = crypto.Sha3(append(Xor(macSecret, remoteNonce), auth...)) |
||||||
|
// # destroy remote-nonce
|
||||||
|
rw = &secretRW{ |
||||||
|
aesSecret: aesSecret, |
||||||
|
macSecret: macSecret, |
||||||
|
egressMac: egressMac, |
||||||
|
ingressMac: ingressMac, |
||||||
|
} |
||||||
|
return |
||||||
|
} |
||||||
|
|
||||||
|
func Xor(one, other []byte) (xor []byte) { |
||||||
|
for i := 0; i < len(one); i++ { |
||||||
|
xor[i] = one[i] ^ other[i] |
||||||
|
} |
||||||
|
return |
||||||
|
} |
Loading…
Reference in new issue