diff --git a/p2p/crypto.go b/p2p/crypto.go index 9204fa9d0f..6e3f360d92 100644 --- a/p2p/crypto.go +++ b/p2p/crypto.go @@ -1,11 +1,11 @@ package p2p import ( - "bytes" + // "bytes" "crypto/ecdsa" "crypto/rand" "fmt" - "io" + // "io" "github.com/ethereum/go-ethereum/crypto" "github.com/obscuren/ecies" @@ -13,21 +13,22 @@ import ( ) var ( - skLen int = 32 // ecies.MaxSharedKeyLength(pubKey) / 2 - sigLen int = 32 // elliptic S256 - pubKeyLen int = 32 // ECDSA - msgLen int = sigLen + 1 + pubKeyLen + skLen // 97 + sskLen int = 16 // ecies.MaxSharedKeyLength(pubKey) / 2 + sigLen int = 65 // elliptic S256 + keyLen int = 32 // ECDSA + msgLen int = sigLen + 3*keyLen + 1 // 162 + resLen int = 65 ) -//, aesSecret, macSecret, egressMac, ingress +// aesSecret, macSecret, egressMac, ingress type secretRW struct { aesSecret, macSecret, egressMac, ingressMac []byte } type cryptoId struct { - prvKey *ecdsa.PrivateKey - pubKey *ecdsa.PublicKey - pubKeyR io.ReaderAt + prvKey *ecdsa.PrivateKey + pubKey *ecdsa.PublicKey + pubKeyDER []byte } func newCryptoId(id ClientIdentity) (self *cryptoId, err error) { @@ -50,99 +51,181 @@ func newCryptoId(id ClientIdentity) (self *cryptoId, err error) { // 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()) + self.pubKeyDER = id.Pubkey() return } -// -func (self *cryptoId) setupAuth(remotePubKeyDER, sessionToken []byte) (auth []byte, nonce []byte, sharedKnowledge []byte, err error) { +// initAuth is called by peer if it initiated the connection +func (self *cryptoId) initAuth(remotePubKeyDER, sessionToken []byte) (auth []byte, initNonce []byte, remotePubKey *ecdsa.PublicKey, err error) { // session init, common to both parties - var remotePubKey = crypto.ToECDSAPub(remotePubKeyDER) + 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 + + var tokenFlag byte if sessionToken == nil { - err = fmt.Errorf("no session token for peer") - return + // 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 + if sessionToken, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remotePubKey), sskLen, sskLen); err != nil { + return + } + fmt.Printf("secret generated: %v %x", len(sessionToken), sessionToken) + // tokenFlag = 0x00 // redundant + } else { + // for known peers, we use stored token from the previous session + tokenFlag = 0x01 } - // allocate msgLen long message + + //E(remote-pubk, S(ecdhe-random, ecdh-shared-secret^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x0) + // E(remote-pubk, S(ecdhe-random, token^nonce) || H(ecdhe-random-pubk) || pubk || nonce || 0x1) + // 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 { + // generate sskLen long nonce + initNonce = msg[msgLen-keyLen-1 : msgLen-1] + // nonce = msg[msgLen-sskLen-1 : msgLen-1] + if _, err = rand.Read(initNonce); 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) + // ecdh-shared-secret^nonce for new peers + // token^nonce for old peers + var sharedSecret = Xor(sessionToken, initNonce) // 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 + // sign shared secret (message known to both parties): shared-secret var signature []byte - // signature = sign(ecdhe-random, ecdh-shared-secret^nonce^token) + // signature = sign(ecdhe-random, shared-secret) // uses secp256k1.Sign - if signature, err = crypto.Sign(signedMsg, ecdsaRandomPrvKey); err != nil { + if signature, err = crypto.Sign(sharedSecret, 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) + fmt.Printf("signature generated: %v %x", len(signature), signature) + // message + // signed-shared-secret || H(ecdhe-random-pubk) || pubk || nonce || 0x0 + copy(msg, signature) // copy signed-shared-secret + // H(ecdhe-random-pubk) + copy(msg[sigLen:sigLen+keyLen], crypto.Sha3(crypto.FromECDSAPub(&ecdsaRandomPrvKey.PublicKey))) + // pubkey copied to the correct segment. + copy(msg[sigLen+keyLen:sigLen+2*keyLen], self.pubKeyDER) + // nonce is already in the slice + // stick tokenFlag byte to the end + msg[msgLen-1] = tokenFlag + + fmt.Printf("plaintext message generated: %v %x", len(msg), msg) + + // encrypt using remote-pubk // auth = eciesEncrypt(remote-pubk, msg) + if auth, err = crypto.Encrypt(remotePubKey, msg); err != nil { return } + fmt.Printf("encrypted message generated: %v %x\n used pubkey: %x\n", len(auth), auth, crypto.FromECDSAPub(remotePubKey)) + return } -func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionToken []byte, rw *secretRW, err error) { +// verifyAuth is called by peer if it accepted (but not initiated) the connection +func (self *cryptoId) verifyAuth(auth, sharedSecret []byte, remotePubKey *ecdsa.PublicKey) (authResp []byte, respNonce []byte, initNonce []byte, remoteRandomPubKey *ecdsa.PublicKey, err error) { var msg []byte + fmt.Printf("encrypted message received: %v %x\n used pubkey: %x\n", len(auth), auth, crypto.FromECDSAPub(self.pubKey)) // 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 remoteNonce []byte = msg[msgLen-skLen-1 : msgLen-1] + initNonce = msg[msgLen-keyLen-1 : msgLen-1] + // I prove that i own prv key (to derive shared secret, and read nonce off encrypted msg) and that I own shared secret + // they prove they own the private key belonging to ecdhe-random-pubk + var signedMsg = Xor(sharedSecret, initNonce) var remoteRandomPubKeyDER []byte - if remoteRandomPubKeyDER, err = secp256k1.RecoverPubkey(signedMsg, msg[:32]); err != nil { + if remoteRandomPubKeyDER, err = secp256k1.RecoverPubkey(signedMsg, msg[:sigLen]); err != nil { return } - var remoteRandomPubKey = crypto.ToECDSAPub(remoteRandomPubKeyDER) + 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 + + var resp = make([]byte, 2*keyLen+1) + // generate sskLen long nonce + respNonce = msg[msgLen-keyLen-1 : msgLen-1] + if _, err = rand.Read(respNonce); err != nil { + return + } + // generate random keypair + var ecdsaRandomPrvKey *ecdsa.PrivateKey + if ecdsaRandomPrvKey, err = crypto.GenerateKey(); err != nil { + return + } + // var ecdsaRandomPubKey *ecdsa.PublicKey + // ecdsaRandomPubKey= &ecdsaRandomPrvKey.PublicKey + + // message + // E(remote-pubk, ecdhe-random-pubk || nonce || 0x0) + copy(resp[:keyLen], crypto.FromECDSAPub(&ecdsaRandomPrvKey.PublicKey)) + // pubkey copied to the correct segment. + copy(resp[keyLen:2*keyLen], self.pubKeyDER) + // nonce is already in the slice + // stick tokenFlag byte to the end + var tokenFlag byte + if sharedSecret == nil { + } else { + // for known peers, we use stored token from the previous session + tokenFlag = 0x01 + } + resp[resLen] = tokenFlag + + // encrypt using remote-pubk + // auth = eciesEncrypt(remote-pubk, msg) + // why not encrypt with ecdhe-random-remote + if authResp, err = crypto.Encrypt(remotePubKey, resp); err != nil { + return + } + return +} + +func (self *cryptoId) verifyAuthResp(auth []byte) (respNonce []byte, remoteRandomPubKey *ecdsa.PublicKey, tokenFlag bool, 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 + } + + respNonce = msg[resLen-keyLen-1 : resLen-1] + var remoteRandomPubKeyDER = msg[:keyLen] + remoteRandomPubKey = crypto.ToECDSAPub(remoteRandomPubKeyDER) + if remoteRandomPubKey == nil { + err = fmt.Errorf("invalid ecdh random remote public key") + return + } + if msg[resLen-1] == 0x01 { + tokenFlag = true + } + return +} + +func (self *cryptoId) newSession(initNonce, respNonce, auth []byte, remoteRandomPubKey *ecdsa.PublicKey) (sessionToken []byte, rw *secretRW, err error) { + // 3) Now we can trust ecdhe-random-pubk to derive new 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) + dhSharedSecret, err = ecies.ImportECDSA(self.prvKey).GenerateShared(ecies.ImportECDSAPublic(remoteRandomPubKey), sskLen, sskLen) 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...))...)) + var sharedSecret = crypto.Sha3(append(dhSharedSecret, crypto.Sha3(append(respNonce, initNonce...))...)) // token = crypto.Sha3(shared-secret) sessionToken = crypto.Sha3(sharedSecret) // aes-secret = crypto.Sha3(ecdhe-shared-secret || shared-secret) @@ -152,10 +235,10 @@ func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionTo 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...)) + var egressMac = crypto.Sha3(append(Xor(macSecret, respNonce), auth...)) // # destroy nonce // ingress-mac = crypto.Sha3(mac-secret^initiator-nonce || auth), - var ingressMac = crypto.Sha3(append(Xor(macSecret, remoteNonce), auth...)) + var ingressMac = crypto.Sha3(append(Xor(macSecret, initNonce), auth...)) // # destroy remote-nonce rw = &secretRW{ aesSecret: aesSecret, @@ -166,7 +249,9 @@ func (self *cryptoId) verifyAuth(auth, nonce, sharedKnowledge []byte) (sessionTo return } +// should use cipher.xorBytes from crypto/cipher/xor.go for fast xor func Xor(one, other []byte) (xor []byte) { + xor = make([]byte, len(one)) for i := 0; i < len(one); i++ { xor[i] = one[i] ^ other[i] }