Ejemplo n.º 1
0
def validate_password_reset(session, reset_token, auth_code, recovery_key):
    """
    Retrieves a user given a password reset validation token

    :param session: An ORM session
    :param reset_token: A reset token
    :param auth_code: A two factor authentication code (optional)
    :param recovery_key: An encryption recovery key (optional)
    :return: A descriptor describing the result of the operation
    """
    now = datetime.now()
    prv_key = ''

    user = session.query(models.User).filter(
        models.User.reset_password_token == reset_token,
        models.User.reset_password_date >= now - timedelta(hours=72)
    ).one_or_none()

    # If the authentication token is invalid
    if user is None:
        return {'status': 'invalid_reset_token_provided'}

    # If encryption is enabled require the recovery key
    if user.crypto_prv_key:
        try:
            x = State.TempKeys.pop(user.id, None)
            if x:
                enc_key = GCE.derive_key(reset_token.encode(), user.salt)
                prv_key = GCE.symmetric_decrypt(enc_key, Base64Encoder.decode(x.key))
            else:
                recovery_key = recovery_key.replace('-', '').upper() + '===='
                recovery_key = Base32Encoder.decode(recovery_key.encode())
                prv_key = GCE.symmetric_decrypt(recovery_key, Base64Encoder.decode(user.crypto_bkp_key))
        except:
            return {'status': 'require_recovery_key'}

    elif user.two_factor_enable:
        two_factor_secret = user.two_factor_secret
        if not pyotp.TOTP(two_factor_secret).verify(auth_code, valid_window=1):
            return {'status': 'require_two_factor_authentication'}

    # Token is used, void it out
    user.reset_password_token = None
    user.reset_password_date = now

    # Require password change
    user.password_change_needed = True

    session = Sessions.new(user.tid, user.id,
                           user.tid, user.role,
                           user.password_change_needed,
                           user.two_factor_enable,
                           prv_key,
                           user.crypto_escrow_prv_key)

    return {'status': 'success', 'token': session.id}
Ejemplo n.º 2
0
    def test_base64_smessage_with_detached_sig_matches_with_attached_sig(self):
        sk = SigningKey.generate()
        vk = sk.verify_key

        smsg = sk.sign(b"Hello World in base64", encoder=Base64Encoder)

        msg = smsg.message
        b64sig = smsg.signature

        sig = Base64Encoder.decode(b64sig)

        assert vk.verify(msg, sig, encoder=Base64Encoder) == \
            vk.verify(smsg, encoder=Base64Encoder)

        assert Base64Encoder.decode(msg) == b"Hello World in base64"
Ejemplo n.º 3
0
    def test_base64_smessage_with_detached_sig_matches_with_attached_sig(self):
        sk = SigningKey.generate()
        vk = sk.verify_key

        smsg = sk.sign(b"Hello World in base64", encoder=Base64Encoder)

        msg = smsg.message
        b64sig = smsg.signature

        sig = Base64Encoder.decode(b64sig)

        assert vk.verify(msg, sig, encoder=Base64Encoder) == \
            vk.verify(smsg, encoder=Base64Encoder)

        assert Base64Encoder.decode(msg) == b"Hello World in base64"
Ejemplo n.º 4
0
def verify_signature(webhook_uri,
                     signature_header,
                     signature_expiry_seconds=60):
    # v1 is signature, s is submissionId, f is formId, t is submission epoch
    logger.debug(f'X-FormSG-Signature is <{signature_header}>.')
    formsg_signature = dict(
        part.split('=', 1) for part in signature_header.split(','))
    formsg_signature['t'] = int(formsg_signature['t'])

    # Javascript url.href adds a trailing `/` to root domain urls
    # https://github.com/opengovsg/formsg-javascript-sdk/blob/master/src/webhooks.ts#L25
    u = urlparse(webhook_uri)
    if not u.path:
        u = u._replace(path='/')
    webhook_uri = u.geturl()

    FORMSG_WEBHOOK_PUBLIC_KEY.verify(
        smessage=
        f'{webhook_uri}.{formsg_signature["s"]}.{formsg_signature["f"]}.{formsg_signature["t"]}'
        .encode('ascii'),
        signature=Base64Encoder.decode(formsg_signature['v1']),
    )

    if time() - (formsg_signature['t'] / 1000) > signature_expiry_seconds:
        raise BadSignatureError('FormSG signature has expired.')

    return formsg_signature
def read_file(file_name, base64=False):
    f = open(file_name, 'r')
    data = f.read()
    if base64:
        data = Base64Encoder.decode(data)
    f.close()
    return data
Ejemplo n.º 6
0
 def _read_bytes_as_b64_from_file(self, file_path, silent=False):
     try:
         with open(file_path, 'r') as read_file:
             return Base64Encoder.decode(read_file.read())
     except Exception as e:
         if not silent:
             print(f'Opening file: {file_path}')
             print(e.args[1])
Ejemplo n.º 7
0
    def decrypt(self, encrypted, sender_pub, recipient):
        box = Box(secret_key(recipient), public_key(sender_pub))
        if encrypted[:4] != 'msg:':
            raise DecryptionError

        try:
            return box.decrypt(Base64Encoder.decode(encrypted[4:]))
        except CryptoError:
            raise DecryptionError
