X-Git-Url: http://www.git.cypherpunks.ru/?a=blobdiff_plain;f=src%2Fgovpn%2Fhandshake.go;h=fb0c649d168b2d8d045158a14db3e9e755b1afbc;hb=5bbf08f846a1893e6c3a1b303b114ef9a7b12ec6;hp=7019148e899949f754435bdbad61c2042963350c;hpb=1bc6bc30886376a2fc94d7f8f77aff1e4d9cc7ea;p=govpn.git diff --git a/src/govpn/handshake.go b/src/govpn/handshake.go index 7019148..fb0c649 100644 --- a/src/govpn/handshake.go +++ b/src/govpn/handshake.go @@ -1,6 +1,6 @@ /* GoVPN -- simple secure free software virtual private network daemon -Copyright (C) 2014-2015 Sergey Matveev +Copyright (C) 2014-2016 Sergey Matveev This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -19,11 +19,10 @@ along with this program. If not, see . package govpn import ( - "crypto/rand" "crypto/subtle" "encoding/binary" + "io" "log" - "net" "time" "github.com/agl/ed25519" @@ -40,7 +39,8 @@ const ( ) type Handshake struct { - addr *net.UDPAddr + addr string + conn io.Writer LastPing time.Time Conf *PeerConf dsaPubH *[ed25519.PublicKeySize]byte @@ -69,28 +69,28 @@ func HApply(data *[32]byte) { // Zero handshake's memory state func (h *Handshake) Zero() { if h.rNonce != nil { - sliceZero(h.rNonce[:]) + SliceZero(h.rNonce[:]) } if h.dhPriv != nil { - sliceZero(h.dhPriv[:]) + SliceZero(h.dhPriv[:]) } if h.key != nil { - sliceZero(h.key[:]) + SliceZero(h.key[:]) } if h.dsaPubH != nil { - sliceZero(h.dsaPubH[:]) + SliceZero(h.dsaPubH[:]) } if h.rServer != nil { - sliceZero(h.rServer[:]) + SliceZero(h.rServer[:]) } if h.rClient != nil { - sliceZero(h.rClient[:]) + SliceZero(h.rClient[:]) } if h.sServer != nil { - sliceZero(h.sServer[:]) + SliceZero(h.sServer[:]) } if h.sClient != nil { - sliceZero(h.sClient[:]) + SliceZero(h.sClient[:]) } } @@ -107,7 +107,7 @@ func dhKeypairGen() (*[32]byte, *[32]byte) { repr := new([32]byte) reprFound := false for !reprFound { - if _, err := rand.Read(priv[:]); err != nil { + if _, err := Rand.Read(priv[:]); err != nil { log.Fatalln("Error reading random for DH private key:", err) } reprFound = extra25519.ScalarBaseMult(pub, repr, priv) @@ -123,14 +123,15 @@ func dhKeyGen(priv, pub *[32]byte) *[32]byte { } // Create new handshake state. -func HandshakeNew(addr *net.UDPAddr, conf *PeerConf) *Handshake { +func NewHandshake(addr string, conn io.Writer, conf *PeerConf) *Handshake { state := Handshake{ addr: addr, + conn: conn, LastPing: time.Now(), Conf: conf, } state.dsaPubH = new([ed25519.PublicKeySize]byte) - copy(state.dsaPubH[:], state.Conf.DSAPub[:]) + copy(state.dsaPubH[:], state.Conf.Verifier.Pub[:]) HApply(state.dsaPubH) return &state } @@ -147,105 +148,187 @@ func idTag(id *PeerId, data []byte) []byte { } // Start handshake's procedure from the client. It is the entry point -// for starting the handshake procedure. You have to specify outgoing -// conn address, remote's addr address, our own peer configuration. -// First handshake packet will be sent immediately. -func HandshakeStart(conf *PeerConf, conn *net.UDPConn, addr *net.UDPAddr) *Handshake { - state := HandshakeNew(addr, conf) - +// for starting the handshake procedure. // First handshake packet +// will be sent immediately. +func HandshakeStart(addr string, conn io.Writer, conf *PeerConf) *Handshake { + state := NewHandshake(addr, conn, conf) var dhPubRepr *[32]byte state.dhPriv, dhPubRepr = dhKeypairGen() state.rNonce = new([RSize]byte) - if _, err := rand.Read(state.rNonce[:]); err != nil { + if _, err := Rand.Read(state.rNonce[:]); err != nil { log.Fatalln("Error reading random for nonce:", err) } - enc := make([]byte, 32) - salsa20.XORKeyStream(enc, dhPubRepr[:], state.rNonce[:], state.dsaPubH) + var enc []byte + if conf.Noise { + enc = make([]byte, conf.MTU-xtea.BlockSize-RSize) + } else { + enc = make([]byte, 32) + } + copy(enc, dhPubRepr[:]) + if conf.EncLess { + var err error + enc, err = EncLessEncode(state.dsaPubH, state.rNonce[:], enc) + if err != err { + panic(err) + } + } else { + salsa20.XORKeyStream(enc, enc, state.rNonce[:], state.dsaPubH) + } data := append(state.rNonce[:], enc...) data = append(data, idTag(state.Conf.Id, state.rNonce[:])...) - conn.WriteToUDP(data, addr) + state.conn.Write(data) return state } // Process handshake message on the server side. // This function is intended to be called on server's side. -// Our outgoing conn connection and received data are required. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. -func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer { +func (h *Handshake) Server(data []byte) *Peer { // R + ENC(H(DSAPub), R, El(CDHPub)) + IDtag - if len(data) == 48 && h.rNonce == nil { - // Generate DH keypair - var dhPubRepr *[32]byte - h.dhPriv, dhPubRepr = dhKeypairGen() - + if h.rNonce == nil && ((!h.Conf.EncLess && len(data) >= 48) || + (h.Conf.EncLess && len(data) == EncLessEnlargeSize+h.Conf.MTU)) { h.rNonce = new([RSize]byte) copy(h.rNonce[:], data[:RSize]) - // Decrypt remote public key and compute shared key + // Decrypt remote public key cDHRepr := new([32]byte) - salsa20.XORKeyStream( - cDHRepr[:], - data[RSize:RSize+32], - h.rNonce[:], - h.dsaPubH, - ) + if h.Conf.EncLess { + out, err := EncLessDecode( + h.dsaPubH, + h.rNonce[:], + data[RSize:len(data)-xtea.BlockSize], + ) + if err != nil { + log.Println("Unable to decode packet from", h.addr, err) + return nil + } + copy(cDHRepr[:], out) + } else { + salsa20.XORKeyStream( + cDHRepr[:], + data[RSize:RSize+32], + h.rNonce[:], + h.dsaPubH, + ) + } + + // Generate DH keypair + var dhPubRepr *[32]byte + h.dhPriv, dhPubRepr = dhKeypairGen() + + // Compute shared key cDH := new([32]byte) extra25519.RepresentativeToPublicKey(cDH, cDHRepr) h.key = dhKeyGen(h.dhPriv, cDH) - encPub := make([]byte, 32) - salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH) + var encPub []byte + var err error + if h.Conf.EncLess { + encPub = make([]byte, h.Conf.MTU) + copy(encPub, dhPubRepr[:]) + encPub, err = EncLessEncode(h.dsaPubH, h.rNonceNext(1), encPub) + if err != nil { + panic(err) + } + } else { + encPub = make([]byte, 32) + salsa20.XORKeyStream(encPub, dhPubRepr[:], h.rNonceNext(1), h.dsaPubH) + } // Generate R* and encrypt them h.rServer = new([RSize]byte) - if _, err := rand.Read(h.rServer[:]); err != nil { + if _, err = Rand.Read(h.rServer[:]); err != nil { log.Fatalln("Error reading random for R:", err) } h.sServer = new([SSize]byte) - if _, err := rand.Read(h.sServer[:]); err != nil { + if _, err = Rand.Read(h.sServer[:]); err != nil { log.Fatalln("Error reading random for S:", err) } - encRs := make([]byte, RSize+SSize) - salsa20.XORKeyStream(encRs, append(h.rServer[:], h.sServer[:]...), h.rNonce[:], h.key) + var encRs []byte + if h.Conf.Noise && !h.Conf.EncLess { + encRs = make([]byte, h.Conf.MTU-len(encPub)-xtea.BlockSize) + } else if h.Conf.EncLess { + encRs = make([]byte, h.Conf.MTU-xtea.BlockSize) + } else { + encRs = make([]byte, RSize+SSize) + } + copy(encRs, append(h.rServer[:], h.sServer[:]...)) + if h.Conf.EncLess { + encRs, err = EncLessEncode(h.key, h.rNonce[:], encRs) + if err != nil { + panic(err) + } + } else { + salsa20.XORKeyStream(encRs, encRs, h.rNonce[:], h.key) + } // Send that to client - conn.WriteToUDP(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...), h.addr) + h.conn.Write(append(encPub, append(encRs, idTag(h.Conf.Id, encPub)...)...)) h.LastPing = time.Now() } else // ENC(K, R+1, RS + RC + SC + Sign(DSAPriv, K)) + IDtag - if len(data) == 120 && h.rClient == nil { - // Decrypted Rs compare rServer - dec := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) - salsa20.XORKeyStream( - dec, - data[:RSize+RSize+SSize+ed25519.SignatureSize], - h.rNonceNext(1), - h.key, - ) + if h.rClient == nil && ((!h.Conf.EncLess && len(data) >= 120) || + (h.Conf.EncLess && len(data) == EncLessEnlargeSize+h.Conf.MTU)) { + var dec []byte + var err error + if h.Conf.EncLess { + dec, err = EncLessDecode( + h.key, + h.rNonceNext(1), + data[:len(data)-xtea.BlockSize], + ) + if err != nil { + log.Println("Unable to decode packet from", h.addr, err) + return nil + } + dec = dec[:RSize+RSize+SSize+ed25519.SignatureSize] + } else { + dec = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) + salsa20.XORKeyStream( + dec, + data[:RSize+RSize+SSize+ed25519.SignatureSize], + h.rNonceNext(1), + h.key, + ) + } if subtle.ConstantTimeCompare(dec[:RSize], h.rServer[:]) != 1 { log.Println("Invalid server's random number with", h.addr) return nil } sign := new([ed25519.SignatureSize]byte) copy(sign[:], dec[RSize+RSize+SSize:]) - if !ed25519.Verify(h.Conf.DSAPub, h.key[:], sign) { + if !ed25519.Verify(h.Conf.Verifier.Pub, h.key[:], sign) { log.Println("Invalid signature from", h.addr) return nil } // Send final answer to client - enc := make([]byte, RSize) - salsa20.XORKeyStream(enc, dec[RSize:RSize+RSize], h.rNonceNext(2), h.key) - conn.WriteToUDP(append(enc, idTag(h.Conf.Id, enc)...), h.addr) + var enc []byte + if h.Conf.Noise { + enc = make([]byte, h.Conf.MTU-xtea.BlockSize) + } else { + enc = make([]byte, RSize) + } + copy(enc, dec[RSize:RSize+RSize]) + if h.Conf.EncLess { + enc, err = EncLessEncode(h.key, h.rNonceNext(2), enc) + if err != nil { + panic(err) + } + } else { + salsa20.XORKeyStream(enc, enc, h.rNonceNext(2), h.key) + } + h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...)) // Switch peer peer := newPeer( + false, h.addr, + h.conn, h.Conf, - 0, keyFromSecrets(h.sServer[:], dec[RSize+RSize:RSize+RSize+SSize])) h.LastPing = time.Now() return peer @@ -257,74 +340,142 @@ func (h *Handshake) Server(conn *net.UDPConn, data []byte) *Peer { // Process handshake message on the client side. // This function is intended to be called on client's side. -// Our outgoing conn connection, authentication -// key and received data are required. // If this is the final handshake message, then new Peer object // will be created and used as a transport. If no mutually // authenticated Peer is ready, then return nil. -func (h *Handshake) Client(conn *net.UDPConn, data []byte) *Peer { - switch len(data) { - case 80: // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag - if h.key != nil { - log.Println("Invalid handshake stage from", h.addr) - return nil +func (h *Handshake) Client(data []byte) *Peer { + // ENC(H(DSAPub), R+1, El(SDHPub)) + ENC(K, R, RS + SS) + IDtag + if h.rServer == nil && h.key == nil && + ((!h.Conf.EncLess && len(data) >= 80) || + (h.Conf.EncLess && len(data) == 2*(EncLessEnlargeSize+h.Conf.MTU))) { + // Decrypt remote public key + sDHRepr := new([32]byte) + var tmp []byte + var err error + if h.Conf.EncLess { + tmp, err = EncLessDecode( + h.dsaPubH, + h.rNonceNext(1), + data[:len(data)/2], + ) + if err != nil { + log.Println("Unable to decode packet from", h.addr, err) + return nil + } + copy(sDHRepr[:], tmp[:32]) + } else { + salsa20.XORKeyStream( + sDHRepr[:], + data[:32], + h.rNonceNext(1), + h.dsaPubH, + ) } - // Decrypt remote public key and compute shared key - sDHRepr := new([32]byte) - salsa20.XORKeyStream(sDHRepr[:], data[:32], h.rNonceNext(1), h.dsaPubH) + // Compute shared key sDH := new([32]byte) extra25519.RepresentativeToPublicKey(sDH, sDHRepr) h.key = dhKeyGen(h.dhPriv, sDH) // Decrypt Rs - decRs := make([]byte, RSize+SSize) - salsa20.XORKeyStream(decRs, data[SSize:32+RSize+SSize], h.rNonce[:], h.key) h.rServer = new([RSize]byte) - copy(h.rServer[:], decRs[:RSize]) h.sServer = new([SSize]byte) - copy(h.sServer[:], decRs[RSize:]) + if h.Conf.EncLess { + tmp, err = EncLessDecode( + h.key, + h.rNonce[:], + data[len(data)/2:len(data)-xtea.BlockSize], + ) + if err != nil { + log.Println("Unable to decode packet from", h.addr, err) + return nil + } + copy(h.rServer[:], tmp[:RSize]) + copy(h.sServer[:], tmp[RSize:RSize+SSize]) + } else { + decRs := make([]byte, RSize+SSize) + salsa20.XORKeyStream( + decRs, + data[SSize:SSize+RSize+SSize], + h.rNonce[:], + h.key, + ) + copy(h.rServer[:], decRs[:RSize]) + copy(h.sServer[:], decRs[RSize:]) + } // Generate R* and signature and encrypt them h.rClient = new([RSize]byte) - if _, err := rand.Read(h.rClient[:]); err != nil { + if _, err = Rand.Read(h.rClient[:]); err != nil { log.Fatalln("Error reading random for R:", err) } h.sClient = new([SSize]byte) - if _, err := rand.Read(h.sClient[:]); err != nil { + if _, err = Rand.Read(h.sClient[:]); err != nil { log.Fatalln("Error reading random for S:", err) } sign := ed25519.Sign(h.Conf.DSAPriv, h.key[:]) - enc := make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) - salsa20.XORKeyStream(enc, - append(h.rServer[:], - append(h.rClient[:], - append(h.sClient[:], sign[:]...)...)...), h.rNonceNext(1), h.key) + var enc []byte + if h.Conf.Noise { + enc = make([]byte, h.Conf.MTU-xtea.BlockSize) + } else { + enc = make([]byte, RSize+RSize+SSize+ed25519.SignatureSize) + } + copy(enc, h.rServer[:]) + copy(enc[RSize:], h.rClient[:]) + copy(enc[RSize+RSize:], h.sClient[:]) + copy(enc[RSize+RSize+SSize:], sign[:]) + if h.Conf.EncLess { + enc, err = EncLessEncode(h.key, h.rNonceNext(1), enc) + if err != nil { + panic(err) + } + } else { + salsa20.XORKeyStream(enc, enc, h.rNonceNext(1), h.key) + } // Send that to server - conn.WriteToUDP(append(enc, idTag(h.Conf.Id, enc)...), h.addr) + h.conn.Write(append(enc, idTag(h.Conf.Id, enc)...)) h.LastPing = time.Now() - case 16: // ENC(K, R+2, RC) + IDtag - if h.key == nil { - log.Println("Invalid handshake stage from", h.addr) - return nil - } - + } else + // ENC(K, R+2, RC) + IDtag + if h.key != nil && ((!h.Conf.EncLess && len(data) >= 16) || + (h.Conf.EncLess && len(data) == EncLessEnlargeSize+h.Conf.MTU)) { + var err error // Decrypt rClient - dec := make([]byte, RSize) - salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key) + var dec []byte + if h.Conf.EncLess { + dec, err = EncLessDecode( + h.key, + h.rNonceNext(2), + data[:len(data)-xtea.BlockSize], + ) + if err != nil { + log.Println("Unable to decode packet from", h.addr, err) + return nil + } + dec = dec[:RSize] + } else { + dec = make([]byte, RSize) + salsa20.XORKeyStream(dec, data[:RSize], h.rNonceNext(2), h.key) + } if subtle.ConstantTimeCompare(dec, h.rClient[:]) != 1 { log.Println("Invalid client's random number with", h.addr) return nil } // Switch peer - peer := newPeer(h.addr, h.Conf, 1, keyFromSecrets(h.sServer[:], h.sClient[:])) + peer := newPeer( + true, + h.addr, + h.conn, + h.Conf, + keyFromSecrets(h.sServer[:], h.sClient[:]), + ) h.LastPing = time.Now() return peer - default: - log.Println("Invalid handshake message from", h.addr) + } else { + log.Println("Invalid handshake stage from", h.addr) } return nil }