Example #1
0
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())
Example #2
0
    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())
Example #3
0
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
Example #4
0
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"))
Example #5
0
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)
Example #7
0
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", "/"))
Example #8
0
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