def Decrypt_NaCl(Pk, Sk, nonce, ciphertext):
    Sk = PrivateKeyNaCl(Sk, encoder=Base64Encoder)
    Pk = PublicKeyNaCl(Pk, encoder=Base64Encoder)
    nonce = Base64Encoder.decode(nonce)
    box = BoxNaCl(Sk, Pk)
    # import numpy as np
    # print np.fromstring(box.shared_key(), dtype=np.uint8)
    plaintext = box.decrypt(ciphertext, nonce, encoder=Base64Encoder)
    return plaintext
 def _decrypt_and_set_private_key(self, private_key_store):
     if not self.password:
         raise AttributeError(
             'No password found! Password must be set ahead!')
     secure_key = pwhash.argon2i.kdf(secret.SecretBox.KEY_SIZE,
                                     Base64Encoder.decode(self.password),
                                     Base64Encoder.decode(
                                         private_key_store['salt']),
                                     opslimit=private_key_store['ops'],
                                     memlimit=private_key_store['mem'])
     encrypted = Base64Encoder.decode(private_key_store['private_key'])
     box = secret.SecretBox(secure_key)
     try:
         self.private_key = PrivateKey(box.decrypt(encrypted))
         self.public_key = self.private_key.public_key
         return True
     except Exception as e:
         print(e)
Ejemplo n.º 10
0
 def decrypt_text(self, cipher_text):
     if not self.private_key:
         self.import_private_key_from_file()
     if not self.private_key:
         raise AttributeError(
             'No private key known or found in file. Generate private key first!'
         )
     else:
         unseal_box = SealedBox(self.private_key)
         if re.fullmatch(self.CIPHER_PATTERN, cipher_text):
             cipher_text = re.search(self.CIPHER_PATTERN,
                                     cipher_text).group(1)
         return unseal_box.decrypt(
             Base64Encoder.decode(cipher_text)).decode('utf-8')
Ejemplo n.º 11
0
def get_recovery_key(session, tid, user_id, user_cc):
    """
    Transaction to get a user recovery key

    :param session: An ORM session
    :param tid: The tenant ID
    :param user_id: The user ID
    :param user_cc: The user key
    :return: The recovery key encoded base32
    """
    user = db_get_user(session, tid, user_id)

    if not user.crypto_rec_key:
        return ''

    user.clicked_recovery_key = True

    return Base32Encoder.encode(GCE.asymmetric_decrypt(user_cc, Base64Encoder.decode(user.crypto_rec_key.encode()))).replace(b'=', b'')
Ejemplo n.º 12
0
 def export_private_key_to_file(self):
     if not self.private_key:
         raise AttributeError(
             'No private key found to export. Generate or set private key first!'
         )
     else:
         salt = utils.random(pwhash.argon2i.SALTBYTES)
         secure_key = pwhash.argon2i.kdf(secret.SecretBox.KEY_SIZE,
                                         Base64Encoder.decode(
                                             self.password),
                                         salt,
                                         opslimit=self.ops,
                                         memlimit=self.mem)
         private_key_store = {
             'private_key': self._encrypt_private_key(secure_key),
             'salt': self._base64(salt),
             'ops': self.ops,
             'mem': self.mem
         }
         return self._write_dict_as_json_file(private_key_store,
                                              self.private_key_store)
Ejemplo n.º 13
0
def decrypt_content(body_json, secret_key):
    if 'data' in body_json:
        encrypted_content = body_json['data']['encryptedContent']
    else:
        encrypted_content = body_json[
            'encryptedContent']  # old version POST body
    #end if

    submission_public_key, nonce, encrypted_message = ENCRYPTED_CONTENT_REGEX.match(
        encrypted_content).groups()

    box = Box(
        PrivateKey(secret_key, encoder=Base64Encoder),
        PublicKey(submission_public_key, encoder=Base64Encoder),
    )

    plaintext = box.decrypt(encrypted_message,
                            Base64Encoder.decode(nonce),
                            encoder=Base64Encoder)

    return json.loads(plaintext)
Ejemplo n.º 14
0
VALID_PASSWORD2 = VALID_PASSWORD1
VALID_SALT1 = GCE.generate_salt()
VALID_SALT2 = GCE.generate_salt()
VALID_HASH1 = GCE.hash_password(VALID_PASSWORD1, VALID_SALT1)
VALID_HASH2 = GCE.hash_password(VALID_PASSWORD2, VALID_SALT2)
VALID_BASE64_IMG = 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVQYV2NgYAAAAAMAAWgmWQ0AAAAASUVORK5CYII='
INVALID_PASSWORD = '******'

KEY = GCE.generate_key()
USER_KEY = GCE.derive_key(VALID_PASSWORD1, VALID_SALT1)
USER_PRV_KEY, USER_PUB_KEY = GCE.generate_keypair()
USER_PRV_KEY_ENC = Base64Encoder.encode(
    GCE.symmetric_encrypt(USER_KEY, USER_PRV_KEY))
