func (conn *obfs4Conn) clientHandshake(nodeID *ntor.NodeID, peerIdentityKey *ntor.PublicKey, sessionKey *ntor.Keypair) error { if conn.isServer { return fmt.Errorf("clientHandshake called on server connection") } // Generate and send the client handshake. hs := newClientHandshake(nodeID, peerIdentityKey, sessionKey) blob, err := hs.generateHandshake() if err != nil { return err } if _, err = conn.Conn.Write(blob); err != nil { return err } // Consume the server handshake. var hsBuf [maxHandshakeLength]byte for { n, err := conn.Conn.Read(hsBuf[:]) if err != nil { // The Read() could have returned data and an error, but there is // no point in continuing on an EOF or whatever. return err } conn.receiveBuffer.Write(hsBuf[:n]) n, seed, err := hs.parseServerHandshake(conn.receiveBuffer.Bytes()) if err == ErrMarkNotFoundYet { continue } else if err != nil { return err } _ = conn.receiveBuffer.Next(n) // Use the derived key material to intialize the link crypto. okm := ntor.Kdf(seed, framing.KeyLength*2) conn.encoder = framing.NewEncoder(okm[:framing.KeyLength]) conn.decoder = framing.NewDecoder(okm[framing.KeyLength:]) return nil } }
func (conn *obfs4Conn) serverHandshake(sf *obfs4ServerFactory, sessionKey *ntor.Keypair) error { if !conn.isServer { return fmt.Errorf("serverHandshake called on client connection") } // Generate the server handshake, and arm the base timeout. hs := newServerHandshake(sf.nodeID, sf.identityKey, sessionKey) if err := conn.Conn.SetDeadline(time.Now().Add(serverHandshakeTimeout)); err != nil { return err } // Consume the client handshake. var hsBuf [maxHandshakeLength]byte for { n, err := conn.Conn.Read(hsBuf[:]) if err != nil { // The Read() could have returned data and an error, but there is // no point in continuing on an EOF or whatever. return err } conn.receiveBuffer.Write(hsBuf[:n]) seed, err := hs.parseClientHandshake(sf.replayFilter, conn.receiveBuffer.Bytes()) if err == ErrMarkNotFoundYet { continue } else if err != nil { return err } conn.receiveBuffer.Reset() if err := conn.Conn.SetDeadline(time.Time{}); err != nil { return nil } // Use the derived key material to intialize the link crypto. okm := ntor.Kdf(seed, framing.KeyLength*2) conn.encoder = framing.NewEncoder(okm[framing.KeyLength:]) conn.decoder = framing.NewDecoder(okm[:framing.KeyLength]) break } // Since the current and only implementation always sends a PRNG seed for // the length obfuscation, this makes the amount of data received from the // server inconsistent with the length sent from the client. // // Rebalance this by tweaking the client mimimum padding/server maximum // padding, and sending the PRNG seed unpadded (As in, treat the PRNG seed // as part of the server response). See inlineSeedFrameLength in // handshake_ntor.go. // Generate/send the response. blob, err := hs.generateHandshake() if err != nil { return err } var frameBuf bytes.Buffer if _, err = frameBuf.Write(blob); err != nil { return err } // Send the PRNG seed as the first packet. if err := conn.makePacket(&frameBuf, packetTypePrngSeed, sf.lenSeed.Bytes()[:], 0); err != nil { return err } if _, err = conn.Conn.Write(frameBuf.Bytes()); err != nil { return err } return nil }