func (hs *ssDHClientHandshake) parseServerHandshake(resp []byte) (int, []byte, error) { if len(resp) < minHandshakeLength { return 0, nil, errMarkNotFoundYet } // The server response is Y | P_S | M_S | MAC(Y | P_S | M_S | E). if hs.serverPublicKey == nil { y := resp[:uniformdh.Size] // Pull out the public key, and derive the server mark. hs.serverPublicKey = &uniformdh.PublicKey{} if err := hs.serverPublicKey.SetBytes(y); err != nil { return 0, nil, err } hs.mac.Reset() hs.mac.Write(y) hs.serverMark = hs.mac.Sum(nil)[:macLength] } // Find the mark+MAC, if it exits. endPos := len(resp) if endPos > maxHandshakeLength-macLength { endPos = maxHandshakeLength - macLength } pos := bytes.Index(resp[uniformdh.Size:endPos], hs.serverMark) if pos == -1 { if len(resp) >= maxHandshakeLength { // Couldn't find the mark in a maximum length response. return 0, nil, ErrInvalidHandshake } return 0, nil, errMarkNotFoundYet } else if len(resp) < pos+2*macLength { // Didn't receive the full M_S. return 0, nil, errMarkNotFoundYet } pos += uniformdh.Size // Validate the MAC. hs.mac.Write(resp[uniformdh.Size : pos+macLength]) hs.mac.Write(hs.epochHour) macCmp := hs.mac.Sum(nil)[:macLength] macRx := resp[pos+macLength : pos+2*macLength] if !hmac.Equal(macCmp, macRx) { return 0, nil, ErrInvalidHandshake } // Derive the shared secret. ss, err := uniformdh.Handshake(hs.keypair, hs.serverPublicKey) if err != nil { return 0, nil, err } seed := sha256.Sum256(ss) return pos + 2*macLength, seed[:], nil }
func (conn *obfs3Conn) handshake() error { // The party who opens the connection is the 'initiator'; the one who // accepts it is the 'responder'. Each begins by generating a // UniformDH keypair, and a random number PADLEN in [0, MAX_PADDING/2]. // Both parties then send: // // PUB_KEY | WR(PADLEN) privateKey, err := uniformdh.GenerateKey(csrand.Reader) if err != nil { return err } padLen := csrand.IntRange(0, maxPadding/2) blob := make([]byte, uniformdh.Size+padLen) publicKey, err := privateKey.PublicKey.Bytes() if err != nil { return err } copy(blob[0:], publicKey) if err := csrand.Bytes(blob[uniformdh.Size:]); err != nil { return err } if _, err := conn.Conn.Write(blob); err != nil { return err } // Read the public key from the peer. rawPeerPublicKey := make([]byte, uniformdh.Size) if _, err := io.ReadFull(conn.Conn, rawPeerPublicKey); err != nil { return err } var peerPublicKey uniformdh.PublicKey if err := peerPublicKey.SetBytes(rawPeerPublicKey); err != nil { return err } // After retrieving the public key of the other end, each party // completes the DH key exchange and generates a shared-secret for the // session (named SHARED_SECRET). sharedSecret, err := uniformdh.Handshake(privateKey, &peerPublicKey) if err != nil { return err } if err := conn.kdf(sharedSecret); err != nil { return err } return nil }