def handshake_initiate(private_key, redis_client): try: request = expect_json_request(bottle.request, INITIATE_SCHEMA) symmetric_key = redis_get_cookie( redis_client, request[INITIATE_COOKIE_FIELD]) cookie_sbox = SecretBox(symmetric_key) cookie = cookie_sbox.decrypt( str(request[INITIATE_COOKIE_FIELD]), encoder=Base64Encoder) if len(cookie) != 2 * CURVE25519_KEY_BYTES: bottle.response.status = HTTP_INTERNAL_SERVER_ERROR return {'error': 'An invalid cookie was sent to the client.'} client_transient_pkey = PublicKey(cookie[0:CURVE25519_KEY_BYTES]) transient_skey = PrivateKey(cookie[CURVE25519_KEY_BYTES:]) if request[INITIATE_CLIENT_TRANSIENT_PKEY_FIELD] != \ client_transient_pkey.encode(Base64Encoder): raise InvalidClientRequest( 'Initiate: non matching transient public keys.') vouch_json = open_box(request[INITIATE_VOUCH_FIELD], transient_skey, client_transient_pkey) vouch = parse_and_verify_json(vouch_json, VOUCH_SCHEMA) client_pkey = PublicKey( str(vouch[VOUCH_CLIENT_PKEY_FIELD]), encoder=Base64Encoder) vouch_for_transient_pkey = open_box( vouch[VOUCH_TRANSIENT_KEY_BOX_FIELD], private_key, client_pkey) if vouch_for_transient_pkey != client_transient_pkey.encode(): raise InvalidClientRequest( 'Initiate: non matching transient public keys.') resp = 'I believe you are {} and you want {}'.format( client_pkey.encode(Base64Encoder), vouch[VOUCH_MESSAGE_FIELD]) print(resp) response_nonce = nacl.utils.random(Box.NONCE_SIZE) response_box = Box(transient_skey, client_transient_pkey) response_box_cipher = response_box.encrypt( resp, response_nonce, encoder=Base64Encoder) return {'response': response_box_cipher} except jsonschema.ValidationError as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except InvalidClientRequest as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except MissingCookie as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except CryptoError as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': 'Bad encryption in handshake.'} return {'error': ''}
def test_box_seal( privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext): pubalice = PublicKey(pubalice, encoder=HexEncoder) privalice = PrivateKey(privalice, encoder=HexEncoder) plaintext = binascii.unhexlify(plaintext) sealed = pubalice.seal(plaintext) unsealed = privalice.seal_open(sealed) assert plaintext == unsealed sealed2 = privalice.seal(plaintext) unsealed2 = privalice.seal_open(sealed2) assert plaintext == unsealed2
def processM1(self, msg): #print "processM1", self.petname self.theirTempPubkey = PublicKey(msg) self.db.update("UPDATE invitations SET theirTempPubkey=?" " WHERE id=?", (self.theirTempPubkey.encode(Hex), self.iid), "invitations", self.iid) # theirTempPubkey will committed by our caller, in the same txn as # the message send my_privkey = self.getMyTempPrivkey() my_channel_record = self.getMyPublicChannelRecord() b = Box(my_privkey, self.theirTempPubkey) signedBody = b"".join([self.theirTempPubkey.encode(), my_privkey.public_key.encode(), my_channel_record.encode("utf-8")]) my_sign = self.getMySigningKey() body = b"".join([b"i0:m2a:", my_sign.verify_key.encode(), my_sign.sign(signedBody) ]) nonce = os.urandom(Box.NONCE_SIZE) nonce_and_ciphertext = b.encrypt(body, nonce) #print "ENCRYPTED n+c", len(nonce_and_ciphertext), nonce_and_ciphertext.encode("hex") #print " nonce", nonce.encode("hex") msg2 = "i0:m2:"+nonce_and_ciphertext self.send(msg2) self.nextExpectedMessage = 2
def handshake_hello(private_key, redis_client): try: request = expect_json_request(bottle.request, HELLO_SCHEMA) client_transient_pkey = PublicKey( str(request[HELLO_CLIENT_TRANSIENT_PKEY_FIELD]), Base64Encoder) zeros = open_box(request[HELLO_ZEROS_BOX_FIELD], private_key, client_transient_pkey) if len(zeros) != HELLO_PADDING_BYTES: raise InvalidClientRequest( 'zeros_box should contain exactly %d bytes of padding' % HELLO_PADDING_BYTES) transient_skey = PrivateKey.generate() cookie_plain = client_transient_pkey.encode() + \ transient_skey.encode() cookie_nonce = nacl.utils.random(SecretBox.NONCE_SIZE) symmetric_key = nacl.utils.random(SecretBox.KEY_SIZE) cookie_sbox = SecretBox(symmetric_key) cookie = cookie_sbox.encrypt( cookie_plain, cookie_nonce, encoder=Base64Encoder) redis_set_cookie(redis_client, cookie, symmetric_key) cookie_box = Box(private_key, client_transient_pkey) cookie_box_nonce = nacl.utils.random(Box.NONCE_SIZE) server_tpkey = transient_skey.public_key.encode(Base64Encoder) cookie_box_cipher = cookie_box.encrypt(json.dumps({ COOKIE_SERVER_TRANSIENT_PKEY_FIELD: server_tpkey, COOKIE_COOKIE_FIELD: cookie }), cookie_box_nonce, encoder=Base64Encoder) response = {COOKIE_COOKIE_BOX_FIELD: cookie_box_cipher} jsonschema.validate(response, COOKIE_SCHEMA) return response except jsonschema.ValidationError: log.exception(e) bottle.response.status = HTTP_INTERNAL_SERVER_ERROR return {'error': 'A packet with an invalid JSON schema was generated.'} except InvalidClientRequest as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': str(e)} except CryptoError as e: log.exception(e) bottle.response.status = HTTP_BAD_REQUEST return {'error': 'bad encryption'} return {'error': ''}
def on_post(self, req, resp): configuration = req.context['configuration'] if req.content_length: data = json.load(req.bounded_stream) else: raise Exception("No data.") if 'public_key' not in data: raise Exception("No public key.") db_session = req.context['db_session'] user_public_key = PublicKey(data['public_key'].encode('utf-8'), encoder = nacl.encoding.Base64Encoder) user = db_session.query(db.User).filter(db.User.public_key == user_public_key.encode(nacl.encoding.RawEncoder)).first() if user is None: raise Exception("Public key unknown.") challenge_answer = nacl.utils.random(Box.NONCE_SIZE) challenge_uuid = str(uuid.uuid4()) box = Box(configuration['server_key_pair'], user_public_key) challenge_box = box.encrypt(plaintext = challenge_answer, encoder = nacl.encoding.Base64Encoder) challenge = db.Challenge( uuid = challenge_uuid, user = user, answer_hash = nacl.hash.sha256(challenge_answer, encoder = nacl.encoding.RawEncoder) ) db_session.add(challenge) db_session.commit() resp.status = falcon.HTTP_200 resp.body = json.dumps({ 'uuid': challenge_uuid, 'nonce': str(challenge_box.nonce.decode('utf-8')), 'challenge': str(challenge_box.ciphertext.decode('utf-8')) })
def encrypt(msg, private_key, public_key): """ Encrypt message. :param bytes msg: message to be encrypted. :param bytes private_key: Private Key of encrypter. :param bytes public_key: Public Key of decrypter. :returns: The encrypted message. :rtype: nacl.utils.EncryptedMessage """ return Box(PrivateKey(private_key), PublicKey(public_key)).encrypt(msg, random(Box.NONCE_SIZE))
def vouch(self, public_key): """ given the public key of another user, asymmetrically encrypt the master key. this encryption inherently includes authentication so we don't need to also sign this block. """ assert self._master_key is not None pkey = PublicKey(public_key, encoder=nacl.encoding.HexEncoder) box = Box(self.enc_key, pkey) encrypted = box.encrypt(self._master_key) data = {'version': VERSION, 'key': encrypted.hex()} return json.dumps(data).encode('ascii')
def encrypt(self, message, reciever_public_key): """Encrypt the message to send to a receiver. (public key encryption) Args: message (bytes): The message to be encrypted. reciever_public_key (bytes): The receiver's public key. Returns: bytes: The encrypted message """ return Box(self.private_key, PublicKey(reciever_public_key)).encrypt(message)
def test_box_decode(): pub = PublicKey( b"ec2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798", encoder=HexEncoder, ) priv = PrivateKey( b"5c2bee2d5be613ca82e377c96a0bf2220d823ce980cdff6279473edc52862798", encoder=HexEncoder, ) b1 = Box(priv, pub) b2 = Box.decode(b1._shared_key) assert b1._shared_key == b2._shared_key
def test_box_optional_nonce(privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext): pubbob = PublicKey(pubbob, encoder=HexEncoder) privalice = PrivateKey(privalice, encoder=HexEncoder) box = Box(privalice, pubbob) encrypted = box.encrypt(binascii.unhexlify(plaintext), encoder=HexEncoder) decrypted = binascii.hexlify(box.decrypt(encrypted, encoder=HexEncoder)) assert decrypted == plaintext
def from_chain(cls, chain, difficulty=1, difficulty_mode=0): blockchain = cls() blockchain.difficulty = difficulty blockchain.difficulty_mode = difficulty_mode for i in range(0, len(chain)): blockchain.append(chain[i]) blockchain.sort() blockchain.address = blockchain[0]['node_address'] blockchain.public_key = PublicKey(blockchain[0]['public_key']) return blockchain
def decrypt(self, message, sender_public_key): """Decrypt a received message. (public key encryption) Args: message (bytes): The encrypted message. sender_public_key (bytes): The public key of the sender. Returns: bytes: The decrypted message """ return Box(self.private_key, PublicKey(sender_public_key)).decrypt(message)
def test_box_decryption(skalice, pkalice, skbob, pkbob, nonce, plaintext, ciphertext): pkbob = PublicKey(pkbob, encoder=HexEncoder) skalice = PrivateKey(skalice, encoder=HexEncoder) box = Box(skalice, pkbob) nonce = binascii.unhexlify(nonce) decrypted = binascii.hexlify( box.decrypt(ciphertext, nonce, encoder=HexEncoder), ) assert decrypted == plaintext
def test_box_decryption_combined(privalice, pubalice, privbob, pubbob, nonce, plaintext, ciphertext): pubbob = PublicKey(pubbob, encoder=HexEncoder) privalice = PrivateKey(privalice, encoder=HexEncoder) box = Box(privalice, pubbob) combined = binascii.hexlify( binascii.unhexlify(nonce) + binascii.unhexlify(ciphertext), ) decrypted = binascii.hexlify(box.decrypt(combined, encoder=HexEncoder)) assert decrypted == plaintext
def crea_sesion_a(self, nombre): """ Se escribe el archivo como nombreA_nombreB_values :param nombre: Usuario con quien se desea establecer la comunicación :return: devuelve el secreto calculado a partir de las llaves del usuario con el nombre especificado """ dicti = self.solicita_llaves(nombre) if not dicti["ok"]: return "Error:\n" + dicti["detalles"] ikb = PublicKey(base64.b64decode(dicti["llave_identidad"])) mkb = PublicKey(base64.b64decode(dicti["pre_llave"])) # genera la llave efimera efk_priv = PrivateKey.generate() # calcula el secreto como: DH(IKA,MKB) || DH(EKA, IKB) || DH(EKA,MKB) b1 = Box(self.sk, mkb).shared_key() b2 = Box(efk_priv, ikb).shared_key() b3 = Box(efk_priv, mkb).shared_key() secreto = b1 + b2 + b3 # Genera un diccionario para escribir en un archivo las llaves de Bob dicc = { 'ikb': base64.b64encode(ikb.__bytes__()), 'mkb': base64.b64encode(mkb.__bytes__()), 'efk_pub': base64.b64encode(efk_priv.public_key.__bytes__()), 'secreto': base64.b64encode(secreto), 'kenv': "", 'krecib': "", "establecida": False, "tipo": "A" } # escribe el archivo json.dump(dicc, open(self.nombre + "_" + nombre + "_values", "w")) self.secreto = secreto return secreto
def _serialize_sign_encrypt(self, data, serialization_format="json", pubkey_hex=None, nacl=None, threebot=None): """ will sign any data with private key of our local 3bot private key if public_encryption_key given will then encrypt using the pub key given (as binary hex encoded key) :param data: can be a binary blob or a list of items, which will be converted to binary counterpart :param serialization_format: json or msgpack a list of following items int,float,str,binary,list and dict -> stay in native format jsxobject -> jsxobject.json jsxdict -> jsxdict._data which is the source dict format of our jumpscale dict representation this gets serialized using the chosen format result is [3botid,serializeddata,signature] this then gets signed with private key of this threebot the serializeddata optionally gets encrypted with pubkey_hex (the person who asked for the info) :return: [3botid,serializeddata,signature] """ if not nacl: nacl = self._nacl data2 = self._serialize(data, serialization_format=serialization_format) if isinstance(data2, str): data2 = data2.encode() signature = nacl.sign(data2) if threebot: threebot_client = j.clients.threebot.client_get(threebot) data3 = threebot_client.encrypt_for_threebot(data2) tid = threebot_client.tid else: tid = j.tools.threebot.me.default.tid if pubkey_hex: assert len(pubkey_hex) == 64 pubkey = PublicKey(binascii.unhexlify(pubkey_hex)) data3 = nacl.encrypt(data2, public_key=pubkey) else: data3 = data2 return [tid, data3, signature]
def decrypt_from_perma_payments(ciphertext, encoder=encoding.Base64Encoder): """ Decrypt bytes encrypted by perma-payments. """ box = Box( PrivateKey( settings.PERMA_PAYMENTS_ENCRYPTION_KEYS['perma_secret_key'], encoder=encoder ), PublicKey( settings.PERMA_PAYMENTS_ENCRYPTION_KEYS['perma_payments_public_key'], encoder=encoder ) ) return box.decrypt(ciphertext, encoder=encoder)
def handle_N(self, message): """ encryption key negotiation """ pub_key = PublicKey(message[:32]) auth = message[32:64] if blake2b(bytes(pub_key) + get_cas_secret(), encoder=RawEncoder) != auth: raise RuntimeError('Bad Client Auth! %s' % auth) self.client_box = Box(self.private_key, pub_key) if self.debug: print('server public_key: %s' % self.public_key.hex()) print('client public_key: %s' % bytes(pub_key).hex()) return [b'OK']
def rpc_dispute_close(self, sender, pubkey, encrypted): try: box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) res = box.decrypt(encrypted) resolution_json = json.loads(res, object_pairs_hook=OrderedDict) close_dispute(resolution_json, self.db, self.get_message_listener(), self.get_notification_listener(), self.multiplexer.testnet) self.router.addContact(sender) self.log.info("Contract dispute closed by %s" % sender) return ["True"] except Exception: self.log.error("unable to parse disputed close message from %s" % sender) return ["False"]
def encrypt_for_perma_payments(message, encoder=encoding.Base64Encoder): """ Basic public key encryption ala pynacl. """ box = Box( PrivateKey( settings.PERMA_PAYMENTS_ENCRYPTION_KEYS['perma_secret_key'], encoder=encoder ), PublicKey( settings.PERMA_PAYMENTS_ENCRYPTION_KEYS['perma_payments_public_key'], encoder=encoder ) ) return box.encrypt(message, encoder=encoder)
def createMsgA(trec, msgC): MSTID = rrid.randomize(trec["STID"].decode("hex")) msgB = netstring(MSTID) + msgC privkey1 = PrivateKey.generate() pubkey1 = privkey1.public_key.encode() assert len(pubkey1) == 32 transport_pubkey = trec["transport_pubkey"].decode("hex") transport_box = Box(privkey1, PublicKey(transport_pubkey)) boxed = transport_box.encrypt(msgB, os.urandom(Box.NONCE_SIZE)) msgA = b"".join([b"a0:", pubkey1, boxed]) return msgA
def create_properties(self): try: rk = os.environ.get('KNOWN_RK') or \ "d90cacd31e22c63ce99f062e88d6d2734e944e8d3dac895a67472701b6c55c7e" uk = os.environ.get('KNOWN_UK') or \ "1a41da8aa64dc15e26e8ca787d35d559c10774d4a8e8c373418a0b0862f6567c" except: rk = 'd90cacd31e22c63ce99f062e88d6d2734e944e8d3dac895a67472701b6c55c7e' uk = '1a41da8aa64dc15e26e8ca787d35d559c10774d4a8e8c373418a0b0862f6567c' self.rk = PrivateKey(private_key=bytes(rk, 'utf-8'), encoder=nacl.encoding.HexEncoder) self.uk = PublicKey(public_key=bytes(uk, 'utf-8'), encoder=nacl.encoding.HexEncoder)
def test_login_with_wrong_password(self): """ Ensure we cannot login with wrong authkey """ # our public / private key box box = PrivateKey.generate() # our hex encoded public / private keys user_session_private_key_hex = box.encode( encoder=nacl.encoding.HexEncoder).decode() user_session_public_key_hex = box.public_key.encode( encoder=nacl.encoding.HexEncoder).decode() server_crypto_box = Box( PrivateKey(user_session_private_key_hex, encoder=nacl.encoding.HexEncoder), PublicKey(settings.PUBLIC_KEY, encoder=nacl.encoding.HexEncoder)) login_info_nonce = nacl.utils.random(nacl.secret.SecretBox.NONCE_SIZE) encrypted = server_crypto_box.encrypt( json.dumps({ 'username': self.test_username, 'authkey': make_password( binascii.hexlify(os.urandom( settings.AUTH_KEY_LENGTH_BYTES)).decode()), }).encode("utf-8"), login_info_nonce) login_info_encrypted = encrypted[len(login_info_nonce):] data = { 'login_info': nacl.encoding.HexEncoder.encode(login_info_encrypted).decode(), 'login_info_nonce': nacl.encoding.HexEncoder.encode(login_info_nonce).decode(), 'public_key': user_session_public_key_hex, } url = reverse('authentication_login') models.Token.objects.all().delete() response = self.client.post(url, data) self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST) self.assertEqual(response.data.get('non_field_errors'), [u'USERNAME_OR_PASSWORD_WRONG']) self.assertEqual(models.Token.objects.count(), 0)
def rpc_complete_order(self, sender, pubkey, encrypted): try: box = Box(PrivateKey(self.signing_key.encode(nacl.encoding.RawEncoder)), PublicKey(pubkey)) order = box.decrypt(encrypted) c = Contract(self.db, contract=json.loads(order, object_pairs_hook=OrderedDict), testnet=self.multiplexer.testnet) contract_id = c.accept_receipt(self.multiplexer.ws, self.multiplexer.blockchain) self.router.addContact(sender) self.log.info("received receipt for order %s" % contract_id) return ["True"] except Exception: self.log.error("unable to parse receipt from %s" % sender) return ["False"]
def recv_hello_response(self, body): hello = HelloResponse() hello.parse_from_bytes(body) signed_data = HelloSignedData() signed_data.parse_from_bytes(hello.signedData) self.secure = True self.pub_key = PublicKey(signed_data.serverPublicKey) self.box = Box(self.priv_key, self.pub_key) # please forgive me for writing this monstrosity shell = InteractiveShellEmbed() threading.Thread(target=shell, kwargs={"local_ns": {"client": self}}).start()
def _get_public_key(): """ Get the configured PublicKey instance Returns: PublicKey: the public key as configured in settings """ if not settings.EXAMS_AUDIT_NACL_PUBLIC_KEY: raise ImproperlyConfigured( "EXAMS_AUDIT_NACL_PUBLIC_KEY is required but not set" ) return PublicKey(settings.EXAMS_AUDIT_NACL_PUBLIC_KEY, encoder=Base64Encoder)
def rpc_refund(self, sender, pubkey, encrypted): try: box = Box(self.signing_key.to_curve25519_private_key(), PublicKey(pubkey)) refund = box.decrypt(encrypted) refund_json = json.loads(refund, object_pairs_hook=OrderedDict) c = Contract(self.db, hash_value=unhexlify(refund_json["refund"]["order_id"]), testnet=self.multiplexer.testnet) c.process_refund(refund_json, self.multiplexer.blockchain, self.get_notification_listener()) self.router.addContact(sender) self.log.info("order %s refunded by vendor" % refund_json["refund"]["order_id"]) return ["True"] except Exception, e: self.log.error("unable to parse refund message from %s" % sender) return [e.message]
def _do_connect(config: Config) -> SocketPort: if (not config.server_verify_key or not config.signing_key or not config.server_address): print( "Using permuter@home requires someone to give you access to a central -J server.\n" "Run `./pah.py setup` to set this up.") print() sys.exit(1) host, port_str = config.server_address.split(":") try: sock = socket.create_connection((host, int(port_str))) except ConnectionRefusedError: raise EOFError("connection refused") from None except socket.gaierror as e: raise EOFError(f"DNS lookup failed: {e}") from None except Exception as e: raise EOFError("unable to connect: " + exception_to_string(e)) from None # Send over the protocol version and an ephemeral encryption key which we # are going to use for all communication. ephemeral_key = PrivateKey.generate() ephemeral_key_data = ephemeral_key.public_key.encode() sock.sendall(b"p@h0" + ephemeral_key_data) # Receive the server's encryption key, plus a signature of it and our own # ephemeral key -- this guarantees that we are talking to the server and # aren't victim to a replay attack. Use it to set up a communication port. msg = socket_read_fixed(sock, 32 + 64) server_enc_key_data = msg[:32] config.server_verify_key.verify( b"HELLO:" + ephemeral_key_data + server_enc_key_data, msg[32:]) box = Box(ephemeral_key, PublicKey(server_enc_key_data)) port = SocketPort(sock, box, "controller", is_client=True) # Use the encrypted port to send over our public key, proof that we are # able to sign new things with it, as well as permuter version. signature: bytes = config.signing_key.sign(b"WORLD:" + server_enc_key_data).signature port.send(config.signing_key.verify_key.encode() + signature + struct.pack(">I", PERMUTER_VERSION)) # Get an acknowledgement that the server wants to talk to us. obj = port.receive_json() if "message" in obj: print(obj["message"]) return port
def connect(self, path=None): if path is None: path = Connection.get_socket_path() self.socket.connect(path) message = json.dumps(self.change_public_keys()) self.socket.sendall(message.encode("utf-8")) response = self.get_unencrypted_response() if not response["success"]: raise ResponseUnsuccesfulException self.box = Box(self.private_key, PublicKey(base64.b64decode(response["publicKey"]))) self.nonce = (int.from_bytes(self.nonce, "big") + 1).to_bytes( 24, "big")
def test_sealed_box_encryption(privalice, pubalice, plaintext, _encrypted): pubalice = PublicKey(pubalice, encoder=HexEncoder) privalice = PrivateKey(privalice, encoder=HexEncoder) box = SealedBox(pubalice) encrypted = box.encrypt(binascii.unhexlify(plaintext), encoder=HexEncoder,) assert encrypted != _encrypted # since SealedBox.encrypt uses an ephemeral sender's keypair box2 = SealedBox(privalice) decrypted = box2.decrypt(encrypted, encoder=HexEncoder,) assert binascii.hexlify(decrypted) == plaintext assert bytes(box) == bytes(box2)
def test_msgA(self): nA, nB, entA, entB = self.make_nodes() msgC = "msgC" trec = json.loads(entA["their_channel_record_json"])["transports"][0] msgA = createMsgA(trec, msgC) pubkey1_s, boxed = parseMsgA(msgA) tpriv = self.tport2[0]["privkey"] b = Box(tpriv, PublicKey(pubkey1_s)) msgB = b.decrypt(boxed) MSTID, msgC2 = parseMsgB(msgB) self.failUnlessEqual(msgC, msgC2)
def send_message(self, receiving_node, public_key, message_type, message, subject=None, store_only=False): """ Sends a message to another node. If the node isn't online it will be placed in the dht for the node to pick up later. """ pro = Profile(self.db).get() p = objects.Plaintext_Message() p.sender_guid = self.kserver.node.id p.signed_pubkey = self.kserver.node.signed_pubkey p.encryption_pubkey = PrivateKey( self.signing_key.encode()).public_key.encode() p.type = message_type p.message = message if subject is not None: p.subject = subject if pro.handle: p.handle = pro.handle if pro.avatar_hash: p.avatar_hash = pro.avatar_hash p.timestamp = int(time.time()) signature = self.signing_key.sign(p.SerializeToString())[:64] p.signature = signature skephem = PrivateKey.generate() pkephem = skephem.public_key.encode(nacl.encoding.RawEncoder) box = Box(skephem, PublicKey(public_key, nacl.encoding.HexEncoder)) nonce = nacl.utils.random(Box.NONCE_SIZE) ciphertext = box.encrypt(p.SerializeToString(), nonce) def get_response(response): if not response[0]: ciphertext = box.encrypt(p.SerializeToString().encode("zlib"), nonce) self.kserver.set(digest(receiving_node.id), pkephem, ciphertext) self.log.info("sending encrypted message to %s" % receiving_node.id.encode("hex")) if not store_only: self.protocol.callMessage(receiving_node, pkephem, ciphertext).addCallback(get_response) else: get_response([False])
def identify(self, data): if not self.disable_encryption: from nacl.public import PublicKey cmd_data = data["data"] try: self.name = cmd_data["kwargs"]["name"] print(cmd_data["kwargs"]["public_key"],len(cmd_data["kwargs"]["public_key"])) if not self.disable_encryption: self.public_key = PublicKey(bytes([int(x) for x in base64.b64decode(cmd_data["kwargs"]["public_key"]).decode('utf-8').split(',')])) self.identified = True self.sendMsg( commandmessage(sender="server", cmd="set_time", time=self.server.t0) ) except Exception as e: self.server.logger.exception(e)
class TestPublicKey: def test_equal_keys_have_equal_hashes(self): k1 = PublicKey(b"\x00" * crypto_box_PUBLICKEYBYTES) k2 = PublicKey(b"\x00" * crypto_box_PUBLICKEYBYTES) assert hash(k1) == hash(k2) assert id(k1) != id(k2) def test_equal_keys_are_equal(self): k1 = PublicKey(b"\x00" * crypto_box_PUBLICKEYBYTES) k2 = PublicKey(b"\x00" * crypto_box_PUBLICKEYBYTES) assert_equal(k1, k1) assert_equal(k1, k2) @pytest.mark.parametrize( "k2", [ b"\x00" * crypto_box_PUBLICKEYBYTES, PublicKey(b"\x01" * crypto_box_PUBLICKEYBYTES), PublicKey(b"\x00" * (crypto_box_PUBLICKEYBYTES - 1) + b"\x01"), ], ) def test_different_keys_are_not_equal(self, k2): k1 = PublicKey(b"\x00" * crypto_box_PUBLICKEYBYTES) assert_not_equal(k1, k2)
def encrypt(self, packetID, payload): if packetID == 10100: return payload elif packetID == 10101: self.encryptNonce = Nonce() self.Nonce = Nonce(clientKey=self.pk, serverKey=self.serverKey) self.sharedKey = Box(self.sk, PublicKey(self.serverKey)) return bytes(self.pk) + self.encryptPacket(self.sessionKey + bytes(self.encryptNonce) + payload, self.Nonce) else: return self.encryptPacket(payload)
def __init__(self, iid, db, manager): self.iid = iid self.db = db self.manager = manager c = self.db.execute("SELECT petname, inviteID, inviteKey," # 0,1,2 " theirTempPubkey," # 3 " nextExpectedMessage," # 4 " myMessages," # 5 " theirMessages" # 6 " FROM invitations WHERE id = ?", (iid,)) res = c.fetchone() if not res: raise KeyError("no pending Invitation for '%d'" % iid) self.petname = res[0] self.inviteID = res[1] self.inviteKey = SigningKey(res[2].decode("hex")) self.theirTempPubkey = None if res[3]: self.theirTempPubkey = PublicKey(res[3].decode("hex")) self.nextExpectedMessage = int(res[4]) self.myMessages = splitMessages(res[5]) self.theirMessages = splitMessages(res[6])
def on_post(self, req, resp): configuration = req.context['configuration'] if req.content_length: data = json.load(req.bounded_stream) else: raise Exception("No data.") if 'public_key' not in data: raise Exception("No public key.") if 'captcha' not in data: raise Exception("No captcha.") if 'uuid' not in data['captcha']: raise Exception("No captcha UUID.") if 'encrypted_answer' not in data['captcha']: raise Exception("No captcha encrypted answer.") if 'nonce' not in data['captcha']: raise Exception("No captcha nonce.") # Decrypt captcha answer user_public_key = PublicKey(data['public_key'].encode('utf-8'), encoder = nacl.encoding.Base64Encoder) captcha_encrypted_answer = base64.b64decode(data['captcha']['encrypted_answer'].encode('utf-8')) captcha_nonce = base64.b64decode(data['captcha']['nonce'].encode('utf-8')) box = Box(configuration['server_key_pair'], user_public_key) captcha_answer = box.decrypt(captcha_encrypted_answer, captcha_nonce).decode('utf-8').lower() # Get captcha from database db_session = req.context['db_session'] captcha = db_session.query(db.Captcha).filter(db.Captcha.uuid == data['captcha']['uuid']).first() # Whatever happens, delete the captcha from database db_session.delete(captcha) db_session.commit() # Check the IP hash of the user who requested the captcha matches the current client's IP hash ip_address_hash = nacl.hash.sha256(str.encode(captcha.uuid + req.remote_addr), encoder = nacl.encoding.RawEncoder) user_agent_hash = nacl.hash.sha256(str.encode(captcha.uuid + req.user_agent), encoder = nacl.encoding.RawEncoder) answer_hash = nacl.hash.sha256(str.encode(captcha.uuid + captcha_answer), encoder = nacl.encoding.RawEncoder) if (ip_address_hash != captcha.ip_address_hash) or (user_agent_hash != captcha.user_agent_hash): raise Exception("Captcha answer incorrect.") # Check the captcha answer if captcha.answer_hash != answer_hash: raise Exception("Captcha answer incorrect.") # Create new user user = db.User( public_key = user_public_key.encode(nacl.encoding.RawEncoder) ) db_session.add(user) db_session.commit() resp.status = falcon.HTTP_201 resp.body = json.dumps({})
# Info ServerInfo = '{"Name":"Example","Url":"https://noob.example.com/sendOOB"}' PeerInfo = '{"Make":"Acme","Type":"None","Serial":"DU-9999","SSID":"Noob1",\ "BSSID":"6c:19:8f:83:c2:80"}' # KeyingMode KeyingMode = 2 # SleepTime SleepTime = 60 ################################################################################ ############################## CALCULATED VALUES ############################## ## Load peer and server public keys PK_peer = PublicKey(bytes.fromhex(PKp)) PK_server = PublicKey(bytes.fromhex(PKs)) #PK2_peer = PublicKey(bytes.fromhex(PKp)) #PK2_server = PublicKey(bytes.fromhex(PKs)) # Peer and server public keys - base64 encoded PKp_b64 = base64url_encode(bytes.fromhex(PKp)).decode().strip('=') PKs_b64 = base64url_encode(bytes.fromhex(PKs)).decode().strip('=') #PKp2_b64 = base64url_encode(bytes.fromhex(PKp2)).decode().strip('=') #PKs2_b64 = base64url_encode(bytes.fromhex(PKs2)).decode().strip('=') # Peer and server public keys - jwk formatted PKp_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', object_pairs_hook=OrderedDict) PKs_full = loads('{"kty":"EC", "crv":"Curve25519", "x":""}', object_pairs_hook=OrderedDict)
class Invitation: # This has a brief lifetime: one is created in response to the rendezvous # client discovering new messages for us, used for one reactor tick, then # dereferenced. It holds onto a few values during that tick (which may # process multiple messages for a single invitation, e.g. A's second poll # will receive both B-m1 and B-m2 together). But all persistent state # beyond that one tick is stored in the database. def __init__(self, iid, db, manager): self.iid = iid self.db = db self.manager = manager c = self.db.execute("SELECT petname, inviteID, inviteKey," # 0,1,2 " theirTempPubkey," # 3 " nextExpectedMessage," # 4 " myMessages," # 5 " theirMessages" # 6 " FROM invitations WHERE id = ?", (iid,)) res = c.fetchone() if not res: raise KeyError("no pending Invitation for '%d'" % iid) self.petname = res[0] self.inviteID = res[1] self.inviteKey = SigningKey(res[2].decode("hex")) self.theirTempPubkey = None if res[3]: self.theirTempPubkey = PublicKey(res[3].decode("hex")) self.nextExpectedMessage = int(res[4]) self.myMessages = splitMessages(res[5]) self.theirMessages = splitMessages(res[6]) def getAddressbookID(self): c = self.db.execute("SELECT addressbook_id FROM invitations" " WHERE id = ?", (self.iid,)) return c.fetchone()[0] def getMyTempPrivkey(self): c = self.db.execute("SELECT myTempPrivkey FROM invitations" " WHERE id = ?", (self.iid,)) return PrivateKey(c.fetchone()[0].decode("hex")) def getMySigningKey(self): c = self.db.execute("SELECT mySigningKey FROM invitations" " WHERE id = ?", (self.iid,)) return SigningKey(c.fetchone()[0].decode("hex")) def getMyPublicChannelRecord(self): c = self.db.execute("SELECT my_channel_record FROM invitations" " WHERE id = ?", (self.iid,)) return c.fetchone()[0] def getMyPrivateChannelData(self): c = self.db.execute("SELECT my_private_channel_data FROM invitations" " WHERE id = ?", (self.iid,)) return json.loads(c.fetchone()[0]) def sendFirstMessage(self): pub = self.getMyTempPrivkey().public_key.encode() self.send("i0:m1:"+pub) self.db.update("UPDATE invitations SET myMessages=? WHERE id=?", (",".join(self.myMessages), self.iid), "invitations", self.iid) # that will be commited by our caller def processMessages(self, messages): # These messages are neither version-checked nor signature-checked. # Also, we may have already processed some of them. #print "processMessages", messages #print " my", self.myMessages #print " theirs", self.theirMessages assert isinstance(messages, set), type(messages) assert None not in messages, messages assert None not in self.myMessages, self.myMessages assert None not in self.theirMessages, self.theirMessages # Send anything that didn't make it to the server. This covers the # case where we commit our outbound message in send() but crash # before finishing delivery. for m in self.myMessages - messages: #print "resending", m self.manager.sendToAll(self.inviteID, m) newMessages = messages - self.myMessages - self.theirMessages #print " %d new messages" % len(newMessages) if not newMessages: print " huh, no new messages, stupid rendezvous client" # check signatures, extract bodies. invalid messages kill the channel # and the invitation. MAYBE TODO: lose the one channel, keep using # the others. bodies = set() for m in newMessages: #print " new inbound message", m try: if not m.startswith("r0:"): print "unrecognized rendezvous message prefix" if not VALID_MESSAGE.search(m): raise CorruptChannelError() m = m[len("r0:"):].decode("hex") bodies.add(self.inviteKey.verify_key.verify(m)) except (BadSignatureError, CorruptChannelError) as e: print "channel %s is corrupt" % self.inviteID if isinstance(e, BadSignatureError): print " (bad sig)" self.unsubscribe(self.inviteID) # TODO: mark invitation as failed, destroy it return #print " new inbound bodies:", ", ".join([repr(b[:10])+" ..." for b in bodies]) # these handlers will update self.myMessages with sent messages, and # will increment self.nextExpectedMessage. We can handle multiple # (sequential) messages in a single pass. if self.nextExpectedMessage == 1: self.findPrefixAndCall("i0:m1:", bodies, self.processM1) # no elif here: self.nextExpectedMessage may have incremented if self.nextExpectedMessage == 2: self.findPrefixAndCall("i0:m2:", bodies, self.processM2) if self.nextExpectedMessage == 3: self.findPrefixAndCall("i0:m3:", bodies, self.processM3) self.db.update("UPDATE invitations SET" " myMessages=?," " theirMessages=?," " nextExpectedMessage=?" " WHERE id=?", (",".join(self.myMessages), ",".join(self.theirMessages | newMessages), self.nextExpectedMessage, self.iid), "invitations", self.iid) #print " db.commit" self.db.commit() def findPrefixAndCall(self, prefix, bodies, handler): for msg in bodies: if msg.startswith(prefix): return handler(msg[len(prefix):]) return None def send(self, msg, persist=True): #print "send", repr(msg[:10]), "..." signed = "r0:%s" % self.inviteKey.sign(msg).encode("hex") if persist: # m4-destroy is not persistent self.myMessages.add(signed) # will be persisted by caller # This will be added to the DB, and committed, by our caller, to # get it into the same transaction as the update to which inbound # messages we've processed. assert VALID_MESSAGE.search(signed), signed self.manager.sendToAll(self.inviteID, signed) def processM1(self, msg): #print "processM1", self.petname self.theirTempPubkey = PublicKey(msg) self.db.update("UPDATE invitations SET theirTempPubkey=?" " WHERE id=?", (self.theirTempPubkey.encode(Hex), self.iid), "invitations", self.iid) # theirTempPubkey will committed by our caller, in the same txn as # the message send my_privkey = self.getMyTempPrivkey() my_channel_record = self.getMyPublicChannelRecord() b = Box(my_privkey, self.theirTempPubkey) signedBody = b"".join([self.theirTempPubkey.encode(), my_privkey.public_key.encode(), my_channel_record.encode("utf-8")]) my_sign = self.getMySigningKey() body = b"".join([b"i0:m2a:", my_sign.verify_key.encode(), my_sign.sign(signedBody) ]) nonce = os.urandom(Box.NONCE_SIZE) nonce_and_ciphertext = b.encrypt(body, nonce) #print "ENCRYPTED n+c", len(nonce_and_ciphertext), nonce_and_ciphertext.encode("hex") #print " nonce", nonce.encode("hex") msg2 = "i0:m2:"+nonce_and_ciphertext self.send(msg2) self.nextExpectedMessage = 2 def processM2(self, msg): #print "processM2", repr(msg[:10]), "...", self.petname assert self.theirTempPubkey nonce_and_ciphertext = msg my_privkey = self.getMyTempPrivkey() b = Box(my_privkey, self.theirTempPubkey) #nonce = msg[:Box.NONCE_SIZE] #ciphertext = msg[Box.NONCE_SIZE:] #print "DECRYPTING n+ct", len(msg), msg.encode("hex") body = b.decrypt(nonce_and_ciphertext) if not body.startswith("i0:m2a:"): raise ValueError("expected i0:m2a:, got '%r'" % body[:20]) verfkey_and_signedBody = body[len("i0:m2a:"):] theirVerfkey = VerifyKey(verfkey_and_signedBody[:32]) signedBody = verfkey_and_signedBody[32:] body = theirVerfkey.verify(signedBody) check_myTempPubkey = body[:32] check_theirTempPubkey = body[32:64] their_channel_record_json = body[64:].decode("utf-8") #print " binding checks:" #print " check_myTempPubkey", check_myTempPubkey.encode("hex") #print " my real tempPubkey", my_privkey.public_key.encode(Hex) #print " check_theirTempPubkey", check_theirTempPubkey.encode("hex") #print " first theirTempPubkey", self.theirTempPubkey.encode(Hex) if check_myTempPubkey != my_privkey.public_key.encode(): raise ValueError("binding failure myTempPubkey") if check_theirTempPubkey != self.theirTempPubkey.encode(): raise ValueError("binding failure theirTempPubkey") them = json.loads(their_channel_record_json) me = self.getMyPrivateChannelData() addressbook_id = self.db.insert( "INSERT INTO addressbook" " (petname, acked," " next_outbound_seqnum, my_signkey," " their_channel_record_json," " my_CID_key, next_CID_token," " highest_inbound_seqnum," " my_old_channel_privkey, my_new_channel_privkey," " they_used_new_channel_key, their_verfkey)" " VALUES (?,?, " " ?,?," " ?," " ?,?," # my_CID_key, next_CID_token " ?," # highest_inbound_seqnum " ?,?," " ?,?)", (self.petname, 0, 1, me["my_signkey"], json.dumps(them), me["my_CID_key"], None, 0, me["my_old_channel_privkey"], me["my_new_channel_privkey"], 0, theirVerfkey.encode(Hex) ), "addressbook") self.db.update("UPDATE invitations SET addressbook_id=?" " WHERE id=?", (addressbook_id, self.iid), "invitations", self.iid) msg3 = "i0:m3:ACK-"+os.urandom(16) self.send(msg3) self.nextExpectedMessage = 3 def processM3(self, msg): #print "processM3", repr(msg[:10]), "..." if not msg.startswith("ACK-"): raise ValueError("bad ACK") cid = self.getAddressbookID() self.db.update("UPDATE addressbook SET acked=1 WHERE id=?", (cid,), "addressbook", cid ) self.db.delete("DELETE FROM invitations WHERE id=?", (self.iid,), "invitations", self.iid) # we no longer care about the channel msg4 = "i0:destroy:"+os.urandom(16) self.send(msg4, persist=False) self.manager.unsubscribe(self.inviteID)