def generate_zos_keys(node_public_key): """Generate a new set of wireguard key pair and encrypt the private side using the public key of a 0-OS node. Args: node_public_key (str): hex encoded public key of 0-OS node. This is the format you find in the explorer Returns: tuple: tuple containing 3 fields (private key, private key encrypted, public key) """ wg_private = public.PrivateKey.generate() wg_public = wg_private.public_key wg_private_base64 = wg_private.encode(Base64Encoder) wg_public_base64 = wg_public.encode(Base64Encoder) node_public_bin = binascii.unhexlify(node_public_key) node_public = VerifyKey(node_public_bin) box = public.SealedBox(node_public.to_curve25519_public_key()) wg_private_encrypted = box.encrypt(wg_private_base64) wg_private_encrypted_hex = binascii.hexlify(wg_private_encrypted) return (wg_private_base64.decode(), wg_private_encrypted_hex.decode(), wg_public_base64.decode())
def generate_zos_keys(self, node_public_key): """ Generate a new set of wireguard key pair and encrypt the private side using the public key of a 0-OS node. This implementation match the format 0-OS except to be able to read wireguard keys into network reservations. :param node_public_key: hex encoded public key of 0-OS node. This is the format you find in the explorer :type node_public_key: str :return: tuple containing 3 fields (private key, private key encrypted, public key) :rtype: typle """ wg_private_base64, wg_public_base64 = self.generate_key_pair() node_public_bin = j.data.hash.hex2bin(node_public_key) node_public = VerifyKey(node_public_bin) box = SealedBox(node_public.to_curve25519_public_key()) wg_private_encrypted = box.encrypt(wg_private_base64) wg_private_encrypted_hex = j.data.hash.bin2hex(wg_private_encrypted) return (wg_private_base64.decode(), wg_private_encrypted_hex.decode(), wg_public_base64.decode())
def encrypt_password(password, public_key): node_public_bin = j.data.hash.hex2bin(public_key) node_public = VerifyKey(node_public_bin) box = SealedBox(node_public.to_curve25519_public_key()) pasword_encrypted = box.encrypt(password.encode()) pasword_encrypted_hex = j.data.hash.bin2hex(pasword_encrypted) return pasword_encrypted_hex
def run_vouch(magic: str) -> None: try: vouch_data = base64.b64decode(magic.encode("utf-8")) verify_key = VerifyKey(vouch_data[:32]) signed_nickname = vouch_data[32:] msg = verify_with_magic(b"NAME", verify_key, signed_nickname) nickname = msg.decode("utf-8") except Exception: print("Could not parse data!") sys.exit(1) try: config = read_config() port = connect(config) port.send_json( { "method": "vouch", "who": verify_key.encode(HexEncoder).decode("utf-8"), "signed_name": HexEncoder.encode(signed_nickname).decode("utf-8"), } ) port.receive_json() except Exception as e: print(f"Error: {e}") sys.exit(1) if not ask(f"Grant permuter server access to {nickname}", default=True): return try: port.send_json({}) port.receive_json() except Exception as e: print(f"Failed to grant access: {e}") sys.exit(1) assert config.server_address, "checked by connect" assert config.server_verify_key, "checked by connect" data = config.server_verify_key.encode() + config.server_address.encode("utf-8") token = SealedBox(verify_key.to_curve25519_public_key()).encrypt(data) print("Granted!") print() print("Send them the following token:") print(base64.b64encode(token).decode("utf-8"))
def encrypt_for_node(public_key: str, payload: Union[str, bytes]) -> str: """encrypt payload with the public key of a node so only the node itself can decrypt it use this if you have sensitive data to send in a reservation Args: public_key(str): public key of the node, hex-encoded payload(Union[str, bytes]): any data you want to encrypt Returns: str: hex-encoded encrypted data. you can use this safely into your reservation data """ node_public_bin = binascii.unhexlify(public_key) node_public = VerifyKey(node_public_bin) box = SealedBox(node_public.to_curve25519_public_key()) if isinstance(payload, str): payload = payload.encode() encrypted = box.encrypt(payload) return binascii.hexlify(encrypted)
class SecretHandshakeEnvelopeFactory(object): """ i am a factory for building cryptographic envelopes for the secret-handshake protocol as described in this document: https://github.com/dominictarr/secret-handshake-paper """ application_key = attr.ib(validator=is_32bytes) local_ephemeral_key = attr.ib( validator=attr.validators.instance_of(Curve25519KeyPair)) local_signing_key = attr.ib( validator=attr.validators.instance_of(Ed25519KeyPair)) remote_longterm_pub_key = attr.ib(validator=is_VerifyKeyOrNone) _remote_ephemeral_pub_key = attr.ib( init=False, validator=attr.validators.instance_of(PublicKey)) _remote_app_mac = attr.ib(init=False, validator=is_32bytes) _secret = attr.ib(init=False, validator=is_32bytes) _hashed_secret = attr.ib(init=False, validator=is_32bytes) _shared_secret = attr.ib(init=False) def __attrs_post_init__(self): nonce = bytes(b"\x00" * 24) upstream_secret = hashlib.sha256( bytes(self._shared_secret) + bytes(self._remote_ephemeral_pub_key)).digest() downstream_secret = hashlib.sha256( bytes(self._shared_secret) + bytes(self.local_ephemeral_key.public_key)).digest() if sorted([ bytes(self._remote_ephemeral_pub_key), bytes(self.local_ephemeral_key.public_key) ])[0] == bytes(self._remote_ephemeral_pub_key): self.upstream_box = BoxStream(upstream_secret, nonce) self.downstream_box = BoxStream(downstream_secret, nonce) else: self.downstream_box = BoxStream(upstream_secret, nonce) self.upstream_box = BoxStream(downstream_secret, nonce) def create_client_challenge(self): """ on behalf of a client i create a challenge envelope, the first envelope in our handshake protocol, the mathematical representation of this cryptographic envelope is as follows: a_pub, hmac_{K}(a_pub) where: K = <<this is the application key>> """ h = hmac.HMAC(self.application_key, hashes.SHA512(), backend=default_backend()) h.update(bytes(self.local_ephemeral_key.public_key)) client_auth_hmac = h.finalize() return client_auth_hmac[:32] + bytes( self.local_ephemeral_key.public_key) def is_client_challenge_verified(self, challenge): """ this is used by the server side. if i can verify the challenge then return True otherwise return False """ assert len(challenge) == 64 mac = challenge[:32] remote_ephemeral_pub_key = challenge[32:64] h = hmac.HMAC(self.application_key[:32], hashes.SHA512(), backend=default_backend()) h.update(bytes(remote_ephemeral_pub_key)) new_hash = h.finalize()[:32] ok = new_hash == mac self._remote_ephemeral_pub_key = PublicKey(remote_ephemeral_pub_key) self._remote_app_mac = mac self._secret = crypto_scalarmult( bytes(self.local_ephemeral_key.private_key), remote_ephemeral_pub_key) self._hashed_secret = hashlib.sha256(self._secret).digest() return ok def create_server_challenge(self): """ this is the challenge envelope that the server sends to the client. this envelope's crypto math representation: b_pub, hmac_{[ K | crypto_scalarmult(b_priv, a_pub) ]}(b_pub) where: K = <<this is the application key>> """ scalar = crypto_scalarmult(bytes(self.local_ephemeral_key.private_key), bytes(self._remote_ephemeral_pub_key)) hmac_key = self.application_key + scalar h = hmac.HMAC(hmac_key, hashes.SHA512(), backend=default_backend()) h.update(bytes(self.local_ephemeral_key.public_key)) _hmac = h.finalize() return _hmac[:32] + bytes(self.local_ephemeral_key.public_key) def is_server_challenge_verified(self, challenge): """ this is used by the client side to verify a challenge from the server. if verified returns True, otherwise returns False. """ assert len(challenge) == 64 mac = challenge[:32] remote_ephemeral_pub_key = challenge[32:64] scalar_val = crypto_scalarmult( bytes(self.local_ephemeral_key.private_key), bytes(remote_ephemeral_pub_key)) hmac_key = self.application_key[:32] + scalar_val h = hmac.HMAC(hmac_key, hashes.SHA512(), backend=default_backend()) h.update(bytes(remote_ephemeral_pub_key)) new_hmac = h.finalize()[:32] ok = new_hmac == mac self._remote_ephemeral_pub_key = PublicKey(remote_ephemeral_pub_key) self._remote_app_mac = mac self._secret = crypto_scalarmult( bytes(self.local_ephemeral_key.private_key), remote_ephemeral_pub_key) self._hashed_secret = hashlib.sha256(self._secret).digest() return ok def create_client_auth(self): """ this is the client authentication cryptographic envelope which the client sends to the server. it's mathematical representation is as follows: Box_{box_secret}(data_to_box) where: box_secret = hash([K | crypto_scalarmult(a_priv, b_pub) | crypto_scalarmult(a_priv, B_pub)]) data_to_box = A_pub | Sign_A_priv( K | B_pub | hash(crypto_scalarmult(a_priv, b_pub))) K = <<this is the application key>> """ hasher = hashlib.sha256() scalar = crypto_scalarmult(bytes(self.local_ephemeral_key.private_key), bytes(self._remote_ephemeral_pub_key)) hasher.update(scalar) hashed_value = hasher.digest() signed_message = self.local_signing_key.private_key.sign( self.application_key + bytes(self.remote_longterm_pub_key) + bytes(hashed_value)) message_to_box = bytes( self.local_signing_key.public_key) + signed_message.signature self._client_auth = message_to_box scalar_remote_longterm = crypto_scalarmult( bytes(self.local_ephemeral_key.private_key), bytes(self.remote_longterm_pub_key.to_curve25519_public_key())) hasher = hashlib.sha256() # XXX hasher.update(self.application_key + scalar + scalar_remote_longterm) box_secret = hasher.digest() nonce = b"\x00" * 24 return crypto_box_afternm(message_to_box, nonce, box_secret) def verify_client_auth(self, client_auth): """ verify the client auth crypto envelope. this operation is performed by the server when it receives a client auth message. """ scalar = crypto_scalarmult(bytes(self.local_ephemeral_key.private_key), bytes(self._remote_ephemeral_pub_key)) scalar_remote_longterm = crypto_scalarmult( bytes(self.local_signing_key.private_key.to_curve25519_private_key( )), bytes(self._remote_ephemeral_pub_key)) hasher = hashlib.sha256() hasher.update(self.application_key + scalar + scalar_remote_longterm) box_secret = hasher.digest() nonce = b"\x00" * 24 message = crypto_box_open_afternm(client_auth, nonce, box_secret) self._client_vouch = message self.remote_longterm_pub_key = VerifyKey(message[:32]) signature = message[32:] hasher = hashlib.sha256() scalar = crypto_scalarmult(bytes(self.local_ephemeral_key.private_key), bytes(self._remote_ephemeral_pub_key)) hasher.update(scalar) hashed_value = hasher.digest() signed_message = self.application_key + bytes( self.local_signing_key.public_key) + hashed_value self.remote_longterm_pub_key.verify(signed_message, signature=signature) def create_server_accept(self): """ create a server accept crypto envelope. this envelope is sent from the server to the client. it's math representation is the following: Box_{box_secret}(data_to_box) where: data_to_box = Sign_B( K | H | hash(crypto_scalarmult(b_priv, a_pub)) box_secret = hash([ K | crypto_scalarmult(b_priv, a_pub) | crypto_scalarmult(B_priv, a_pub) | crypto_scalarmult(b_priv, A_pub) ]) H = A_pub | Sign_A_priv( K | B_pub | hash(crypto_scalarmult(b_priv, a_pub))) K = <<this is the application key>> """ message_to_sign = self.application_key + self._client_vouch + self._hashed_secret signed_message = self.local_signing_key.private_key.sign( message_to_sign) message_to_box = signed_message.signature local_longterm_sharedsecret = crypto_scalarmult( bytes(self.local_signing_key.private_key.to_curve25519_private_key( )), bytes(self._remote_ephemeral_pub_key)) remote_longterm_sharedsecret = crypto_scalarmult( bytes(self.local_ephemeral_key.private_key), bytes(self.remote_longterm_pub_key.to_curve25519_public_key())) to_hash = self.application_key + self._secret + local_longterm_sharedsecret + remote_longterm_sharedsecret hasher = hashlib.sha256() hasher.update(to_hash) box_secret = hasher.digest() self._shared_secret = box_secret nonce = b"\x00" * 24 return crypto_box_afternm(message_to_box, nonce, box_secret) def verify_server_accept(self, server_accept_envelope): """ this is used by the client to verify the server accept envelope """ remote_longterm_sharedsecret = crypto_scalarmult( bytes(self.local_ephemeral_key.private_key), bytes(self.remote_longterm_pub_key.to_curve25519_public_key())) local_longterm_sharedsecret = crypto_scalarmult( bytes(self.local_signing_key.private_key.to_curve25519_private_key( )), bytes(self._remote_ephemeral_pub_key)) to_hash = self.application_key + self._secret + remote_longterm_sharedsecret + local_longterm_sharedsecret hasher = hashlib.sha256() hasher.update(to_hash) box_secret = hasher.digest() self._shared_secret = box_secret nonce = b"\x00" * 24 signature = crypto_box_open_afternm(server_accept_envelope, nonce, box_secret) message = self.application_key + self._client_auth + self._hashed_secret self.remote_longterm_pub_key.verify(message, signature) def datagram_encrypt(self, datagram): return self.upstream_box.encrypt(datagram) def datagram_decrypt(self, datagram): return self.downstream_box.decrypt(datagram)
def callback(): """Takes the response from the provider and verify the identity of the logged in user Returns: Redirect to the wanted page after authentication """ session = request.environ.get("beaker.session") data = request.query.get("signedAttempt") if not data: return abort(400, "signedAttempt parameter is missing") data = j.data.serializers.json.loads(data) if "signedAttempt" not in data: return abort(400, "signedAttempt value is missing") username = data["doubleName"] if not username: return abort(400, "DoubleName is missing") res = requests.get(f"https://login.threefold.me/api/users/{username}", {"Content-Type": "application/json"}) if res.status_code != 200: return abort(400, "Error getting user pub key") pub_key = res.json()["publicKey"] user_pub_key = VerifyKey(j.data.serializers.base64.decode(pub_key)) # verify data signedData = data["signedAttempt"] verifiedData = user_pub_key.verify( j.data.serializers.base64.decode(signedData)).decode() data = j.data.serializers.json.loads(verifiedData) if "doubleName" not in data: return abort(400, "Decrypted data does not contain (doubleName)") if "signedState" not in data: return abort(400, "Decrypted data does not contain (state)") if data["doubleName"] != username: return abort(400, "username mismatch!") # verify state state = data["signedState"] if state != session["state"]: return abort(400, "Invalid state. not matching one in user session") nonce = j.data.serializers.base64.decode(data["data"]["nonce"]) ciphertext = j.data.serializers.base64.decode(data["data"]["ciphertext"]) try: priv = j.core.identity.me.nacl.private_key box = Box(priv, user_pub_key.to_curve25519_public_key()) decrypted = box.decrypt(ciphertext, nonce) except nacl.exceptions.CryptoError: return abort(400, "Error decrypting data") try: result = j.data.serializers.json.loads(decrypted) except JSONDecodeError: return abort(400, "3Bot login returned faulty data") if "email" not in result: return abort(400, "Email is not present in data") email = result["email"]["email"] sei = result["email"]["sei"] res = requests.post( "https://openkyc.live/verification/verify-sei", headers={"Content-Type": "application/json"}, json={"signedEmailIdentifier": sei}, ) if res.status_code != 200: return abort(400, "Email is not verified") session["username"] = username session["email"] = email session["authorized"] = True session["signedAttempt"] = signedData try: tid = j.sals.reservation_chatflow.reservation_chatflow.validate_user({ "username": username, "email": email }).id session["tid"] = tid session["explorer"] = j.core.identity.me.explorer_url except Exception as e: j.logger.warning( f"Error in validating user: {username} with email: {email} in explorer: {j.core.identity.me.explorer_url}\n from {str(e)}" ) return redirect(session.get("next_url", "/"))
class SHSClientCrypto(SHSCryptoBase): """An object that encapsulates all the SHS client-side crypto. :param local_key: the keypair used by the client (:class:`nacl.public.PrivateKey` object) :param server_pub_key: the server's public key (``byte`` string) :param ephemeral_key: a fresh local :class:`nacl.public.PrivateKey` :param application_key: the unique application key (``byte`` string), defaults to SSB's """ def __init__(self, local_key, server_pub_key, ephemeral_key, application_key=None): super(SHSClientCrypto, self).__init__(local_key, ephemeral_key, application_key) self.remote_pub_key = VerifyKey(server_pub_key) def verify_server_challenge(self, data): """Verify the correctness of challenge sent from the server.""" assert super(SHSClientCrypto, self).verify_challenge(data) curve_pkey = self.remote_pub_key.to_curve25519_public_key() # a_bob is (a * B) a_bob = crypto_scalarmult(bytes(self.local_ephemeral_key), bytes(curve_pkey)) self.a_bob = a_bob # this shall be hash(K | a * b | a * B) self.box_secret = hashlib.sha256(self.application_key + self.shared_secret + a_bob).digest() # and message_to_box will correspond to H = sign(A)[K | Bp | hash(a * b)] | Ap signed_message = self.local_key.sign(self.application_key + bytes(self.remote_pub_key) + self.shared_hash) message_to_box = signed_message.signature + bytes( self.local_key.verify_key) self.hello = message_to_box return True def generate_client_auth(self): """Generate box[K|a*b|a*B](H)""" nonce = b"\x00" * 24 # return box(K | a * b | a * B)[H] return crypto_box_afternm(self.hello, nonce, self.box_secret) def verify_server_accept(self, data): """Verify that the server's accept message is sane""" curve_lkey = self.local_key.to_curve25519_private_key() # b_alice is (A * b) b_alice = crypto_scalarmult(bytes(curve_lkey), self.remote_ephemeral_key) self.b_alice = b_alice # this is hash(K | a * b | a * B | A * b) self.box_secret = hashlib.sha256(self.application_key + self.shared_secret + self.a_bob + b_alice).digest() nonce = b"\x00" * 24 try: # let's use the box secret to unbox our encrypted message signature = crypto_box_open_afternm(data, nonce, self.box_secret) except CryptoError: raise SHSError('Error decrypting server acceptance message') # we should have received sign(B)[K | H | hash(a * b)] # let's see if that signature can verify the reconstructed data on our side self.remote_pub_key.verify( self.application_key + self.hello + self.shared_hash, signature) return True def clean(self, new_ephemeral_key=None): super(SHSClientCrypto, self).clean(new_ephemeral_key=new_ephemeral_key) self.a_bob = None self.b_alice = None