USER_BKP_KEY, USER_REC_KEY = GCE.generate_recovery_key(USER_PRV_KEY)
USER_REC_KEY_PLAIN = GCE.asymmetric_decrypt(USER_PRV_KEY,
                                            Base64Encoder.decode(USER_REC_KEY))
USER_REC_KEY_PLAIN = Base32Encoder.encode(USER_REC_KEY_PLAIN).replace(
    b'=', b'').decode('utf-8')
GCE_orig_generate_key = GCE.generate_key
GCE_orig_generate_keypair = GCE.generate_keypair


def mock_nullfunction(*args, **kwargs):
    return


def mock_GCE_generate_key():
    return KEY


def mock_GCE_generate_keypair():
Ejemplo n.º 15
0
 def set_public_key(self, b64_public_key):
     self.public_key = PublicKey(Base64Encoder.decode(b64_public_key))
Ejemplo n.º 16
0
def a2b(a):
    return Base64Encoder.decode(a)
Ejemplo n.º 17
0
def a2b(a):
    return Base64Encoder.decode(a)
Ejemplo n.º 18
0
def fax_status_callback(request: HttpRequest):
    # get relevant signature data
    event_timestamp = request.headers.get("Telnyx-Timestamp")
    event_signature = request.headers.get("Telnyx-Signature-Ed25519")
    public_key = settings.TELNYX_PUBLIC_KEY

    # prepare signature data for nacl
    verify_key = VerifyKey(public_key, encoder=Base64Encoder)
    callback_bytes = f"{event_timestamp}|".encode("UTF-8") + request.body
    signature = Base64Encoder.decode(event_signature)

    # verify signature
    try:
        verify_key.verify(callback_bytes, signature=signature)
    except BadSignatureError:
        return HttpResponseForbidden("invalid signature",
                                     content_type="text/plain")

    payload_json = json.loads(request.body)

    # get message object
    fax_id = None
    try:
        fax_id = payload_json.get("data").get("payload").get("fax_id")
    except AttributeError as e:
        # this key should always exist. we should never end up here
        raise ValueError(
            f"This is not a valid API response body: {request.body}") from e

    if not fax_id:
        raise ValueError(
            f"This is not a valid API response body: {request.body}")

    fax_message: FoiMessage = get_object_or_404(FoiMessage,
                                                email_message_id=fax_id)

    # find status
    try:
        status = payload_json.get("data").get("payload").get("status")
    except AttributeError as e:
        # we should never end up here either
        raise ValueError(
            f"This is not a valid API response body: {request.body}") from e

    if status == "failed":
        status = DeliveryStatus.Delivery.STATUS_FAILED
    elif status == "queued":
        status = DeliveryStatus.Delivery.STATUS_SENDING
    elif status == "media.processed":
        status = DeliveryStatus.Delivery.STATUS_SENDING
    elif status.startswith("sending"):
        status = DeliveryStatus.Delivery.STATUS_SENDING
    elif status == "delivered":
        status = DeliveryStatus.Delivery.STATUS_SENT
    else:
        # again: we should not end up here. according to telnyx-docu those are all possible stati
        raise ValueError(f"This is not a valid status response: {status}")

    # only try and update if the timestamp in request is more recent than the one in the database
    dt = datetime.datetime.fromtimestamp(int(event_timestamp),
                                         pytz.timezone("UTC"))
    if fax_message.deliverystatus.last_update > dt:
        return HttpResponse(status=409)

    ds, _created = DeliveryStatus.objects.update_or_create(
        message=fax_message,
        defaults=dict(
            status=status,
            last_update=timezone.now(),
        ),
    )
    data = payload_json.get("data")

    # Create machine-readable log
    fax_log_data = {
        "from_": data["payload"]["from"],
        "to": data["payload"]["to"],
        "sid": data["payload"]["fax_id"],
        "status": data["payload"]["status"],
        "num_pages": data["payload"].get("page_count", 0),
        "duration": data["payload"].get("call_duration_secs", 0),
        "failure_reason": data["payload"].get("failure_reason"),
        "date_created": data["occurred_at"],
    }
    ds.log = create_fax_log(ds.log, fax_log_data)
    ds.save()

    if status == DeliveryStatus.Delivery.STATUS_SENT:
        fax_message.timestamp = ds.last_update
        fax_message.save()
        ProblemReport.objects.find_and_resolve(
            message=fax_message, kind=ProblemReport.PROBLEM.BOUNCE_PUBLICBODY)

    failed = False
    if status == DeliveryStatus.Delivery.STATUS_FAILED:
        if ds.retry_count >= 3:
            failed = True
        else:
            # Retry fax delivery in 15 minutes
            retry_fax_delivery.apply_async(
                (fax_message.pk, ),
                {},
                # resend in intervals of 0.25, 1, 2 and 4 hours
                countdown=15 * 60 * 4**ds.retry_count,
            )

    if failed:
        ProblemReport.objects.report(
            message=fax_message,
            kind=ProblemReport.PROBLEM.BOUNCE_PUBLICBODY,
            description=ds.log,
            auto_submitted=True,
        )

    return HttpResponse(status=200)