Example #1
0
func newServerHandshake(nodeID *ntor.NodeID, serverIdentity *ntor.Keypair, sessionKey *ntor.Keypair) *serverHandshake {
	hs := new(serverHandshake)
	hs.keypair = sessionKey
	hs.nodeID = nodeID
	hs.serverIdentity = serverIdentity
	hs.padLen = csrand.IntRange(serverMinPadLength, serverMaxPadLength)
	hs.mac = hmac.New(sha256.New, append(hs.serverIdentity.Public().Bytes()[:], hs.nodeID.Bytes()[:]...))

	return hs
}
Example #2
0
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
}
Example #3
0
func (conn *obfs3Conn) Write(b []byte) (n int, err error) {
	// If this is the first time we write data post handshake, send the
	// padding/magic value.
	if conn.txMagic != nil {
		padLen := csrand.IntRange(0, maxPadding/2)
		blob := make([]byte, padLen+len(conn.txMagic))
		if err = csrand.Bytes(blob[:padLen]); err != nil {
			conn.Close()
			return
		}
		copy(blob[padLen:], conn.txMagic)
		if _, err = conn.Conn.Write(blob); err != nil {
			conn.Close()
			return
		}
		conn.txMagic = nil
	}

	return conn.tx.Write(b)
}
Example #4
0
func (conn *obfs2Conn) handshake() error {
	// Each begins by generating a seed and a padding key as follows.
	// The initiator generates:
	//
	//  INIT_SEED = SR(SEED_LENGTH)
	//  INIT_PAD_KEY = MAC("Initiator obfuscation padding", INIT_SEED)[:KEYLEN]
	//
	// And the responder generates:
	//
	//  RESP_SEED = SR(SEED_LENGTH)
	//  RESP_PAD_KEY = MAC("Responder obfuscation padding", INIT_SEED)[:KEYLEN]
	//
	// Each then generates a random number PADLEN in range from 0 through
	// MAX_PADDING (inclusive).
	var seed [seedLen]byte
	if err := csrand.Bytes(seed[:]); err != nil {
		return err
	}
	var padMagic []byte
	if conn.isInitiator {
		padMagic = []byte(initiatorPadString)
	} else {
		padMagic = []byte(responderPadString)
	}
	padKey, padIV := hsKdf(padMagic, seed[:], conn.isInitiator)
	padLen := uint32(csrand.IntRange(0, maxPadding))

	hsBlob := make([]byte, hsLen+padLen)
	binary.BigEndian.PutUint32(hsBlob[0:4], magicValue)
	binary.BigEndian.PutUint32(hsBlob[4:8], padLen)
	if padLen > 0 {
		if err := csrand.Bytes(hsBlob[8:]); err != nil {
			return err
		}
	}

	// The initiator then sends:
	//
	//  INIT_SEED | E(INIT_PAD_KEY, UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN))
	//
	// and the responder sends:
	//
	//  RESP_SEED | E(RESP_PAD_KEY, UINT32(MAGIC_VALUE) | UINT32(PADLEN) | WR(PADLEN))
	txBlock, err := aes.NewCipher(padKey)
	if err != nil {
		return err
	}
	txStream := cipher.NewCTR(txBlock, padIV)
	conn.tx = &cipher.StreamWriter{S: txStream, W: conn.Conn}
	if _, err := conn.Conn.Write(seed[:]); err != nil {
		return err
	}
	if _, err := conn.Write(hsBlob); err != nil {
		return err
	}

	// Upon receiving the SEED from the other party, each party derives
	// the other party's padding key value as above, and decrypts the next
	// 8 bytes of the key establishment message.
	var peerSeed [seedLen]byte
	if _, err := io.ReadFull(conn.Conn, peerSeed[:]); err != nil {
		return err
	}
	var peerPadMagic []byte
	if conn.isInitiator {
		peerPadMagic = []byte(responderPadString)
	} else {
		peerPadMagic = []byte(initiatorPadString)
	}
	peerKey, peerIV := hsKdf(peerPadMagic, peerSeed[:], !conn.isInitiator)
	rxBlock, err := aes.NewCipher(peerKey)
	if err != nil {
		return err
	}
	rxStream := cipher.NewCTR(rxBlock, peerIV)
	conn.rx = &cipher.StreamReader{S: rxStream, R: conn.Conn}
	hsHdr := make([]byte, hsLen)
	if _, err := io.ReadFull(conn, hsHdr[:]); err != nil {
		return err
	}

	// If the MAGIC_VALUE does not match, or the PADLEN value is greater than
	// MAX_PADDING, the party receiving it should close the connection
	// immediately.
	if peerMagic := binary.BigEndian.Uint32(hsHdr[0:4]); peerMagic != magicValue {
		return fmt.Errorf("invalid magic value: %x", peerMagic)
	}
	padLen = binary.BigEndian.Uint32(hsHdr[4:8])
	if padLen > maxPadding {
		return fmt.Errorf("padlen too long: %d", padLen)
	}

	// Otherwise, it should read the remaining PADLEN bytes of padding data
	// and discard them.
	tmp := make([]byte, padLen)
	if _, err := io.ReadFull(conn.Conn, tmp); err != nil { // Note: Skips AES.
		return err
	}

	// Derive the actual keys.
	if err := conn.kdf(seed[:], peerSeed[:]); err != nil {
		return err
	}

	return nil
}
Example #5
0
func newDHClientHandshake(kB *ssSharedSecret, sessionKey *uniformdh.PrivateKey) *ssDHClientHandshake {
	hs := &ssDHClientHandshake{keypair: sessionKey}
	hs.mac = hmac.New(sha256.New, kB[:])
	hs.padLen = csrand.IntRange(dhMinPadLength, dhMaxPadLength)
	return hs
}
Example #6
0
// Decode decodes a stream of data and returns the length if any.  ErrAgain is
// a temporary failure, all other errors MUST be treated as fatal and the
// session aborted.
func (decoder *Decoder) Decode(data []byte, frames *bytes.Buffer) (int, error) {
	// A length of 0 indicates that we do not know how big the next frame is
	// going to be.
	if decoder.nextLength == 0 {
		// Attempt to pull out the next frame length.
		if lengthLength > frames.Len() {
			return 0, ErrAgain
		}

		// Remove the length field from the buffer.
		var obfsLen [lengthLength]byte
		_, err := io.ReadFull(frames, obfsLen[:])
		if err != nil {
			return 0, err
		}

		// Derive the nonce the peer used.
		if err = decoder.nonce.bytes(&decoder.nextNonce); err != nil {
			return 0, err
		}

		// Deobfuscate the length field.
		length := binary.BigEndian.Uint16(obfsLen[:])
		lengthMask := decoder.drbg.NextBlock()
		length ^= binary.BigEndian.Uint16(lengthMask)
		if maxFrameLength < length || minFrameLength > length {
			// Per "Plaintext Recovery Attacks Against SSH" by
			// Martin R. Albrecht, Kenneth G. Paterson and Gaven J. Watson,
			// there are a class of attacks againt protocols that use similar
			// sorts of framing schemes.
			//
			// While obfs4 should not allow plaintext recovery (CBC mode is
			// not used), attempt to mitigate out of bound frame length errors
			// by pretending that the length was a random valid range as per
			// the countermeasure suggested by Denis Bider in section 6 of the
			// paper.

			decoder.nextLengthInvalid = true
			length = uint16(csrand.IntRange(minFrameLength, maxFrameLength))
		}
		decoder.nextLength = length
	}

	if int(decoder.nextLength) > frames.Len() {
		return 0, ErrAgain
	}

	// Unseal the frame.
	var box [maxFrameLength]byte
	n, err := io.ReadFull(frames, box[:decoder.nextLength])
	if err != nil {
		return 0, err
	}
	out, ok := secretbox.Open(data[:0], box[:n], &decoder.nextNonce, &decoder.key)
	if !ok || decoder.nextLengthInvalid {
		// When a random length is used (on length error) the tag should always
		// mismatch, but be paranoid.
		return 0, ErrTagMismatch
	}

	// Clean up and prepare for the next frame.
	decoder.nextLength = 0
	decoder.nonce.counter++

	return len(out), nil
}
Example #7
0
func newTicketClientHandshake(mac hash.Hash, ticket *ssTicket) *ssTicketClientHandshake {
	hs := &ssTicketClientHandshake{mac: mac, ticket: ticket}
	hs.padLen = csrand.IntRange(ticketMinPadLength, ticketMaxPadLength)
	return hs
}