def _get_server_nonce_and_hash(self): """ Get the server's nonce and hash, validate them and send our own hash. """ if len(self.buffer) < AUTH_HASH_LEN + AUTH_NONCE_LEN: return server_hash = self.buffer.read(AUTH_HASH_LEN) server_nonce = self.buffer.read(AUTH_NONCE_LEN) auth_cookie = _read_auth_cookie(self.cookie_file) proper_server_hash = hmac_sha256.hmac_sha256_digest(auth_cookie, AUTH_SERVER_TO_CLIENT_CONST + self.client_nonce + server_nonce) log.debug("%s: client_nonce: %s\nserver_nonce: %s\nserver_hash: %s\nproper_server_hash: %s\n" % \ (self.name, repr(self.client_nonce), repr(server_nonce), repr(server_hash), repr(proper_server_hash))) if proper_server_hash != server_hash: log.warning("%s: Invalid server hash. Authentication failed." % (self.name)) self.close() return client_hash = hmac_sha256.hmac_sha256_digest(auth_cookie, AUTH_CLIENT_TO_SERVER_CONST + self.client_nonce + server_nonce) # Send our hash. self.write(client_hash) self.state = STATE_WAIT_FOR_RESULTS
def _handle_server_nonce_and_hash(self): """ Get the server's nonce and hash, validate them and send our own hash. Throws NeedMoreData and RcvdInvalidAuth and CouldNotReadCookie. """ if len(self.buffer) < AUTH_HASH_LEN + AUTH_NONCE_LEN: raise NeedMoreData('Need more data') server_hash = self.buffer.read(AUTH_HASH_LEN) server_nonce = self.buffer.read(AUTH_NONCE_LEN) auth_cookie = _read_auth_cookie(self.cookie_file) proper_server_hash = hmac_sha256.hmac_sha256_digest(auth_cookie, AUTH_SERVER_TO_CLIENT_CONST + self.client_nonce + server_nonce) log.debug("%s: client_nonce: %s\nserver_nonce: %s\nserver_hash: %s\nproper_server_hash: %s\n" % \ (self.name, repr(self.client_nonce), repr(server_nonce), repr(server_hash), repr(proper_server_hash))) if proper_server_hash != server_hash: raise RcvdInvalidAuth("%s: Invalid server hash. Authentication failed." % (self.name)) client_hash = hmac_sha256.hmac_sha256_digest(auth_cookie, AUTH_CLIENT_TO_SERVER_CONST + self.client_nonce + server_nonce) # Send our hash. self.write(client_hash)
def _read_handshake_post_dh(self, shared_secret, other_pubkey, data): """ Setup the crypto from the calculated shared secret, and complete the obfs3 handshake. """ self.shared_secret = shared_secret log_prefix = "obfs3:_read_handshake_post_dh()" log.debug("Got public key: %s.\nGot shared secret: %s" % (repr(other_pubkey), repr(self.shared_secret))) # Set up our crypto. self.send_crypto = self._derive_crypto(self.send_keytype) self.recv_crypto = self._derive_crypto(self.recv_keytype) self.other_magic_value = hmac_sha256.hmac_sha256_digest(self.shared_secret, self.recv_magic_const) # Send our magic value to the remote end and append the queued outgoing data. # Padding is prepended so that the server does not just send the 32-byte magic # in a single TCP segment. padding_length = random.randint(0, MAX_PADDING/2) magic = hmac_sha256.hmac_sha256_digest(self.shared_secret, self.send_magic_const) message = rand.random_bytes(padding_length) + magic + self.send_crypto.crypt(self.queued_data) self.queued_data = '' log.debug("%s: Transmitting %d bytes (with magic)." % (log_prefix, len(message))) self.circuit.downstream.write(message) self.state = ST_SEARCHING_MAGIC if len(data) > 0: log.debug("%s: Processing %d bytes of handshake data remaining after key." % (log_prefix, len(data))) self._scan_for_magic(data)
def _derive_crypto(self, pad_string): """ Derive and return an obfs3 key using the pad string in 'pad_string'. """ secret = hmac_sha256.hmac_sha256_digest(self.shared_secret, pad_string) return aes.AES_CTR_128(secret[:KEYLEN], secret[KEYLEN:], counter_wraparound=True)
def _read_handshake(self, data, circuit): """ Read handshake message, parse the other peer's public key and set up our crypto. """ log_prefix = "obfs3:_read_handshake()" if len(data) < PUBKEY_LEN: log.debug("%s: Not enough bytes for key (%d)." % (log_prefix, len(data))) return log.debug("%s: Got %d bytes of handshake data (waiting for key)." % (log_prefix, len(data))) # Get the public key from the handshake message, do the DH and # get the shared secret. other_pubkey = data.read(PUBKEY_LEN) try: self.shared_secret = self.dh.get_secret(other_pubkey) except ValueError: raise base.PluggableTransportError( "obfs3: Corrupted public key '%s'" % repr(other_pubkey)) log.debug("Got public key: %s.\nGot shared secret: %s" % (repr(other_pubkey), repr(self.shared_secret))) # Set up our crypto. self.send_crypto = self._derive_crypto(self.send_keytype) self.recv_crypto = self._derive_crypto(self.recv_keytype) self.other_magic_value = hmac_sha256.hmac_sha256_digest( self.shared_secret, self.recv_magic_const) # Send our magic value to the remote end and append the queued outgoing data. # Padding is prepended so that the server does not just send the 32-byte magic # in a single TCP segment. padding_length = random.randint(0, MAX_PADDING / 2) magic = hmac_sha256.hmac_sha256_digest(self.shared_secret, self.send_magic_const) message = rand.random_bytes( padding_length) + magic + self.send_crypto.crypt(self.queued_data) self.queued_data = '' log.debug("%s: Transmitting %d bytes (with magic)." % (log_prefix, len(message))) circuit.downstream.write(message) self.state = ST_SEARCHING_MAGIC
def _read_handshake(self, data): """ Read handshake message, parse the other peer's public key and set up our crypto. """ log_prefix = "obfs3:_read_handshake()" if len(data) < PUBKEY_LEN: log.debug("%s: Not enough bytes for key (%d)." % (log_prefix, len(data))) return log.debug("%s: Got %d bytes of handshake data (waiting for key)." % (log_prefix, len(data))) # Get the public key from the handshake message, do the DH and # get the shared secret. other_pubkey = data.read(PUBKEY_LEN) try: self.shared_secret = self.dh.get_secret(other_pubkey) except ValueError: raise base.PluggableTransportError("obfs3: Corrupted public key '%s'" % repr(other_pubkey)) log.debug("Got public key: %s.\nGot shared secret: %s" % (repr(other_pubkey), repr(self.shared_secret))) # Set up our crypto. self.send_crypto = self._derive_crypto(self.send_keytype) self.recv_crypto = self._derive_crypto(self.recv_keytype) self.other_magic_value = hmac_sha256.hmac_sha256_digest(self.shared_secret, self.recv_magic_const) # Send our magic value to the remote end and append the queued outgoing data. # Padding is prepended so that the server does not just send the 32-byte magic # in a single TCP segment. padding_length = random.randint(0, MAX_PADDING/2) magic = hmac_sha256.hmac_sha256_digest(self.shared_secret, self.send_magic_const) message = rand.random_bytes(padding_length) + magic + self.send_crypto.crypt(self.queued_data) self.queued_data = '' log.debug("%s: Transmitting %d bytes (with magic)." % (log_prefix, len(message))) self.circuit.downstream.write(message) self.state = ST_SEARCHING_MAGIC