Exemplo n.º 1
0
    def test_compares(self):
        assert constant_time.bytes_eq(b"foo", b"foo") is True

        assert constant_time.bytes_eq(b"foo", b"bar") is False

        assert constant_time.bytes_eq(b"foobar", b"foo") is False

        assert constant_time.bytes_eq(b"foo", b"foobar") is False
Exemplo n.º 2
0
def decrypt(data: bytes, privkey: datatypes.PrivateKey, shared_mac_data: bytes = b'') -> bytes:
    """Decrypt data with ECIES method using the given private key

    1) generate shared-secret = kdf( ecdhAgree(myPrivKey, msg[1:65]) )
    2) verify tag
    3) decrypt

    ecdhAgree(r, recipientPublic) == ecdhAgree(recipientPrivate, R)
    [where R = r*G, and recipientPublic = recipientPrivate*G]

    """
    if data[:1] != b'\x04':
        raise DecryptionError("wrong ecies header")

    #  1) generate shared-secret = kdf( ecdhAgree(myPrivKey, msg[1:65]) )
    shared = data[1:1 + PUBKEY_LEN]
    key_material = ecdh_agree(privkey, keys.PublicKey(shared))
    key = kdf(key_material)
    key_enc, key_mac = key[:KEY_LEN // 2], key[KEY_LEN // 2:]
    key_mac = sha256(key_mac).digest()
    tag = data[-KEY_LEN:]

    # 2) Verify tag
    expected_tag = hmac_sha256(key_mac, data[1 + PUBKEY_LEN:- KEY_LEN] + shared_mac_data)
    if not bytes_eq(expected_tag, tag):
        raise DecryptionError("Failed to verify tag")

    # 3) Decrypt
    algo = CIPHER(key_enc)
    blocksize = algo.block_size // 8
    iv = data[1 + PUBKEY_LEN:1 + PUBKEY_LEN + blocksize]
    ciphertext = data[1 + PUBKEY_LEN + blocksize:- KEY_LEN]
    ctx = Cipher(algo, MODE(iv), default_backend()).decryptor()
    return ctx.update(ciphertext) + ctx.finalize()
Exemplo n.º 3
0
    def decrypt(self, k, a, iv, e, t):
        """ Decrypt according to the selected encryption and hashing
        functions.
        :param k: Encryption key (optional)
        :param a: Additional Authenticated Data
        :param iv: Initialization Vector
        :param e: Ciphertext
        :param t: Authentication Tag

        Returns plaintext or raises an error
        """
        hkey = k[:self.keysize]
        dkey = k[self.keysize:]

        # verify mac
        if not constant_time.bytes_eq(t, self._mac(hkey, a, iv, e)):
            raise InvalidJWEData('Failed to verify MAC')

        # decrypt
        cipher = Cipher(algorithms.AES(dkey), modes.CBC(iv),
                        backend=self.backend)
        decryptor = cipher.decryptor()
        d = decryptor.update(e) + decryptor.finalize()
        unpadder = PKCS7(self.blocksize).unpadder()
        return unpadder.update(d) + unpadder.finalize()
Exemplo n.º 4
0
    def decrypt(self, data, associated_data=b""):
        decoded_data = base64.urlsafe_b64decode(data)
        mac = decoded_data[-16:]
        iv = decoded_data[0:16]
        cipher_text = decoded_data[16:-16]

        associated_data_length = struct.pack(">Q", len(associated_data) * 8)

        h = hmac.HMAC(self.mac_key, hashes.SHA256(), self.backend)
        h.update(associated_data)
        h.update(iv)
        h.update(cipher_text)
        h.update(associated_data_length)
        if not constant_time.bytes_eq(mac, h.finalize()[:16]):
            raise ValueError("data provided has an invalid signature.")

        cipher = Cipher(
            algorithms.AES(self.encryption_key), modes.CBC(iv), self.backend
        )

        decryptor = cipher.decryptor()
        plain_text = decryptor.update(cipher_text) + decryptor.finalize()

        unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
        unpadded_data = unpadder.update(plain_text) + unpadder.finalize()

        return unpadded_data
Exemplo n.º 5
0
    def parse_endpoint(self, token, version="v0", public_key=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param public_key: the public key (from Encryption-Key: p256ecdsa=)

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a tuple containing the (UAID, CHID)

        """

        token = self.fernet.decrypt(token.encode('utf8'))

        if version == 'v0':
            if not VALID_V0_TOKEN.match(token):
                raise InvalidTokenException("Corrupted push token")
            return tuple(token.split(':'))
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise InvalidTokenException("Invalid key data")
            if not constant_time.bytes_eq(sha256(public_key).digest(),
                                          token[32:]):
                raise InvalidTokenException("Key mismatch")
        return (token[:16].encode('hex'), token[16:32].encode('hex'))
Exemplo n.º 6
0
    def handle(self, request):
        name = request['headers'].get(self.id_header, None)
        key = request['headers'].get(self.key_header, None)
        if name is None and key is None:
            self.logger.debug('Ignoring request no relevant headers provided')
            return None

        validated = False
        try:
            val = self.store.get(self._db_key(name))
            if val is None:
                raise ValueError("No such ID")
            if constant_time.bytes_eq(val.encode('utf-8'),
                                      key.encode('utf-8')):
                validated = True
        except Exception:  # pylint: disable=broad-except
            self.audit_svc_access(log.AUDIT_SVC_AUTH_FAIL,
                                  request['client_id'], name)
            return False

        if validated:
            self.audit_svc_access(log.AUDIT_SVC_AUTH_PASS,
                                  request['client_id'], name)
            request['remote_user'] = name
            return True

        self.audit_svc_access(log.AUDIT_SVC_AUTH_FAIL,
                              request['client_id'], name)
        return False
Exemplo n.º 7
0
def constant_time_compare(val1, val2):
    """
    :type val1: any
    :type val2: any
    :rtype: bool
    """
    return constant_time.bytes_eq(force_bytes(val1), force_bytes(val2))
Exemplo n.º 8
0
def aes_key_unwrap(wrapping_key, wrapped_key, backend):
    if len(wrapped_key) < 24:
        raise ValueError("Must be at least 24 bytes")

    if len(wrapped_key) % 8 != 0:
        raise ValueError("The wrapped key must be a multiple of 8 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    # Implement RFC 3394 Key Unwrap - 2.2.2 (index method)
    decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
    aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"

    r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
    a = r.pop(0)
    n = len(r)
    for j in reversed(range(6)):
        for i in reversed(range(n)):
            # pack/unpack are safe as these are always 64-bit chunks
            atr = struct.pack(
                ">Q", struct.unpack(">Q", a)[0] ^ ((n * j) + i + 1)
            ) + r[i]
            # every decryption operation is a discrete 16 byte chunk so
            # it is safe to reuse the decryptor for the entire operation
            b = decryptor.update(atr)
            a = b[:8]
            r[i] = b[-8:]

    assert decryptor.finalize() == b""

    if not bytes_eq(a, aiv):
        raise InvalidUnwrap()

    return b"".join(r)
Exemplo n.º 9
0
    def decrypt(self, key, alg):
        # iv, ivl2, pt = super(IntegrityProtectedSKEDataV1, self).decrypt(key, alg)
        pt = _decrypt(bytes(self.ct), bytes(key), alg)

        # do the MDC checks
        _expected_mdcbytes = b'\xd3\x14' + hashlib.new('SHA1', pt[:-20]).digest()
        if not constant_time.bytes_eq(bytes(pt[-22:]), _expected_mdcbytes):
            raise PGPDecryptionError("Decryption failed")  # pragma: no cover

        iv = bytes(pt[:alg.block_size // 8])
        del pt[:alg.block_size // 8]

        ivl2 = bytes(pt[:2])
        del pt[:2]

        if not constant_time.bytes_eq(iv[-2:], ivl2):
            raise PGPDecryptionError("Decryption failed")  # pragma: no cover

        return pt
Exemplo n.º 10
0
def aes_key_unwrap_with_padding(wrapping_key: bytes,
                                wrapped_key: bytes,
                                backend=None) -> bytes:
    backend = _get_backend(backend)
    if len(wrapped_key) < 16:
        raise InvalidUnwrap("Must be at least 16 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    if len(wrapped_key) == 16:
        # RFC 5649 - 4.2 - exactly two 64-bit blocks
        decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
        b = decryptor.update(wrapped_key)
        assert decryptor.finalize() == b""
        a = b[:8]
        data = b[8:]
        n = 1
    else:
        r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
        encrypted_aiv = r.pop(0)
        n = len(r)
        a, r = _unwrap_core(wrapping_key, encrypted_aiv, r, backend)
        data = b"".join(r)

    # 1) Check that MSB(32,A) = A65959A6.
    # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n.  If so, let
    #    MLI = LSB(32,A).
    # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of
    #    the output data are zero.
    (mli, ) = struct.unpack(">I", a[4:])
    b = (8 * n) - mli
    if (not bytes_eq(a[:4], b"\xa6\x59\x59\xa6")
            or not 8 * (n - 1) < mli <= 8 * n
            or (b != 0 and not bytes_eq(data[-b:], b"\x00" * b))):
        raise InvalidUnwrap()

    if b == 0:
        return data
    else:
        return data[:-b]
Exemplo n.º 11
0
def aes_key_unwrap_with_padding(wrapping_key, wrapped_key, backend):
    if len(wrapped_key) < 16:
        raise ValueError("Must be at least 16 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    if len(wrapped_key) == 16:
        # RFC 5649 - 4.2 - exactly two 64-bit blocks
        decryptor = Cipher(AES(wrapping_key), ECB(), backend).decryptor()
        b = decryptor.update(wrapped_key)
        assert decryptor.finalize() == b""
        a = b[:8]
        data = b[8:]
        n = 1
    else:
        r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
        encrypted_aiv = r.pop(0)
        n = len(r)
        a, r = _unwrap_core(wrapping_key, encrypted_aiv, r, backend)
        data = b"".join(r)

    # 1) Check that MSB(32,A) = A65959A6.
    # 2) Check that 8*(n-1) < LSB(32,A) <= 8*n.  If so, let
    #    MLI = LSB(32,A).
    # 3) Let b = (8*n)-MLI, and then check that the rightmost b octets of
    #    the output data are zero.
    (mli,) = struct.unpack(">I", a[4:])
    b = (8 * n) - mli
    if (
        not bytes_eq(a[:4], b"\xa6\x59\x59\xa6") or not
        8 * (n - 1) < mli <= 8 * n or (
            b != 0 and not bytes_eq(data[-b:], b"\x00" * b)
        )
    ):
        raise InvalidUnwrap()

    if b == 0:
        return data
    else:
        return data[:-b]
Exemplo n.º 12
0
    def decrypt(self, private_key, cipher_text):
        """Ecies decrypt cipher text.

        First restore the ephemeral public key from bytes(97 bytes for 384,
         65 bytes for 256).
        Then derived a shared key based ecdh, using the key based hkdf to
        generate aes key and hmac key,
        using hmac-sha3 to verify the hmac bytes.
        Last using aes-256-cfb to decrypt the bytes.

        :param private_key: private key
        :param cipher_text: cipher text
        :return: plain text
        """
        key_len = private_key.curve.key_size
        if key_len != self.curve.key_size:
            raise ValueError(
                "Invalid key. Input security level {} does not "
                "match the current security level {}".format(
                    key_len,
                    self.curve.key_size))

        d_len = key_len >> 3
        rb_len = ((key_len + 7) // 8) * 2 + 1
        ct_len = len(cipher_text)
        if ct_len <= rb_len + d_len:
            raise ValueError(
                "Illegal cipherText length: cipher text length {} "
                "must be > rb length plus d_len {}".format(ct_len,
                                                           rb_len + d_len)
            )

        rb = cipher_text[:rb_len]
        em = cipher_text[rb_len:ct_len - d_len]
        d = cipher_text[ct_len - d_len:ct_len]

        ephemeral_public_key = EllipticCurvePublicNumbers \
            .from_encoded_point(self.curve(), rb) \
            .public_key(default_backend())
        z = private_key.exchange(ec.ECDH(), ephemeral_public_key)
        hkdf_output = Hkdf(salt=None, input_key_material=z, hash=self.hash) \
            .expand(length=AES_KEY_LENGTH + HMAC_KEY_LENGTH)
        aes_key = hkdf_output[:AES_KEY_LENGTH]
        hmac_key = hkdf_output[AES_KEY_LENGTH:AES_KEY_LENGTH + HMAC_KEY_LENGTH]

        mac = hmac.new(hmac_key, em, self.hash)
        recovered_d = mac.digest()
        if not constant_time.bytes_eq(recovered_d, d):
            raise ValueError("Hmac verify failed.")

        iv = em[:IV_LENGTH]
        aes_cipher = AES.new(key=aes_key, mode=AES.MODE_CFB, iv=iv)
        return aes_cipher.decrypt(em[IV_LENGTH:len(em)])
Exemplo n.º 13
0
    def validate(self, text):
        """ compare a given string to the code

        Performs a constant time comparison that is case insensitive.

        returns true when the given text matches the code

        """

        expected = self.code.lower().encode("utf-8")
        actual = text.lower().encode("utf-8")
        return bytes_eq(expected, actual)
Exemplo n.º 14
0
    def decrypt(self, private_key, cipher_text):
        """Ecies decrypt cipher text.

        First restore the ephemeral public key from bytes(97 bytes for 384,
         65 bytes for 256).
        Then derived a shared key based ecdh, using the key based hkdf to
        generate aes key and hmac key,
        using hmac-sha3 to verify the hmac bytes.
        Last using aes-256-cfb to decrypt the bytes.

        :param private_key: private key
        :param cipher_text: cipher text
        :Returns: plain text
        """
        key_len = private_key.curve.key_size
        if key_len != self.curve.key_size:
            raise ValueError(
                "Invalid key. Input security level {} does not "
                "match the current security level {}".format(
                    key_len,
                    self.curve.key_size))

        d_len = key_len >> 3
        rb_len = ((key_len + 7) // 8) * 2 + 1
        ct_len = len(cipher_text)
        if ct_len <= rb_len + d_len:
            raise ValueError(
                "Illegal cipherText length: cipher text length {} "
                "must be > rb length plus d_len {}".format(ct_len,
                                                           rb_len + d_len)
            )

        rb = cipher_text[:rb_len]
        em = cipher_text[rb_len:ct_len - d_len]
        d = cipher_text[ct_len - d_len:ct_len]

        ephemeral_public_key = EllipticCurvePublicNumbers \
            .from_encoded_point(self.curve(), rb) \
            .public_key(default_backend())
        z = private_key.exchange(ec.ECDH(), ephemeral_public_key)
        hkdf_output = Hkdf(salt=None, input_key_material=z, hash=self.hash) \
            .expand(length=AES_KEY_LENGTH + HMAC_KEY_LENGTH)
        aes_key = hkdf_output[:AES_KEY_LENGTH]
        hmac_key = hkdf_output[AES_KEY_LENGTH:AES_KEY_LENGTH + HMAC_KEY_LENGTH]

        mac = hmac.new(hmac_key, em, self.hash)
        recovered_d = mac.digest()
        if not constant_time.bytes_eq(recovered_d, d):
            raise ValueError("Hmac verify failed.")

        iv = em[:IV_LENGTH]
        aes_cipher = AES.new(key=aes_key, mode=AES.MODE_CFB, iv=iv)
        return aes_cipher.decrypt(em[IV_LENGTH:len(em)])
Exemplo n.º 15
0
    def register_complete(self, challenge, client_data, attestation_object):
        if client_data.get('type') != WEBAUTHN_TYPE.MAKE_CREDENTIAL:
            raise ValueError('Incorrect type in ClientData.')
        if not self._verify(client_data.get('origin')):
            raise ValueError('Invalid origin in ClientData.')
        if not constant_time.bytes_eq(challenge, client_data.challenge):
            raise ValueError('Wrong challenge in response.')
        if not constant_time.bytes_eq(sha256(self.rp['id'].encode()),
                                      attestation_object.auth_data.rp_id_hash):
            raise ValueError('Wrong RP ID hash in response.')
        if attestation_object.fmt == ATTESTATION.NONE \
                and self.attestation != ATTESTATION.NONE:
            raise ValueError('Attestation required, but not provided.')
        attestation_object.verify(client_data.hash)

        if self.user_verification is USER_VERIFICATION.REQUIRED and \
           not attestation_object.auth_data.is_user_verified():
            raise ValueError(
                'User verification required, but User verified flag not set.')

        return attestation_object.auth_data
Exemplo n.º 16
0
    def checkMessageSignature(self, message):
        """Given a message with a signature, calculate a new signature
        and return whether it matches the signature in the message.

        @raises ValueError: if the message has no signature or no signature
            can be calculated for it.
        """
        message_sig = message.getArg(OPENID_NS, 'sig')
        if not message_sig:
            raise ValueError("%s has no sig." % (message,))
        calculated_sig = self.getMessageSignature(message)
        return bytes_eq(calculated_sig.encode('utf-8'), message_sig.encode('utf-8'))
Exemplo n.º 17
0
    def checkMessageSignature(self, message):
        """Given a message with a signature, calculate a new signature
        and return whether it matches the signature in the message.

        @raises ValueError: if the message has no signature or no signature
            can be calculated for it.
        """
        message_sig = message.getArg(OPENID_NS, 'sig')
        if not message_sig:
            raise ValueError("%s has no sig." % (message,))
        calculated_sig = self.getMessageSignature(message)
        return bytes_eq(calculated_sig.encode('utf-8'), message_sig.encode('utf-8'))
Exemplo n.º 18
0
def decrypt(key, header, ciphertext):
    assert isinstance(key, bytes)
    assert len(key) == 32
    assert isinstance(header, bytes)
    assert isinstance(ciphertext, bytes)
    if len(ciphertext) < 32:
        raise Exception
    tag = ciphertext[:32]
    payload = _stream_xor(key, tag, ciphertext[32:])
    if not bytes_eq(tag, _auth(key, header, payload)):
        raise Exception
    return payload
Exemplo n.º 19
0
    def register_complete(self, state, client_data, attestation_object):
        """Verify the correctness of the registration data received from
        the client.

        :param state: The state data returned by the corresponding
            `register_begin`.
        :param client_data: The client data.
        :param attestation_object: The attestation object.
        :return: The authenticator data"""
        if client_data.get('type') != WEBAUTHN_TYPE.MAKE_CREDENTIAL:
            raise ValueError('Incorrect type in ClientData.')
        if not self._verify(client_data.get('origin')):
            raise ValueError('Invalid origin in ClientData.')
        if not constant_time.bytes_eq(websafe_decode(state['challenge']),
                                      client_data.challenge):
            raise ValueError('Wrong challenge in response.')
        if not constant_time.bytes_eq(self.rp.id_hash,
                                      attestation_object.auth_data.rp_id_hash):
            raise ValueError('Wrong RP ID hash in response.')

        if state['user_verification'] is USER_VERIFICATION.REQUIRED and \
                not attestation_object.auth_data.is_user_verified():
            raise ValueError(
                'User verification required, but User verified flag not set.')

        if self.attestation != ATTESTATION.NONE:
            att_verifier = UnsupportedAttestation()
            for at in self._attestation_types:
                if getattr(at, 'FORMAT', None) == attestation_object.fmt:
                    att_verifier = at
                    break
            # An unsupported format causes an exception to be thrown, which
            # includes the auth_data. The caller may choose to handle this case
            # and allow the registration.
            att_verifier.verify(attestation_object.att_statement,
                                attestation_object.auth_data, client_data.hash)
        # We simply ignore attestation if self.attestation == 'none', as not all
        # clients strip the attestation.

        return attestation_object.auth_data
Exemplo n.º 20
0
 def finalize(self):
     tag_size = self._cipher.block_size // 8
     tag_buf = self._backend._ffi.new("unsigned char[]", tag_size)
     tag_len = self._backend._ffi.new("size_t *", tag_size)
     res = backend._lib.CCCryptorGCMFinal(self._ctx[0], tag_buf, tag_len)
     self._backend._check_response(res)
     _release_cipher_ctx(self._ctx)
     self._tag = self._backend._ffi.buffer(tag_buf)[:]
     if (self._operation == self._backend._lib.kCCDecrypt
             and not constant_time.bytes_eq(self._tag[:len(self._mode.tag)],
                                            self._mode.tag)):
         raise InvalidTag
     return b""
Exemplo n.º 21
0
    def check_xsrf_cookie(self):
        """
            Override needed to change name of header name
        """
        token = self.request.headers.get("X-XSRF-TOKEN")
        if not token:
            token = self.get_argument('xsrf-token', default=None)
        if not token:
            raise HTTPError(403, "X-XSRF-TOKEN argument missing from POST")

        # This is a constant time comparison provided by cryptography package
        if not bytes_eq(self.xsrf_token.encode('utf-8'), token.encode('utf-8')):
            raise HTTPError(403, "XSRF cookie does not match POST argument")
Exemplo n.º 22
0
 def finalize(self):
     tag_size = self._cipher.block_size // 8
     tag_buf = self._backend._ffi.new("unsigned char[]", tag_size)
     tag_len = self._backend._ffi.new("size_t *", tag_size)
     res = backend._lib.CCCryptorGCMFinal(self._ctx[0], tag_buf, tag_len)
     self._backend._check_response(res)
     _release_cipher_ctx(self._ctx)
     self._tag = self._backend._ffi.buffer(tag_buf)[:]
     if self._operation == self._backend._lib.kCCDecrypt and not constant_time.bytes_eq(
         self._tag[: len(self._mode.tag)], self._mode.tag
     ):
         raise InvalidTag
     return b""
Exemplo n.º 23
0
    def check_xsrf_cookie(self):
        """
            Override needed to change name of header name
        """
        token = self.request.headers.get("X-XSRF-TOKEN")
        if not token:
            token = self.get_argument('xsrf-token', default=None)
        if not token:
            raise HTTPError(403, "X-XSRF-TOKEN argument missing from POST")

        # This is a constant time comparison provided by cryptography package
        if not bytes_eq(self.xsrf_token.encode('utf-8'), token.encode('utf-8')):
            raise HTTPError(403, "XSRF cookie does not match POST argument")
Exemplo n.º 24
0
    def decrypt(self, key, alg):  # pragma: no cover
        pt = _decrypt(bytes(self.ct), bytes(key), alg)

        iv = bytes(pt[:alg.block_size // 8])
        del pt[:alg.block_size // 8]

        ivl2 = bytes(pt[:2])
        del pt[:2]

        if not constant_time.bytes_eq(iv[-2:], ivl2):
            raise PGPDecryptionError("Decryption failed")

        return pt
Exemplo n.º 25
0
def _open_aes_ctr(key, nonce, ciphertext, expected_hmac, digest_method):
    data_key, hmac_key = _halve_key(key)
    hmac = _get_hmac(hmac_key, ciphertext, digest_method)
    # Check the HMAC before we decrypt to verify ciphertext integrity
    if not constant_time.bytes_eq(hmac, expected_hmac):
        raise IntegrityError("Computed HMAC on %s does not match stored HMAC")

    decryptor = Cipher(
        algorithms.AES(data_key),
        modes.CTR(nonce),
        backend=default_backend()
    ).decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()
Exemplo n.º 26
0
    def decrypt_header(self, data: bytes) -> bytes:
        if len(data) != HEADER_LEN + MAC_LEN:
            raise ValueError("Unexpected header length: {}".format(len(data)))

        header_ciphertext = data[:HEADER_LEN]
        header_mac = data[HEADER_LEN:]
        mac_secret = self.ingress_mac.digest()[:HEADER_LEN]
        aes = self.mac_enc(mac_secret)[:HEADER_LEN]
        self.ingress_mac.update(sxor(aes, header_ciphertext))
        expected_header_mac = self.ingress_mac.digest()[:HEADER_LEN]
        if not bytes_eq(expected_header_mac, header_mac):
            raise AuthenticationError('Invalid header mac')
        return self.aes_dec.update(header_ciphertext)
Exemplo n.º 27
0
    def decrypt_header(self, data: bytes) -> bytes:
        if len(data) != HEADER_LEN + MAC_LEN:
            raise ValueError("Unexpected header length: {}".format(len(data)))

        header_ciphertext = data[:HEADER_LEN]
        header_mac = data[HEADER_LEN:]
        mac_secret = self.ingress_mac.digest()[:HEADER_LEN]
        aes = self.mac_enc(mac_secret)[:HEADER_LEN]
        self.ingress_mac.update(sxor(aes, header_ciphertext))
        expected_header_mac = self.ingress_mac.digest()[:HEADER_LEN]
        if not bytes_eq(expected_header_mac, header_mac):
            raise DecryptionError('Invalid header mac')
        return self.aes_dec.update(header_ciphertext)
Exemplo n.º 28
0
def check_password(password, salt, password_hash):
    """
    @param password: the user provided password
    @param salt: The salt to be used for password hashing
    @param password_hash: the expected hash

    @return:
        the scrypt hash in base64 of the new password
    """
    if isinstance(password_hash, text_type):
        password_hash = password_hash.encode()

    return constant_time.bytes_eq(hash_password(password, salt), password_hash)
Exemplo n.º 29
0
def _open_aes_ctr(key, nonce, ciphertext, expected_hmac, digest_method):
    data_key, hmac_key = _halve_key(key)
    hmac = _get_hmac(hmac_key, ciphertext, digest_method)
    # Check the HMAC before we decrypt to verify ciphertext integrity
    if not constant_time.bytes_eq(hmac, expected_hmac):
        raise IntegrityError("Computed HMAC on %s does not match stored HMAC")

    decryptor = Cipher(
        algorithms.AES(data_key),
        modes.CTR(nonce),
        backend=default_backend()
    ).decryptor()
    return decryptor.update(ciphertext) + decryptor.finalize()
Exemplo n.º 30
0
def jenkins_ci_notification(repo,
                            pagure_ci_token,
                            username=None,
                            namespace=None):
    """
    Jenkins Build Notification
    --------------------------
    At the end of a build on Jenkins, this URL is used (if the project is
    rightly configured) to flag a pull-request with the result of the build.

    ::

        POST /api/0/ci/jenkins/<repo>/<token>/build-finished

    """

    project = pagure.lib.get_project(SESSION,
                                     repo,
                                     user=username,
                                     namespace=namespace)
    if repo is None:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT)

    if not constant_time.bytes_eq(to_bytes(pagure_ci_token),
                                  to_bytes(project.ci_hook.pagure_ci_token)):
        raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK)

    data = flask.request.get_json()
    if not data:
        APP.logger.debug("Bad Request: No JSON retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    build_id = data.get('build', {}).get('number')
    if not build_id:
        APP.logger.debug("Bad Request: No build ID retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    try:
        lib_ci.process_jenkins_build(
            SESSION,
            project,
            build_id,
            requestfolder=APP.config['REQUESTS_FOLDER'])
    except pagure.exceptions.PagureException as err:
        APP.logger.error('Error processing jenkins notification', exc_info=err)
        raise pagure.exceptions.APIError(400,
                                         error_code=APIERROR.ENOCODE,
                                         error=str(err))

    APP.logger.info('Successfully proccessed jenkins notification')
    return ('', 204)
Exemplo n.º 31
0
def _check_mac(shared_secret, timestamp, nonce, body, message_hmac,
    http_verb=None, http_resource_uri=None, headers=None):
    """
    Computes HMAC and compares expected and obtained values. Performs constant
    time comparison. Raises AuthenticationException.
    """

    # compute the expected hmac
    expected_hmac = _compute_mac(shared_secret, timestamp, nonce, body,
        http_verb=http_verb, http_resource_uri=http_resource_uri, headers=headers)

    # constant time check of expected againstreceived hmac
    if not constant_time.bytes_eq(to_utf8(message_hmac), expected_hmac):
        raise AuthenticationException('signature header value does not match computed value')
Exemplo n.º 32
0
def _verify_challenge(received_challenge, sent_challenge):
    if not isinstance(received_challenge, six.string_types):
        return False
    if not isinstance(sent_challenge, six.string_types):
        return False
    if not received_challenge:
        return False
    if not sent_challenge:
        return False
    if not constant_time.bytes_eq(bytes(sent_challenge, encoding='utf-8'),
                                  bytes(received_challenge, encoding='utf-8')):
        return False

    return True
Exemplo n.º 33
0
    def test_reject_unicode(self):
        with pytest.raises(TypeError):
            constant_time.bytes_eq(b"foo", "foo")

        with pytest.raises(TypeError):
            constant_time.bytes_eq("foo", b"foo")

        with pytest.raises(TypeError):
            constant_time.bytes_eq("foo", "foo")
Exemplo n.º 34
0
    def register_complete(self, state, client_data, attestation_object):
        """Verify the correctness of the registration data received from
        the client.

        :param state: The state data returned by the corresponding
            `register_begin`.
        :param client_data: The client data.
        :param attestation_object: The attestation object.
        :return: The authenticator data"""
        if client_data.get('type') != WEBAUTHN_TYPE.MAKE_CREDENTIAL:
            raise ValueError('Incorrect type in ClientData.')
        if not self._verify(client_data.get('origin')):
            raise ValueError('Invalid origin in ClientData.')
        if not constant_time.bytes_eq(state['challenge'],
                                      client_data.challenge):
            raise ValueError('Wrong challenge in response.')
        if not constant_time.bytes_eq(self.rp.id_hash,
                                      attestation_object.auth_data.rp_id_hash):
            raise ValueError('Wrong RP ID hash in response.')
        if attestation_object.fmt == ATTESTATION.NONE \
                and self.attestation != ATTESTATION.NONE:
            raise ValueError('Attestation required, but not provided.')
        for at in self._attestation_types:
            if getattr(at, 'FORMAT', None) == attestation_object.fmt:
                at.verify(attestation_object.att_statement,
                          attestation_object.auth_data, client_data.hash)
                break
        else:
            raise ValueError('Unsupported attestation type: %s' %
                             attestation_object.fmt)

        if state['user_verification'] is USER_VERIFICATION.REQUIRED and \
           not attestation_object.auth_data.is_user_verified():
            raise ValueError(
                'User verification required, but User verified flag not set.')

        return attestation_object.auth_data
Exemplo n.º 35
0
    def test_reject_unicode(self):
        with pytest.raises(TypeError):
            constant_time.bytes_eq(b"foo", "foo")  # type: ignore[arg-type]

        with pytest.raises(TypeError):
            constant_time.bytes_eq("foo", b"foo")  # type: ignore[arg-type]

        with pytest.raises(TypeError):
            constant_time.bytes_eq("foo", "foo")  # type: ignore[arg-type]
Exemplo n.º 36
0
    def test_reject_unicode(self):
        with pytest.raises(TypeError):
            constant_time.bytes_eq(b"foo", six.u("foo"))

        with pytest.raises(TypeError):
            constant_time.bytes_eq(six.u("foo"), b"foo")

        with pytest.raises(TypeError):
            constant_time.bytes_eq(six.u("foo"), six.u("foo"))
Exemplo n.º 37
0
    def parse_endpoint(self,
                       token,
                       version="v1",
                       ckey_header=None,
                       auth_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
            (from Crypto-Key: p256ecdsa=)
        :param auth_header: The Authorization header bearing the VAPID info

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """
        token = self.fernet.decrypt(repad(token).encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            public_key = crypto_key.get_label('p256ecdsa')

        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if not auth_header:
                raise VapidAuthException("Missing Authorization Header")
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise VapidAuthException("Invalid key data")
            try:
                decoded_key = base64url_decode(public_key)
            except TypeError:
                raise VapidAuthException("Invalid key data")
            if not constant_time.bytes_eq(
                    sha256(decoded_key).digest(), token[32:]):
                raise VapidAuthException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    version=version,
                    public_key=public_key)
Exemplo n.º 38
0
    def validate(self, previous_entry: "LogEntry") -> bool:
        """Validate the hash of a single log entry.

        Validates the hash of this entry with regard to the previous entry's
        hash. The previous entry is the LogEntry with the previous number,
        previous_entry.number == self.number - 1

        :param previous_entry: The previous log entry to validate against.
        :return: True if the digest is correct, False if not.
        """

        if (self.number - previous_entry.number) & 0xFFFF != 1:
            raise ValueError("previous_entry has wrong number!")

        digest = sha256(self.data + previous_entry.digest).digest()[:16]
        return constant_time.bytes_eq(self.digest, digest)
Exemplo n.º 39
0
    def get_api_session(self):
        token = ''
        if b'x-api-token' in self.request.headers:
            token = bytes(self.request.headers[b'x-api-token'])

        # Assert the input is okay and the api_token state is acceptable
        if self.request.tid != 1 or \
           self.state.api_token_session is None or \
           not self.state.tenant_cache[self.request.tid].admin_api_token_digest:
            return

        stored_token_hash = self.state.tenant_cache[
            self.request.tid].admin_api_token_digest.encode()

        if constant_time.bytes_eq(sha256(token), stored_token_hash):
            return self.state.api_token_session
Exemplo n.º 40
0
def _check_mac(shared_secret, timestamp, nonce, body, message_hmac,
               http_verb=None, http_resource_uri=None, headers=None):
    """
    Computes HMAC and compares expected and obtained values. Performs constant
    time comparison. Raises AuthenticationException.
    """

    # compute the expected hmac
    expected_hmac = _compute_mac(shared_secret, timestamp, nonce, body,
                                 http_verb=http_verb,
                                 http_resource_uri=http_resource_uri,
                                 headers=headers)

    # constant time check of expected againstreceived hmac
    if not constant_time.bytes_eq(to_binary(message_hmac), expected_hmac):
        raise AuthenticationException('signature header value does not match computed value')
Exemplo n.º 41
0
    def decrypt_body(self, data: bytes, body_size: int) -> bytes:
        read_size = roundup_16(body_size)
        if len(data) < read_size + MAC_LEN:
            raise ValueError('Insufficient body length; Got {}, wanted {}'.format(
                len(data), (read_size + MAC_LEN)))

        frame_ciphertext = data[:read_size]
        frame_mac = data[read_size:read_size + MAC_LEN]

        self.ingress_mac.update(frame_ciphertext)
        fmac_seed = self.ingress_mac.digest()[:MAC_LEN]
        self.ingress_mac.update(sxor(self.mac_enc(fmac_seed), fmac_seed))
        expected_frame_mac = self.ingress_mac.digest()[:MAC_LEN]
        if not bytes_eq(expected_frame_mac, frame_mac):
            raise DecryptionError('Invalid frame mac')
        return self.aes_dec.update(frame_ciphertext)[:body_size]
Exemplo n.º 42
0
    def decrypt_body(self, data: bytes, body_size: int) -> bytes:
        read_size = roundup_16(body_size)
        if len(data) < read_size + MAC_LEN:
            raise ValueError('Insufficient body length; Got {}, wanted {}'.format(
                len(data), (read_size + MAC_LEN)))

        frame_ciphertext = data[:read_size]
        frame_mac = data[read_size:read_size + MAC_LEN]

        self.ingress_mac.update(frame_ciphertext)
        fmac_seed = self.ingress_mac.digest()[:MAC_LEN]
        self.ingress_mac.update(sxor(self.mac_enc(fmac_seed), fmac_seed))
        expected_frame_mac = self.ingress_mac.digest()[:MAC_LEN]
        if not bytes_eq(expected_frame_mac, frame_mac):
            raise AuthenticationError('Invalid frame mac')
        return self.aes_dec.update(frame_ciphertext)[:body_size]
Exemplo n.º 43
0
Arquivo: wsgiapp.py Projeto: qsdj/grr
def ValidateCSRFTokenOrRaise(request):
    """Decorator for WSGI handler that checks CSRF cookie against the request."""

    # CSRF check doesn't make sense for GET/HEAD methods, because they can
    # (and are) used when downloading files through <a href> links - and
    # there's no way to set X-CSRFToken header in this case.
    if request.method in ("GET", "HEAD"):
        return

    # In the ideal world only JavaScript can be used to add a custom header, and
    # only within its origin. By default, browsers don't allow JavaScript to
    # make cross origin requests.
    #
    # Unfortunately, in the real world due to bugs in browsers plugins, it can't
    # be guaranteed that a page won't set an HTTP request with a custom header
    # set. That's why we also check the contents of a header via an HMAC check
    # with a server-stored secret.
    #
    # See for more details:
    # https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
    # (Protecting REST Services: Use of Custom Request Headers).
    csrf_token = utils.SmartStr(request.headers.get("X-CSRFToken", ""))
    if not csrf_token:
        logging.info("Did not find headers CSRF token for: %s", request.path)
        raise werkzeug_exceptions.Forbidden("CSRF token is missing")

    try:
        decoded = base64.urlsafe_b64decode(csrf_token + "==")
        digest, token_time = decoded.rsplit(CSRF_DELIMITER, 1)
        token_time = long(token_time)
    except (TypeError, ValueError):
        logging.info("Malformed CSRF token for: %s", request.path)
        raise werkzeug_exceptions.Forbidden("Malformed CSRF token")

    if len(digest) != hashlib.sha256().digest_size:
        logging.info("Invalid digest size for: %s", request.path)
        raise werkzeug_exceptions.Forbidden("Malformed CSRF token digest")

    expected = GenerateCSRFToken(request.user, token_time)
    if not constant_time.bytes_eq(csrf_token, expected):
        logging.info("Non-matching CSRF token for: %s", request.path)
        raise werkzeug_exceptions.Forbidden("Non-matching CSRF token")

    current_time = rdfvalue.RDFDatetime.Now().AsMicrosecondsSinceEpoch()
    if current_time - token_time > CSRF_TOKEN_DURATION.microseconds:
        logging.info("Expired CSRF token for: %s", request.path)
        raise werkzeug_exceptions.Forbidden("Expired CSRF token")
Exemplo n.º 44
0
def ValidateCSRFTokenOrRaise(request):
  """Decorator for WSGI handler that checks CSRF cookie against the request."""

  # CSRF check doesn't make sense for GET/HEAD methods, because they can
  # (and are) used when downloading files through <a href> links - and
  # there's no way to set X-CSRFToken header in this case.
  if request.method in ("GET", "HEAD"):
    return

  # In the ideal world only JavaScript can be used to add a custom header, and
  # only within its origin. By default, browsers don't allow JavaScript to
  # make cross origin requests.
  #
  # Unfortunately, in the real world due to bugs in browsers plugins, it can't
  # be guaranteed that a page won't set an HTTP request with a custom header
  # set. That's why we also check the contents of a header via an HMAC check
  # with a server-stored secret.
  #
  # See for more details:
  # https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
  # (Protecting REST Services: Use of Custom Request Headers).
  csrf_token = utils.SmartStr(request.headers.get("X-CSRFToken", ""))
  if not csrf_token:
    logging.info("Did not find headers CSRF token for: %s", request.path)
    raise werkzeug_exceptions.Forbidden("CSRF token is missing")

  try:
    decoded = base64.urlsafe_b64decode(csrf_token + "==")
    digest, token_time = decoded.rsplit(CSRF_DELIMITER, 1)
    token_time = int(token_time)
  except (TypeError, ValueError):
    logging.info("Malformed CSRF token for: %s", request.path)
    raise werkzeug_exceptions.Forbidden("Malformed CSRF token")

  if len(digest) != hashlib.sha256().digest_size:
    logging.info("Invalid digest size for: %s", request.path)
    raise werkzeug_exceptions.Forbidden("Malformed CSRF token digest")

  expected = GenerateCSRFToken(request.user, token_time)
  if not constant_time.bytes_eq(csrf_token, expected):
    logging.info("Non-matching CSRF token for: %s", request.path)
    raise werkzeug_exceptions.Forbidden("Non-matching CSRF token")

  current_time = rdfvalue.RDFDatetime.Now().AsMicrosecondsSinceEpoch()
  if current_time - token_time > CSRF_TOKEN_DURATION.microseconds:
    logging.info("Expired CSRF token for: %s", request.path)
    raise werkzeug_exceptions.Forbidden("Expired CSRF token")
Exemplo n.º 45
0
    def authenticate_complete(
        self,
        state,
        credentials: Sequence[AttestedCredentialData],
        credential_id: bytes,
        client_data: CollectedClientData,
        auth_data: AuthenticatorData,
        signature: bytes,
    ) -> AttestedCredentialData:
        """Verify the correctness of the assertion data received from
        the client.

        :param state: The state data returned by the corresponding
            `register_begin`.
        :param credentials: The list of previously registered credentials.
        :param credential_id: The credential id from the client response.
        :param client_data: The client data.
        :param auth_data: The authenticator data.
        :param signature: The signature provided by the client."""
        if client_data.type != CollectedClientData.TYPE.GET:
            raise ValueError("Incorrect type in CollectedClientData.")
        if not self._verify(client_data.origin):
            raise ValueError("Invalid origin in CollectedClientData.")
        if websafe_decode(state["challenge"]) != client_data.challenge:
            raise ValueError("Wrong challenge in response.")
        if not constant_time.bytes_eq(self.rp.id_hash, auth_data.rp_id_hash):
            raise ValueError("Wrong RP ID hash in response.")
        if not auth_data.is_user_present():
            raise ValueError("User Present flag not set.")

        if (
            state["user_verification"] == UserVerificationRequirement.REQUIRED
            and not auth_data.is_user_verified()
        ):
            raise ValueError(
                "User verification required, but user verified flag not set."
            )

        for cred in credentials:
            if cred.credential_id == credential_id:
                try:
                    cred.public_key.verify(auth_data + client_data.hash, signature)
                except _InvalidSignature:
                    raise ValueError("Invalid signature.")
                logger.info(f"Credential authenticated: {credential_id.hex()}")
                return cred
        raise ValueError("Unknown credential ID.")
Exemplo n.º 46
0
    def parse_endpoint(self, token, version="v0", ckey_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
        (from Crypto-Key: p256ecdsa=)

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """

        token = self.fernet.decrypt(token.encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            label = crypto_key.get_label('p256ecdsa')
            try:
                public_key = base64url_decode(label)
            except:
                # Ignore missing and malformed app server keys.
                pass

        if version == 'v0':
            if not VALID_V0_TOKEN.match(token):
                raise InvalidTokenException("Corrupted push token")
            items = token.split(':')
            return dict(uaid=items[0], chid=items[1], public_key=public_key)
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise InvalidTokenException("Invalid key data")
            if not constant_time.bytes_eq(
                    sha256(public_key).digest(), token[32:]):
                raise InvalidTokenException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    public_key=public_key)
Exemplo n.º 47
0
    def parse_endpoint(self, token, version="v0", ckey_header=None):
        """Parse an endpoint into component elements of UAID, CHID and optional
        key hash if v2

        :param token: The obscured subscription data.
        :param version: This is the API version of the token.
        :param ckey_header: the Crypto-Key header bearing the public key
        (from Crypto-Key: p256ecdsa=)

        :raises ValueError: In the case of a malformed endpoint.

        :returns: a dict containing (uaid=UAID, chid=CHID, public_key=KEY)

        """

        token = self.fernet.decrypt(token.encode('utf8'))
        public_key = None
        if ckey_header:
            try:
                crypto_key = CryptoKey(ckey_header)
            except CryptoKeyException:
                raise InvalidTokenException("Invalid key data")
            label = crypto_key.get_label('p256ecdsa')
            try:
                public_key = base64url_decode(label)
            except:
                # Ignore missing and malformed app server keys.
                pass

        if version == 'v0':
            if not VALID_V0_TOKEN.match(token):
                raise InvalidTokenException("Corrupted push token")
            items = token.split(':')
            return dict(uaid=items[0], chid=items[1], public_key=public_key)
        if version == 'v1' and len(token) != 32:
            raise InvalidTokenException("Corrupted push token")
        if version == 'v2':
            if len(token) != 64:
                raise InvalidTokenException("Corrupted push token")
            if not public_key:
                raise InvalidTokenException("Invalid key data")
            if not constant_time.bytes_eq(sha256(public_key).digest(),
                                          token[32:]):
                raise InvalidTokenException("Key mismatch")
        return dict(uaid=token[:16].encode('hex'),
                    chid=token[16:32].encode('hex'),
                    public_key=public_key)
Exemplo n.º 48
0
    def verify(self, statement, auth_data, client_data_hash):
        jwt = statement['response']
        header, payload, sig = (websafe_decode(x) for x in jwt.split(b'.'))
        data = json.loads(payload.decode('utf8'))
        if not self.allow_rooted and data['ctsProfileMatch'] is not True:
            raise InvalidData('ctsProfileMatch must be true!')
        expected_nonce = sha256(auth_data + client_data_hash)
        if not bytes_eq(expected_nonce, websafe_decode(data['nonce'])):
            raise InvalidData('Nonce does not match!')

        data = json.loads(header.decode('utf8'))
        certs = [
            x509.load_der_x509_certificate(
                websafe_decode(x), default_backend())
            for x in data['x5c']
        ]
        certs.append(self._ca)

        cert = certs.pop(0)
        cn = cert.subject.get_attributes_for_oid(x509.NameOID.COMMON_NAME)
        if cn[0].value != 'attest.android.com':
            raise InvalidData('Certificate not issued to attest.android.com!')

        CoseKey.for_name(
            data['alg']
        ).from_cryptography_key(
            cert.public_key()
        ).verify(jwt.rsplit(b'.', 1)[0], sig)

        while certs:
            child = cert
            cert = certs.pop(0)
            pub = cert.public_key()
            if isinstance(pub, rsa.RSAPublicKey):
                pub.verify(
                    child.signature,
                    child.tbs_certificate_bytes,
                    padding.PKCS1v15(),
                    child.signature_hash_algorithm
                )
            elif isinstance(pub, ec.EllipticCurvePublicKey):
                pub.verify(
                    child.signature,
                    child.tbs_certificate_bytes,
                    ec.ECDSA(child.signature_hash_algorithm)
                )
Exemplo n.º 49
0
    def open(self, ciphertext, associated_data=None):
        """Verify and decrypt an AES-SIV ciphertext, authenticating and the associated data"""
        if not isinstance(ciphertext, bytes):
            raise TypeError("ciphertext must be bytes")

        if associated_data is None:
            associated_data = []

        v = ciphertext[0:block.SIZE]
        ciphertext = ciphertext[block.SIZE:]
        plaintext = self.__transform(v, ciphertext)

        t = self.__s2v(associated_data, plaintext)
        if not constant_time.bytes_eq(t, v):
            raise exceptions.IntegrityError("ciphertext verification failure!")

        return plaintext
Exemplo n.º 50
0
def aes_key_unwrap(wrapping_key, wrapped_key, aiv):
    if len(wrapped_key) < 24:
        raise InvalidUnwrap("Must be at least 24 bytes")

    if len(wrapped_key) % 8 != 0:
        raise InvalidUnwrap("The wrapped key must be a multiple of 8 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
    a = r.pop(0)
    a, r = unwrap_core(wrapping_key, a, r)
    if not bytes_eq(a, aiv):
        raise InvalidUnwrap()

    return b"".join(r)
Exemplo n.º 51
0
    def get_api_session(self):
        token = ''
        if b'api-token' in self.request.args:
            token = binary_type(self.request.args[b'api-token'][0])
        elif b'x-api-token' in self.request.headers:
            token = binary_type(self.request.headers[b'x-api-token'])

        # Assert the input is okay and the api_token state is acceptable
        if self.request.tid != 1 or \
           self.state.api_token_session is None or \
           not self.state.tenant_cache[self.request.tid].admin_api_token_digest:
            return

        stored_token_hash = self.state.tenant_cache[self.request.tid].admin_api_token_digest.encode()

        if constant_time.bytes_eq(sha512(token), stored_token_hash):
            return self.state.api_token_session
Exemplo n.º 52
0
    def decrypt_blob(self, nonce: bytes, salt: bytes, enc_blob: bytes) -> bytes:
        ''' Decrypts `enc_blob` using nonce and the initialized key.

            Expects `self.key` to be a 32-byte value.
            Expects the message-encoded `salt` to equal `self.salt`.

            Returns the decrypted blob.
        '''

        if not self.salt:
            raise ValueError('Salt must be set')

        if not bytes_eq(salt, self.salt):
            raise ValueError('Salts do not match.')

        algo = ChaCha20Poly1305(self.key)
        return algo.decrypt(nonce, enc_blob, None)
Exemplo n.º 53
0
    def authenticate(self, key, touch_callback=None):
        ct1 = self.send_cmd(INS.AUTHENTICATE, ALGO.TDES, SLOT.CARD_MANAGEMENT,
                            Tlv(TAG.DYN_AUTH, Tlv(0x80)))[4:12]
        backend = default_backend()
        try:
            cipher_key = algorithms.TripleDES(key)
        except ValueError:
            raise BadFormat(
                'Management key must be exactly 24 bytes long, '
                'was: {}'.format(len(key)), None)
        cipher = Cipher(cipher_key, modes.ECB(), backend)
        decryptor = cipher.decryptor()
        pt1 = decryptor.update(ct1) + decryptor.finalize()
        ct2 = os.urandom(8)

        if touch_callback is not None:
            touch_timer = Timer(0.500, touch_callback)
            touch_timer.start()

        try:
            pt2 = self.send_cmd(
                INS.AUTHENTICATE, ALGO.TDES, SLOT.CARD_MANAGEMENT,
                Tlv(TAG.DYN_AUTH,
                    Tlv(0x80, pt1) + Tlv(0x81, ct2)))[4:12]

        except APDUError as e:
            if e.sw == SW.SECURITY_CONDITION_NOT_SATISFIED:
                raise AuthenticationFailed('Incorrect management key', e.sw,
                                           self.version)

            logger.error('Failed to authenticate management key.', exc_info=e)
            raise

        except Exception as e:
            logger.error('Failed to authenticate management key.', exc_info=e)
            raise

        finally:
            if touch_callback is not None:
                touch_timer.cancel()

        encryptor = cipher.encryptor()
        pt2_cmp = encryptor.update(ct2) + encryptor.finalize()
        if not bytes_eq(pt2, pt2_cmp):
            raise ValueError('Device challenge did not match!')
        self._authenticated = True
Exemplo n.º 54
0
def jenkins_ci_notification(repo, pagure_ci_token, username=None):
    """
    Jenkins Build Notification
    --------------------------
    At the end of a build on Jenkins, this URL is used (if the project is
    rightly configured) to flag a pull-request with the result of the build.

    ::

        POST /api/0/ci/jenkins/<token>/build-finished

    """

    project = pagure.lib.get_project(SESSION, repo, user=username)
    if repo is None:
        raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOPROJECT)

    if not constant_time.bytes_eq(
            to_bytes(pagure_ci_token),
            to_bytes(project.ci_hook[0].pagure_ci_token)):
        raise pagure.exceptions.APIError(401, error_code=APIERROR.EINVALIDTOK)

    data = flask.request.get_json()
    if not data:
        APP.logger.debug("Bad Request: No JSON retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    build_id = data.get('build', {}).get('number')
    if not build_id:
        APP.logger.debug("Bad Request: No build ID retrieved")
        raise pagure.exceptions.APIError(400, error_code=APIERROR.EINVALIDREQ)

    try:
        lib_ci.process_jenkins_build(
            SESSION,
            project,
            build_id,
            requestfolder=APP.config['REQUESTS_FOLDER']
        )
    except pagure.exceptions.PagureException as err:
        APP.logger.error('Error processing jenkins notification', exc_info=err)
        raise pagure.exceptions.APIError(
            400, error_code=APIERROR.ENOCODE, error=str(err))

    APP.logger.info('Successfully proccessed jenkins notification')
    return ('', 204)
Exemplo n.º 55
0
    def authenticate(self, key, touch_callback=None):
        ct1 = self.send_cmd(INS.AUTHENTICATE, ALGO.TDES, SLOT.CARD_MANAGEMENT,
                            Tlv(TAG.DYN_AUTH, Tlv(0x80)))[4:12]
        backend = default_backend()
        try:
            cipher_key = algorithms.TripleDES(key)
        except ValueError:
            raise BadFormat('Management key must be exactly 24 bytes long, '
                            'was: {}'.format(len(key)), None)
        cipher = Cipher(cipher_key, modes.ECB(), backend)
        decryptor = cipher.decryptor()
        pt1 = decryptor.update(ct1) + decryptor.finalize()
        ct2 = os.urandom(8)

        if touch_callback is not None:
            touch_timer = Timer(0.500, touch_callback)
            touch_timer.start()

        try:
            pt2 = self.send_cmd(
                INS.AUTHENTICATE, ALGO.TDES, SLOT.CARD_MANAGEMENT,
                Tlv(TAG.DYN_AUTH, Tlv(0x80, pt1) + Tlv(0x81, ct2))
                )[4:12]

        except APDUError as e:
            if e.sw == SW.SECURITY_CONDITION_NOT_SATISFIED:
                raise AuthenticationFailed(
                    'Incorrect management key', e.sw, self.version)

            logger.error('Failed to authenticate management key.', exc_info=e)
            raise

        except Exception as e:
            logger.error('Failed to authenticate management key.', exc_info=e)
            raise

        finally:
            if touch_callback is not None:
                touch_timer.cancel()

        encryptor = cipher.encryptor()
        pt2_cmp = encryptor.update(ct2) + encryptor.finalize()
        if not bytes_eq(pt2, pt2_cmp):
            raise ValueError('Device challenge did not match!')
        self._authenticated = True
Exemplo n.º 56
0
def aes_key_unwrap(wrapping_key, wrapped_key, backend):
    if len(wrapped_key) < 24:
        raise ValueError("Must be at least 24 bytes")

    if len(wrapped_key) % 8 != 0:
        raise ValueError("The wrapped key must be a multiple of 8 bytes")

    if len(wrapping_key) not in [16, 24, 32]:
        raise ValueError("The wrapping key must be a valid AES key length")

    aiv = b"\xa6\xa6\xa6\xa6\xa6\xa6\xa6\xa6"
    r = [wrapped_key[i:i + 8] for i in range(0, len(wrapped_key), 8)]
    a = r.pop(0)
    a, r = _unwrap_core(wrapping_key, a, r, backend)
    if not bytes_eq(a, aiv):
        raise InvalidUnwrap()

    return b"".join(r)
Exemplo n.º 57
0
    def validate(self, reference, attempt):
        """Validate the provided password string.

        Reference is the correct password, which may be encrypted; attempt is
        clear text password attempt.
        """
        try:
            iterations_str, salt_b64, password_hash_b64 = reference.split('$')

            iterations = int(iterations_str)
            salt = base64.b64decode(salt_b64)
            password_hash = base64.b64decode(password_hash_b64)
        except (ValueError, TypeError):
            return False

        if iterations <= 0:
            return False

        attempt_hash = _encrypt(attempt, salt, iterations, len(password_hash))

        return constant_time.bytes_eq(attempt_hash, password_hash)
Exemplo n.º 58
0
def check_password(entered_password, user_password, seed=None):
    """ Version checking and returning the password

    :arg entered_password: password entered by the user.
    :type entered_password: str (Python 3) or unicode (Python 2)
    :arg user_password: the hashed string fetched from the database.
    :type user_password: bytes
    :return: a Boolean depending upon the entered_password, True if the
             password matches
    """
    if not isinstance(entered_password, six.text_type):
        raise ValueError("Entered password is not unicode text")
    if isinstance(user_password, six.text_type):
        user_password = user_password.encode("utf-8")

    if not user_password.count(b"$") >= 2:
        raise pagure.exceptions.PagureException(
            "Password of unknown version found in the database"
        )

    _, version, user_password = user_password.split(b"$", 2)

    if version == b"2":
        password = bcrypt.hashpw(
            entered_password.encode("utf-8"), user_password
        )
    elif version == b"1":
        password = "******" % (entered_password, seed)
        password = (
            hashlib.sha512(password.encode("utf-8"))
            .hexdigest()
            .encode("utf-8")
        )

    else:
        raise pagure.exceptions.PagureException(
            "Password of unknown version found in the database"
        )

    return constant_time.bytes_eq(password, user_password)
Exemplo n.º 59
0
 def finalize(self):
     # CommonCrypto has a yet another bug where you must make at least one
     # call to update. If you pass just AAD and call finalize without a call
     # to update you'll get null bytes for tag. The following update call
     # prevents this issue, which is present in at least 10.8 and 10.9.
     # Filed as rdar://18314580
     self.update(b"")
     tag_size = self._cipher.block_size // 8
     tag_buf = self._backend._ffi.new("unsigned char[]", tag_size)
     tag_len = self._backend._ffi.new("size_t *", tag_size)
     res = self._backend._lib.CCCryptorGCMFinal(
         self._ctx[0], tag_buf, tag_len
     )
     self._backend._check_cipher_response(res)
     self._backend._release_cipher_ctx(self._ctx)
     self._tag = self._backend._ffi.buffer(tag_buf)[:]
     if (self._operation == self._backend._lib.kCCDecrypt and
             not constant_time.bytes_eq(
                 self._tag[:len(self._mode.tag)], self._mode.tag
             )):
         raise InvalidTag
     return b""
Exemplo n.º 60
0
def _a128cbc_hs256_decrypt(key, iv, ciphertext, authdata, authtag):
    if not key or not len(key) >= 32:
        raise ValueError('key must be at least 256 bits for algorithm "A128CBC-HS256"')
    if not iv or len(iv) != 16:
        raise ValueError('iv must be 128 bits for algorithm "A128CBC-HS256"')
    if not ciphertext:
        raise ValueError('ciphertext must be specified')
    if not authdata:
        raise ValueError('authdata must be specified')
    if not authtag or len(authtag) != 16:
        raise ValueError('authtag must be be 128 bits for algorithm "A128CBC-HS256"')

    hmac_key = key[:16]
    aes_key = key[16:32]
    auth_data_length = _int_to_bigendian_8_bytes(len(authdata) * 8)

    # ensure the authtag is the expected length for SHA256 hash
    if not len(authtag) == 16:
        raise ValueError('invalid tag')

    hashdata = authdata + iv + ciphertext + auth_data_length
    hmac_hash = hmac.HMAC(hmac_key, hashes.SHA256(), backend=default_backend())
    hmac_hash.update(hashdata)
    tag = hmac_hash.finalize()[:16]

    if not constant_time.bytes_eq(tag, authtag):
        raise ValueError('"ciphertext" is not authentic')

    cipher = Cipher(algorithms.AES(aes_key), modes.CBC(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    plaintext = decryptor.update(ciphertext) + decryptor.finalize()

    # unpad the decrypted plaintext
    padder = padding.PKCS7(128).unpadder()
    plaintext = padder.update(plaintext) + padder.finalize()

    return plaintext