def sym_decrypt(self, msg): """this is disgusting because we use two different systems for symmetric encryption type 0 uses SmmetricKey while type 1 uses EncryptedDatagram quirks: 0 must also update the nonce stored in the db 1 performs heavy crypto and has non symmetric client/server side encryption/decryption""" if not self.symKey: msgType, msg = Basic.read_byte(msg) if msgType is 0: #get the tor fingerprint so we know how to decrypt the message (binId, ), msg = Basic.read_message('!20s', msg) #convert the tor fingerprint back into hex self.hexId = binascii.hexlify(binId).upper() #get the symmetric key out of the database: sql = "SELECT Owner, Public_Key, Msgnum, auth_blob FROM Relays WHERE Tor_Id = %s" inj = (self.hexId, ) d = db.read(sql, inj) #get the sym key from the db and decrypt the msg d.addCallback(self.fetch_sym_key, msg) #update the message number in the database d.addCallback(self.update_db) elif msgType is 1: self.symKey = EncryptedDatagram.ServerSymKey( Globals.GENERIC_KEY) d = threads.deferToThread(self.get_sym_key_value, msg) else: raise Exception("Unknown msgType: %s" % (msgType)) else: raise Exception( 'Passing more than one message per tcp connection is currently not supported' ) return d
def datagramReceived(self, datagram, address): try: self.address = address log_msg('Datagram received from %s:%s!' % address, 3) msgType, msg = Basic.read_byte(datagram) #for compatability really if msgType == 1: #is it a replay? if not cache.has_key(msg): self.request = msg self.symKey = EncryptedDatagram.ServerSymKey( Globals.GENERIC_KEY) msg = self.symKey.decrypt(msg) #for compatability really request_type, msg = Basic.read_byte(msg) if request_type == 3: self.handler = ACoinMessages.Payment( self.reply, self.address) self.handler.on_message(msg) else: raise Exception("Unknown request_type: %s" % (request_type)) else: self.reply(cache[msg], putInQueue=False) return else: raise Exception("Unknown msgType: %s" % (msgType)) except Exception, e: self.err(e)
def message_arrived(self, msg): """Called when a payment message is received via the controller. Is responsible for piecing it back together into the actual message. @param msg: the data received from Tor @type msg: str""" self.buffer += msg #is the whole message here? msgLen, msgData = Basic.read_short(self.buffer) if len(msgData) >= msgLen: msgData = msgData[:msgLen] #we just discard the rest of the cell, two messages are never packed in the same cell currently self.buffer = "" #what type of message is this? msgType, msgData = Basic.read_byte(msgData) #ok, now handle that message: for msgName in MESSAGE_CODES.keys(): if msgType == MESSAGE_CODES[msgName]: #if we don't know how to handle this message, just close the circuit if msgName not in self.messageHandlers: log_msg("Remote request for %s, which we do not know how to handle" % (msgName), 1) self.close() return #get the handler: handler = self.messageHandlers[msgName] #get the handler function: funcName = "handle_%s" % (msgName) if not hasattr(handler, funcName): raise Exception("%s cannot handle %s payment message?" % (handler, msgName)) f = getattr(handler, funcName) f(msgData) return #uhh, not sure how to handle this message: raise Exception("Unknown message type for payment message: %s" % (msgType))
def _send_remote_peer_request(self, infohash, callback): #make sure we have a circuit to send it out on: if self.circ and self.circ.is_done(): self.circ = None if not self.circ: self.circ = self.app.find_or_build_best_circuit(force=True, protocol="DHT") if self.circ == None: log_msg("Could not build circuit for DHT remote peer request", 0, "dht") return #generate the message: (version, infohash, peerList) msg = "" #header: msg += Basic.write_byte(Node.VERSION) #infohash: msg += infohash #peers: for host, port in self.knownNodes: #is this an IP address? if isIPAddress(host): msg += Basic.write_byte(0) msg += struct.pack("!4sH", socket.inet_aton(host), port) #otherwise, it's a URL that has to be resolved remotely else: msg += Basic.write_byte(1) msg += Basic.write_lenstr(host) msg += Basic.write_short(port) self.circ.send_dht_request(msg, self.make_callback_wrapper(callback))
def message_arrived(self, msg): """Called when a payment message is received via the controller. Is responsible for piecing it back together into the actual message. @param msg: the data received from Tor @type msg: str""" self.buffer += msg # is the whole message here? msgLen, msgData = Basic.read_short(self.buffer) if len(msgData) >= msgLen: msgData = msgData[:msgLen] # we just discard the rest of the cell, two messages are never packed in the same cell currently self.buffer = "" # what type of message is this? msgType, msgData = Basic.read_byte(msgData) # ok, now handle that message: for msgName in MESSAGE_CODES.keys(): if msgType == MESSAGE_CODES[msgName]: # if we don't know how to handle this message, just close the circuit if msgName not in self.messageHandlers: log_msg("Remote request for %s, which we do not know how to handle" % (msgName), 1) self.close() return # get the handler: handler = self.messageHandlers[msgName] # get the handler function: funcName = "handle_%s" % (msgName) if not hasattr(handler, funcName): raise Exception("%s cannot handle %s payment message?" % (handler, msgName)) f = getattr(handler, funcName) f(msgData) return # uhh, not sure how to handle this message: raise Exception("Unknown message type for payment message: %s" % (msgType))
def handle_bank_relay(self, msg): """Send a message to the bank on behalf of someone else, then send the reply onward @param msg: the message to relay @type msg: str""" version, msg = Basic.read_byte(msg) assert version == 1, "only accepts version 1 of PAR protocol" responseHop, msg = Basic.read_byte(msg) bankMsg, msg = Basic.read_lenstr(msg) responseMsg, msg = Basic.read_lenstr(msg) payment = UDPPayment.UDPPayment(self.bank, bankMsg) paymentDeferred = payment.get_deferred() def success(response, responseMsg=responseMsg, responseHop=responseHop): responseMsg += response if responseHop != 0: self.send_direct_tor_message(responseMsg, "payment", True, responseHop) else: self.handle_payment(responseMsg) paymentDeferred.addCallback(success) def failure(error): if error and hasattr(error, "value") and issubclass(type(error.value), TimeoutError): #TODO: this indicates that the bank is down, or something is wrong with their network? log_msg("Relayed payment timed out :(", 0, "par") else: log_ex(error, "Relaying payment message failed!") paymentDeferred.addErrback(failure)
def unpack_and_mint(self, msg): """unpacks the request retreiving the number of coins packed and the total value desired. Verification: values must be positive! """ self.number, msg = Basic.read_short(msg) value, msg = Basic.read_int(msg) log_msg('REQUEST:: %s %s' % (self.number, self.hexId), 0) if not BankUtil.is_positive_integer(self.number): raise ValueError('number of coins must be greater than 0!') if value != ACOIN_VALUE or not BankUtil.is_positive_integer(value): raise ValueError('coins must have a positive, integer value') self.bill = 0 self.signatures = "" for i in range(0, self.number): #TODO: move to a worker pool or something sig = Globals.ACOIN_KEY.decrypt(msg[:Globals.ACOIN_KEY_BYTES], False) self.signatures += struct.pack('!%ss' % (Globals.ACOIN_KEY_BYTES), sig) msg = msg[Globals.ACOIN_KEY_BYTES:] self.bill += value #TODO: move this constraint to postgres to get rid of any potential race conditions sql = "SELECT balance FROM Accounts WHERE Username = %s" inj = (self.user, ) d = db.read(sql, inj) return d
def unpack_and_mint(self, msg): """unpacks the request retreiving the number of coins packed and the total value desired. Verification: values must be positive! """ self.number, msg = Basic.read_short(msg) value, msg = Basic.read_int(msg) log_msg('REQUEST:: %s %s'%(self.number, self.hexId), 0) if not BankUtil.is_positive_integer(self.number): raise ValueError('number of coins must be greater than 0!') if value != ACOIN_VALUE or not BankUtil.is_positive_integer(value): raise ValueError('coins must have a positive, integer value') self.bill = 0 self.signatures = "" for i in range(0, self.number): #TODO: move to a worker pool or something sig = Globals.ACOIN_KEY.decrypt(msg[:Globals.ACOIN_KEY_BYTES], False) self.signatures += struct.pack('!%ss'%(Globals.ACOIN_KEY_BYTES), sig) msg = msg[Globals.ACOIN_KEY_BYTES:] self.bill += value #TODO: move this constraint to postgres to get rid of any potential race conditions sql = "SELECT balance FROM Accounts WHERE Username = %s" inj = (self.user,) d = db.read(sql, inj) return d
def sym_decrypt(self, msg): """this is disgusting because we use two different systems for symmetric encryption type 0 uses SmmetricKey while type 1 uses EncryptedDatagram quirks: 0 must also update the nonce stored in the db 1 performs heavy crypto and has non symmetric client/server side encryption/decryption""" if not self.symKey: msgType, msg = Basic.read_byte(msg) if msgType is 0: #get the tor fingerprint so we know how to decrypt the message (binId,), msg = Basic.read_message('!20s', msg) #convert the tor fingerprint back into hex self.hexId = binascii.hexlify(binId).upper() #get the symmetric key out of the database: sql = "SELECT Owner, Public_Key, Msgnum, auth_blob FROM Relays WHERE Tor_Id = %s" inj = (self.hexId,) d = db.read(sql, inj) #get the sym key from the db and decrypt the msg d.addCallback(self.fetch_sym_key, msg) #update the message number in the database d.addCallback(self.update_db) elif msgType is 1: self.symKey = EncryptedDatagram.ServerSymKey(Globals.GENERIC_KEY) d = threads.deferToThread(self.get_sym_key_value, msg) else: raise Exception("Unknown msgType: %s" % (msgType)) else: raise Exception('Passing more than one message per tcp connection is currently not supported') return d
def get_prefix(self): """@returns: the beginning of the message from this relay""" #send back the PAR version first, so that people can deal with the message intelligently msg = Basic.write_byte(self.parVersion) #send back our hexid, so they know who this is from: msg += Basic.write_hexid(Globals.FINGERPRINT) return msg
def load_public_key(s=None, fileName=None): assert s or fileName, "load_public_key must be passed either a string or file" if fileName: key = M2Crypto.RSA.load_pub_key(fileName) publicKey = PublicKey(key) eStr, nStr = key.pub() publicKey.e = Basic.bytes_to_long(eStr[4:]) publicKey.n = Basic.bytes_to_long(nStr[4:]) return publicKey else: start = s.find("-----BEGIN RSA PUBLIC KEY-----") end = s.find("-----END RSA PUBLIC KEY-----") if start == -1: raise Exception("Missing PEM prefix") if end == -1: raise Exception("Missing PEM postfix") remainder = s[end+len("-----END RSA PUBLIC KEY-----\n\r"):] s = s[start+len("-----BEGIN RSA PUBLIC KEY-----") : end] parser = decoder.decode(s.decode("base64"))[0] n = long(parser.getComponentByPosition(0)) e = long(parser.getComponentByPosition(1)) publicKey = PublicKey(n, e) return publicKey, remainder
def handle_bank_relay(self, msg): """Send a message to the bank on behalf of someone else, then send the reply onward @param msg: the message to relay @type msg: str""" version, msg = Basic.read_byte(msg) assert version == 1, "only accepts version 1 of PAR protocol" responseHop, msg = Basic.read_byte(msg) bankMsg, msg = Basic.read_lenstr(msg) responseMsg, msg = Basic.read_lenstr(msg) payment = UDPPayment.UDPPayment(self.bank, bankMsg) paymentDeferred = payment.get_deferred() def success(response, responseMsg=responseMsg, responseHop=responseHop): responseMsg += response if responseHop != 0: self.send_direct_tor_message(responseMsg, "payment", True, responseHop) else: self.handle_payment(responseMsg) paymentDeferred.addCallback(success) def failure(error): if error and hasattr(error, "value") and issubclass( type(error.value), TimeoutError): #TODO: this indicates that the bank is down, or something is wrong with their network? log_msg("Relayed payment timed out :(", 0, "par") else: log_ex(error, "Relaying payment message failed!") paymentDeferred.addErrback(failure)
def stringReceived(self, data): """Called when the login response arrives""" self.factory.done = True self.responseReceived = True text = None try: protocol, data = Basic.read_byte(data) if protocol == 1: returnCode, data = Basic.read_byte(data) if returnCode == 1: format = '!IIII4sI' (balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration, host, port), blob = Basic.read_message(format, data) size = struct.calcsize(SymmetricKey.MESSAGE_FORMAT) authBlob = blob[:size] text = blob[size:] host = socket.inet_ntoa(host) self.factory.bank.on_new_info(balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration) self.factory.bank.on_login_success(balance, authBlob, host, port, text) else: size = struct.calcsize('!I') timeout = struct.unpack('!I', data[:size])[0] text = data[size:] raise BadLoginPasswordError(timeout) else: raise Exception('unknown protocol: %s'%(protocol)) except Exception, error: self.factory.bank.on_login_failure(error, text)
def stringReceived(self, data): """Called when the login response arrives""" self.factory.done = True self.responseReceived = True text = None try: protocol, data = Basic.read_byte(data) if protocol == 1: returnCode, data = Basic.read_byte(data) if returnCode == 1: format = '!IIII4sI' (balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration, host, port), blob = Basic.read_message(format, data) size = struct.calcsize(SymmetricKey.MESSAGE_FORMAT) authBlob = blob[:size] text = blob[size:] host = socket.inet_ntoa(host) self.factory.bank.on_new_info( balance, currentACoinInterval, currentACoinIntervalExpiration, nextAcoinIntervalExpiration) self.factory.bank.on_login_success(balance, authBlob, host, port, text) else: size = struct.calcsize('!I') timeout = struct.unpack('!I', data[:size])[0] text = data[size:] raise BadLoginPasswordError(timeout) else: raise Exception('unknown protocol: %s' % (protocol)) except Exception, error: self.factory.bank.on_login_failure(error, text)
def datagramReceived(self, datagram, address): try: self.address = address log_msg('Datagram received from %s:%s!'%address, 3) msgType, msg = Basic.read_byte(datagram) #for compatability really if msgType == 1: #is it a replay? if not cache.has_key(msg): self.request = msg self.symKey = EncryptedDatagram.ServerSymKey(Globals.GENERIC_KEY) msg = self.symKey.decrypt(msg) #for compatability really request_type, msg = Basic.read_byte(msg) if request_type == 3: self.handler = ACoinMessages.Payment(self.reply, self.address) self.handler.on_message(msg) else: raise Exception("Unknown request_type: %s" % (request_type)) else: self.reply(cache[msg], putInQueue=False) return else: raise Exception("Unknown msgType: %s" % (msgType)) except Exception, e: self.err(e)
def start_payment_loop(self): coin = Bank.get().get_acoins(1) if not coin: log_msg("No ACoins left!") return coin = coin[0] #generate ACoin request request = BankMessages.make_acoin_request(Bank.get(), Bank.get().currentACoinInterval, 1) #make the message: bankMsg = Basic.write_byte(1) bankMsg += coin.write_binary() + request.msg key = EncryptedDatagram.ClientSymKey(Bank.get().PUBLIC_KEY) bankMsg = Basic.write_byte(1) + key.encrypt(Basic.write_byte(3) + bankMsg) payment = UDPPayment.UDPPayment(Bank.get(), bankMsg) paymentDeferred = payment.get_deferred() def success(result, request=request): log_msg("success") self.payments += 1 # self.start_payment_loop() #validate the ACoin code, sig = Basic.read_byte(result) coin = BankMessages.parse_acoin_response(Bank.get(), sig, request, False) if not coin: log_msg("Invalid ACoin sent for payment!") else: Bank.get().on_earned_coin(coin) paymentDeferred.addCallback(success) def failure(error): self.start_payment_loop() log_ex(error, "Failure during test?") self.failures += 1 paymentDeferred.addErrback(failure)
def stringReceived(self, encryptedMsg): self.responseReceived = True self.transport.loseConnection() blob = self.factory.bank.decrypt_message(encryptedMsg) log_msg("ACoin REQUEST response received.", 4) responseCode, blob = Basic.read_byte(blob) #we had enough credits in our account if responseCode == 0: (newBalance, number), coins = Basic.read_message('!II', blob) #update the balance self.factory.bank.on_new_balance_from_bank(newBalance) acoinStrFormat = "%ss" % (Globals.ACOIN_KEY_BYTES) format = '!' + (acoinStrFormat * number) sigs = list(struct.unpack(format, coins)) while len(self.factory.requests) > 0: request = self.factory.requests.pop(0) sig = sigs.pop(0) coin = BankMessages.parse_acoin_response(self.factory.bank, sig, request, ProgramState.DEBUG) if coin: self.factory.bank.add_acoin(coin) else: log_msg("Got an invalid ACoin from the bank!?", 3) #the bank could not check out the coins because our account doesn't have enough credits! else: (newBalance,), blob = Basic.read_message('!I', blob) self.factory.bank.on_new_balance_from_bank(newBalance)
def died(self, hash): if hash in self.torrent_cache: log_msg( 'DIED: "' + Basic.clean(self.torrent_cache[hash]['path']) + '"', 1) else: log_msg('DIED: "' + Basic.clean(hash) + '"', 1)
def _reply(self, code): """Send an appropriately encoded reply to the client""" self.reply_func(Basic.write_byte(1) + Basic.write_byte(code)) if code == RESPONSE_CODES["SUCCESS"]: log_msg("Account %s created for %s" % (self.username, self.ipAddress)) else: log_msg("Account creation attempt failed with code = %s" % (code))
def __init__(self, n, e=None): """Pass either two arguments (e,n) to build from existing data, or pass one argument (n=existing key) to build from an existing key.""" if e: eStrLen = 0 tmp = 1L while tmp < e or eStrLen % 2 != 0: tmp *= 256L eStrLen += 1 nStrLen = 0 #NOTE: this is completely bizarre. Why does m2crypto think that we need an odd number of bytes to encode a key? nStrLen += 1 tmp = 1L while tmp < n or eStrLen % 2 != 0: tmp *= 256L nStrLen += 1 eStr = struct.pack(">I%ss" % (eStrLen), eStrLen, Basic.long_to_bytes(e, eStrLen)) nStr = struct.pack(">I%ss" % (nStrLen), nStrLen, Basic.long_to_bytes(n, nStrLen)) self.key = M2Crypto.RSA.new_pub_key((eStr, nStr)) self.e = long(e) self.n = long(n) else: #validate that this is of the correct type: try: if n.__class__.__name__ not in ("RSA", "RSA_pub"): raise Exception("Wrong type") except: raise Exception("n is not the right type: " + str(n)) self.key = n #: length of the key in bytes (used in blinding/unblinding) self.keyLen = len(self.key)
def load_public_key(s=None, fileName=None): assert s or fileName, "load_public_key must be passed either a string or file" if fileName: key = M2Crypto.RSA.load_pub_key(fileName) publicKey = PublicKey(key) eStr, nStr = key.pub() publicKey.e = Basic.bytes_to_long(eStr[4:]) publicKey.n = Basic.bytes_to_long(nStr[4:]) return publicKey else: start = s.find("-----BEGIN RSA PUBLIC KEY-----") end = s.find("-----END RSA PUBLIC KEY-----") if start == -1: raise Exception("Missing PEM prefix") if end == -1: raise Exception("Missing PEM postfix") remainder = s[end + len("-----END RSA PUBLIC KEY-----\n\r"):] s = s[start + len("-----BEGIN RSA PUBLIC KEY-----"):end] parser = decoder.decode(s.decode("base64"))[0] n = long(parser.getComponentByPosition(0)) e = long(parser.getComponentByPosition(1)) publicKey = PublicKey(n, e) return publicKey, remainder
def _read_header(self, data, msgName): #read the version version, data = Basic.read_byte(data) assert version == self.VERSION, "Bad version number: %s" % (version) #read the message type: msgType, data = Basic.read_byte(data) assert msgType == self.MESSAGES[msgName], "Bad message type: %s" % (msgType) return data
def _read_header(self, data, msgName): #read the version version, data = Basic.read_byte(data) assert version == self.VERSION, "Bad version number: %s" % (version) #read the message type: msgType, data = Basic.read_byte(data) assert msgType == self.MESSAGES[msgName], "Bad message type: %s" % ( msgType) return data
def _port_test_failure(reason): #is this one of the errors that corresponds to unreachability? if Basic.exception_is_a(reason, [CannotListenError, TimeoutError]): return False #otherwise, we do not know the state of reachability (None signals that) #and log the error if necessary unexpectedException = not Basic.exception_is_a(reason, [ConnectError]) if unexpectedException: log_ex(reason, "Unexpected failure while testing port") return None
def encrypt(self, msg): Basic.validate_type(msg, types.StringType) #make hmac mac = self.make_hmac(msg) inbuf = cStringIO.StringIO(mac + msg) outbuf = cStringIO.StringIO() outbuf.write(self.encryptCipher.update(inbuf.read())) outbuf.write(self.encryptCipher.final()) #no idea what this does because it is undocumented return outbuf.getvalue()
def _port_test_failure(reason): # is this one of the errors that corresponds to unreachability? if Basic.exception_is_a(reason, [CannotListenError, TimeoutError]): return False # otherwise, we do not know the state of reachability (None signals that) # and log the error if necessary unexpectedException = not Basic.exception_is_a(reason, [ConnectError]) if unexpectedException: log_ex(reason, "Unexpected failure while testing port") return None
def encrypt(self, msg): Basic.validate_type(msg, types.StringType) #make hmac mac = self.make_hmac(msg) inbuf = cStringIO.StringIO(mac + msg) outbuf = cStringIO.StringIO() outbuf.write(self.encryptCipher.update(inbuf.read())) outbuf.write(self.encryptCipher.final() ) #no idea what this does because it is undocumented return outbuf.getvalue()
def decrypt(self, msg, usePadding=True): """Decrypt msg (str) with key, return the result (as a string) @param msg: message to be decrypted @type msg: string @param usePadding: use padding if True, otherwise the message isn't padded (as for blinding) @type usePadding: bool @return: encrypted message as string""" Basic.validate_type(msg, types.StringType) if usePadding: return M2Crypto.m2.rsa_private_decrypt(self.key.rsa, msg, M2Crypto.RSA.pkcs1_padding) else: return M2Crypto.m2.rsa_private_decrypt(self.key.rsa, msg, M2Crypto.RSA.no_padding)
def read_request(self, data, host, transport): try: #read the header: data = self._read_header(data, "request") #read the protocol type: protocolType, data = Basic.read_byte(data) assert protocolType in self.TEST_TYPES, "Unknown echo protocol: %s" % (protocolType) protocol = self.TEST_TYPES[protocolType] #read the port: port, data = Basic.read_short(data) except AssertionError, error: raise BadEchoMessageFormat(str(error))
def encrypt(self, msg): Basic.validate_type(msg, types.StringType) #make hmac mac = self.make_hmac(msg) inbuf = cStringIO.StringIO(mac + msg) outbuf = cStringIO.StringIO() outbuf.write(self.encryptCipher.update(inbuf.read())) outbuf.write(self.encryptCipher.final()) #no idea what this does because it is undocumented encryptedKeyConstructor = self.key.encrypt(self.randomData) encryptedKeyConstructor = struct.pack('!%ss'% BANK_KEY_LENGTH, encryptedKeyConstructor) msg = encryptedKeyConstructor+outbuf.getvalue() return msg
def send_receipt_message(self, theirId, numTokens): """Send a new payment request after a successful payment @param theirId: the id that the origin has associated with this payment @type theirId: int @param numTokens: how many payment requests to send back to the origin @type numTokens: int @returns: deferred (triggered when message is done sending)""" msg = self.get_prefix() msg += Basic.write_long(theirId) msg += Basic.write_byte(numTokens) for i in range(0, numTokens): msg += self.generate_payment_request_message() return self.send_direct_tor_message(msg, "receipt", False, 3)
def read_request(self, data, host, transport): try: #read the header: data = self._read_header(data, "request") #read the protocol type: protocolType, data = Basic.read_byte(data) assert protocolType in self.TEST_TYPES, "Unknown echo protocol: %s" % ( protocolType) protocol = self.TEST_TYPES[protocolType] #read the port: port, data = Basic.read_short(data) except AssertionError, error: raise BadEchoMessageFormat(str(error))
def _on_bank_stopped(self, result): Basic.validate_result(result, "bank_stopped") #now stop tor: log_msg("Waiting for Tor to shut down...", 3) d = None if self.torApp: d = self.torApp.stop() if not d: d = self._on_tor_stopped(True) else: d.addCallback(self._on_tor_stopped) d.addErrback(self._on_tor_stopped) return d
def decrypt(self, encryptedMsg): Basic.validate_type(encryptedMsg, types.StringType) #decrypt the message inbuf = cStringIO.StringIO(encryptedMsg) outbuf = cStringIO.StringIO() outbuf.write(self.decryptCipher.update(inbuf.read())) outbuf.write(self.decryptCipher.final()) msg = outbuf.getvalue() mac = msg[:32] msg = msg[32:] #validate the HMAC if self.make_hmac(msg) != mac: raise Exception('HMAC does not authenticate, is something bad going on?') return msg
def encrypt(self, msg): Basic.validate_type(msg, types.StringType) #make hmac mac = self.make_hmac(msg) inbuf = cStringIO.StringIO(mac + msg) outbuf = cStringIO.StringIO() outbuf.write(self.encryptCipher.update(inbuf.read())) outbuf.write(self.encryptCipher.final() ) #no idea what this does because it is undocumented encryptedKeyConstructor = self.key.encrypt(self.randomData) encryptedKeyConstructor = struct.pack('!%ss' % BANK_KEY_LENGTH, encryptedKeyConstructor) msg = encryptedKeyConstructor + outbuf.getvalue() return msg
def _on_applications_stopped(self, result): Basic.validate_result(result, "applications_stopped") self.isReady = False HTTPClient.stop_all() #now stop the bank: log_msg("Waiting for the bank to shut down...", 3) d = None if self.bankApp: d = self.bankApp.stop() if not d: d = self._on_bank_stopped(True) else: d.addCallback(self._on_bank_stopped) d.addErrback(self._on_bank_stopped) return d
def decrypt(self, encryptedMsg): Basic.validate_type(encryptedMsg, types.StringType) #decrypt the message inbuf = cStringIO.StringIO(encryptedMsg) outbuf = cStringIO.StringIO() outbuf.write(self.decryptCipher.update(inbuf.read())) outbuf.write(self.decryptCipher.final()) msg = outbuf.getvalue() mac = msg[:32] msg = msg[32:] #validate the HMAC if self.make_hmac(msg) != mac: raise Exception( 'HMAC does not authenticate, is something bad going on?') return msg
def connectionLost(self, reason): peerId = self.readable_id try: peerId = peerId.decode("hex") #since some peers send in ASCII instead of hex, oh well except TypeError: pass if not self.btApp.is_ready() and not self.btApp.is_starting(): return if self.read == self._read2: stage = 'post handshake' elif self.encrypted: stage = 'before %s' % (self.next_func) else: stage = 'during handshake' log_msg("Lost connection to %s (%s client -- cm: %s) %s." % (Basic.clean(peerId), self.get_client_name(), self.cryptmode, stage), 3, "btconn") if self.Encoder: if self.Encoder.connections and self in self.Encoder.connections: self.sever() #maybe we should retry with encryption? if self.next_func == self.read_header: #did we try connecting via plaintext? if not self.supposedlyEncrypted: log_msg("Ok, retrying with encryption...", 3, "btconn") #ok, lets retry this connection but WITH encryption this time self.Encoder.start_connection((self.get_ip(), self.get_port()), self.id, True) else: log_msg("Failed with encryption too", 3, "btconn") else: self.closed = True
def error(failure): if not self.is_done(): if Basic.exception_is_a(failure, [TorCtl.TorCtlClosed, TorCtl.ErrorReply]): log_msg("Failed to create PAR client, closing", 1, "circuit") else: log_ex(failure, "Unexpected failure while starting circuit") self.on_done()
def socks_method_CONNECT (self): # Check if we have ip address or domain name # log_msg("socks_method_CONNECT host = " + Basic.clean(self.host), 4, "socks") # The FaceTime SOCKS5 proxy treats IP addr the same way as hostname # if _ip_regex.match (self.host): # # we have dotted quad IP address # addressType = 1 # address = socket.inet_aton (self.host) # else: # # we have host name # address = self.host # addressType = 3 address = self.host addressType = 3 addressLen = len(address) #Protocol version=5, Command=1 (CONNECT), Reserved=0 #command = struct.pack ("!BBBB", 5, 1, 0, addressType) command = struct.pack ("!BBBBB", 5, 1, 0, addressType, addressLen) portstr = struct.pack ("!H", self.port) self.transport.write (command + address + portstr) self.state = "gotConnectReply"
def on_failure(reason): #is this an expected failure for a single UDP reply test? if Basic.exception_is_a( reason, [CannotListenError, ConnectError, TimeoutError]): return #otherwise, log the error log_ex(reason, "Unexpected failure while testing UDP replies")
def get_payment_stream(self, msg): """Determine which PaymentStream should handle this message @param msg: the message, reads the hexid off the front @type msg: str @returns: PaymentStream""" hexId, msg = Basic.read_hexid(msg) return self.paymentStreams[hexId], msg
def stringReceived(self, data): self.responseReceived = True self.factory.gotResponse = True data = self.factory.bank.decrypt_message(data) log_msg("ACoin DEPOSIT response received.", 4) (newBalance, interval, expiresCurrent, expiresNext), returnSlip = Basic.read_message('!IIII', data) self.factory.bank.on_new_info(newBalance, interval, expiresCurrent, expiresNext) returnSlip = list(struct.unpack('c'*len(self.factory.coins), returnSlip)) for i in range(0, len(self.factory.coins)): coin = self.factory.coins[i] status = returnSlip[i] gotAcceptableResponse = True badACoin = True if status == "0": badACoin = False elif status == "3": log_msg("ACoin deposit failed. Some node must have double-spent.", 1) elif status == "2": log_msg("ACoin deposit failed. You apparently already sent this acoin.", 1) elif status == "1": log_msg("ACoin deposit failed. ACoin was not valid? %s" % (repr(coin.write_binary())), 0) else: log_msg("Bank returned unknown status message: %s" % (status), 0) gotAcceptableResponse = False if badACoin: #close the circuit, they're trying to cheat us! if coin.originCircuit: coin.originCircuit.close() self.transport.loseConnection()
def get_default_gateway(): #TODO: execing route print is probably a better idea here try: strComputer = "." objWMIService = win32com.client.Dispatch("WbemScripting.SWbemLocator") objSWbemServices = objWMIService.ConnectServer(strComputer, "root\cimv2") colItems = objSWbemServices.ExecQuery( "Select * from Win32_NetworkAdapterConfiguration") gateways = [] for objItem in colItems: z = objItem.DefaultIPGateway if z: for x in z: gateways.append(x) if len(gateways) > 1: log_msg( "Why are there multiple gateways? :( %s" % (Basic.clean(gateways)), 2) elif len(gateways) == 0: return None return gateways.pop(0) except Exception, e: log_ex(e, "Failed to get default gateway") return "192.168.1.1"
def connectionMade(self): log_msg('Sending login message...', 2) signedFingerprint = Globals.PRIVATE_KEY.sign(Globals.FINGERPRINT) publicKey = Basic.long_to_bytes(long(Globals.PRIVATE_KEY.n), 128) protocol = 1 msg = struct.pack('!B128s50s50s128s', protocol, signedFingerprint, self.factory.username, self.factory.password, publicKey) self.sendString(msg)
def on_login_failure(self, bankApp, err, optionalServerResponse=None): text = None self.dia.window.raise_() eType = type(err) if eType is BadLoginPasswordError: text = str(err) elif eType is Failure: eType = type(err.value) if issubclass(eType, ConnectionDone) or issubclass( eType, ConnectionLost): text = "The login server is temporarily offline. We are sorry for the inconvenience. Please try again later." if not text: text = "Login failed for an unknown reason. Please try again." text += "\nNOTE: You must restart the program to change users." if optionalServerResponse: force, optionalServerResponse = Basic.read_byte( optionalServerResponse) #concatenate if force is 0: text += '\n' + optionalServerResponse #nuke it else: text = optionalServerResponse self.label.set_text(text) self.nameEntry.set_sensitive(True) self.pwEntry.set_sensitive(True) self.loginButton.set_sensitive(True) self.quitButton.set_sensitive(True)
def handle_setup_reply(self, msg): """Handle a setup reply. Send it to the appropriate PaymentStream, then check if they are all done""" log_msg("circ=%d: PAR setup done." % (self.circ.id), 3, "par") #unpack the messages: forwardParVersion, msg = Basic.read_byte(msg) if forwardParVersion < self.parVersion: self.parVersion = forwardParVersion payStream, msg = self.get_payment_stream(msg) payStream.handle_setup_reply(forwardParVersion, msg) if self.all_setup_done(): initialTokensDeferred = self.add_start_tokens() #this usually happens if the circuit is already closed, if not, an exception will already be logged if not initialTokensDeferred: self.circ.on_done() return def initial_tokens_added(result): self.circ.initialTokensAdded = True self._add_tokens_callback(result, PaymentMessageHandler.START_READ_TOKENS, PaymentMessageHandler.START_WRITE_TOKENS) return result initialTokensDeferred.addCallback(initial_tokens_added) initialTokensDeferred.addErrback(self.generic_error_handler) self.setupDone = True #send any payment requests that are waiting on the setup: reads = self.queuedReadTokens writes = self.queuedWriteTokens self.queuedReadTokens = 0 self.queuedWriteTokens = 0 if self.queuedReadTokens or self.queuedWriteTokens: self.send_payment_request(reads, writes) self.circ.on_par_ready()
def response(result): if result: read, write = result log_msg( "%s paid us %s for exit stream, now %d / %d" % (Basic.clean(self.baseCircuit.prevHexId[:4]), creditsEarned, read, write), 3, "par")