aboutsummaryrefslogtreecommitdiffstats
path: root/p2p/crypto.go
diff options
context:
space:
mode:
Diffstat (limited to 'p2p/crypto.go')
-rw-r--r--p2p/crypto.go174
1 files changed, 174 insertions, 0 deletions
diff --git a/p2p/crypto.go b/p2p/crypto.go
new file mode 100644
index 000000000..9204fa9d0
--- /dev/null
+++ b/p2p/crypto.go
@@ -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
+}