Example #1
0
def decrypt(algorithm, key, ciphertext, iv=None):
    """Decrypt the ciphertext and return the plaintext value."""
    from pskc.exceptions import DecryptionError
    if key is None:
        raise DecryptionError('No key available')
    if algorithm is None:
        raise DecryptionError('No algorithm specified')
    if len(key) not in algorithm_key_lengths(algorithm):
        raise DecryptionError('Invalid key length')
    if algorithm.endswith('#aes128-cbc') or \
            algorithm.endswith('#aes192-cbc') or \
            algorithm.endswith('#aes256-cbc'):
        from Crypto.Cipher import AES
        from pskc.crypto import unpad
        if not iv:
            iv = ciphertext[:AES.block_size]
            ciphertext = ciphertext[AES.block_size:]
        cipher = AES.new(key, AES.MODE_CBC, iv)
        return unpad(cipher.decrypt(ciphertext), AES.block_size)
    elif algorithm.endswith('#tripledes-cbc'):
        from Crypto.Cipher import DES3
        from pskc.crypto import unpad
        if not iv:
            iv = ciphertext[:DES3.block_size]
            ciphertext = ciphertext[DES3.block_size:]
        cipher = DES3.new(key, DES3.MODE_CBC, iv)
        return unpad(cipher.decrypt(ciphertext), DES3.block_size)
    elif algorithm.endswith('#kw-aes128') or \
            algorithm.endswith('#kw-aes192') or \
            algorithm.endswith('#kw-aes256'):
        from pskc.crypto.aeskw import unwrap
        return unwrap(ciphertext, key)
    elif algorithm.endswith('#kw-tripledes'):  # pragma: no branch
        from pskc.crypto.tripledeskw import unwrap
        return unwrap(ciphertext, key)
Example #2
0
def decrypt(algorithm, key, ciphertext, iv=None):
    """Decrypt the ciphertext and return the plaintext value."""
    from cryptography.hazmat.primitives.ciphers import algorithms
    from pskc.exceptions import DecryptionError
    if key is None:
        raise DecryptionError('No key available')
    if algorithm is None:
        raise DecryptionError('No algorithm specified')
    if len(key) not in algorithm_key_lengths(algorithm):
        raise DecryptionError('Invalid key length')
    if algorithm.endswith('#aes128-cbc') or \
            algorithm.endswith('#aes192-cbc') or \
            algorithm.endswith('#aes256-cbc'):
        return _decrypt_cbc(algorithms.AES, key, ciphertext, iv)
    elif algorithm.endswith('#tripledes-cbc'):
        return _decrypt_cbc(algorithms.TripleDES, key, ciphertext, iv)
    elif algorithm.endswith('#kw-aes128') or \
            algorithm.endswith('#kw-aes192') or \
            algorithm.endswith('#kw-aes256'):
        from pskc.crypto.aeskw import unwrap
        return unwrap(ciphertext, key)
    elif algorithm.endswith('#kw-tripledes'):
        from pskc.crypto.tripledeskw import unwrap
        return unwrap(ciphertext, key)
    elif (algorithm.endswith('#camellia128-cbc')
          or algorithm.endswith('#camellia192-cbc')
          or algorithm.endswith('#camellia256-cbc')):
        return _decrypt_cbc(algorithms.Camellia, key, ciphertext, iv)
    elif (algorithm.endswith('#kw-camellia128') or  # pragma: no branch
          algorithm.endswith('#kw-camellia192') or
          algorithm.endswith('#kw-camellia256')):
        from pskc.crypto.aeskw import unwrap
        return unwrap(ciphertext, key, algorithm=algorithms.Camellia)
Example #3
0
def algorithm_key_lengths(algorithm):
    """Return the possible key lengths for the configured algorithm."""
    from pskc.exceptions import DecryptionError
    if algorithm is None:
        raise DecryptionError('No algorithm specified')
    elif algorithm.endswith('#aes128-cbc') or \
            algorithm.endswith('#aes192-cbc') or \
            algorithm.endswith('#aes256-cbc'):
        return [int(algorithm[-7:-4]) // 8]
    elif algorithm.endswith('#tripledes-cbc') or \
            algorithm.endswith('#kw-tripledes'):
        return [16, 24]
    elif algorithm.endswith('#kw-aes128') or \
            algorithm.endswith('#kw-aes192') or \
            algorithm.endswith('#kw-aes256'):
        return [int(algorithm[-3:]) // 8]
    elif (algorithm.endswith('#camellia128-cbc')
          or algorithm.endswith('#camellia192-cbc')
          or algorithm.endswith('#camellia256-cbc')):
        return [int(algorithm[-7:-4]) // 8]
    elif (algorithm.endswith('#kw-camellia128')
          or algorithm.endswith('#kw-camellia192')
          or algorithm.endswith('#kw-camellia256')):
        return [int(algorithm[-3:]) // 8]
    else:
        raise DecryptionError('Unsupported algorithm: %r' % algorithm)
Example #4
0
def get_mac(algorithm, key, value):
    """Generate the MAC value over the specified value."""
    from pskc.exceptions import DecryptionError
    if algorithm is None:
        raise DecryptionError('No MAC algorithm set')
    hmacfn = get_hmac(algorithm)
    if hmacfn is None:
        raise DecryptionError('Unsupported MAC algorithm: %r' % algorithm)
    return hmacfn(key, value)
Example #5
0
def unwrap(ciphertext, key):
    """Unwrap a key (typically Triple DES key ) with another Triple DES key.

    This uses the algorithm from RFC 3217 to decrypt the ciphertext (the
    previously wrapped key) using the provided key."""
    if len(ciphertext) % DES3.block_size != 0:
        raise DecryptionError('Ciphertext length wrong')
    cipher = DES3.new(key, DES3.MODE_CBC, RFC3217_IV)
    tmp = cipher.decrypt(ciphertext)[::-1]
    cipher = DES3.new(key, DES3.MODE_CBC, tmp[:8])
    tmp = cipher.decrypt(tmp[8:])
    if tmp[-8:] == _cms_hash(tmp[:-8]):
        return tmp[:-8]
    raise DecryptionError('CMS key checksum error')
Example #6
0
def unwrap(ciphertext, key, iv=None, pad=None, algorithm=algorithms.AES):
    """Apply the AES key unwrap algorithm to the ciphertext.

    The iv can specify an initial value, otherwise the value from RFC 3394 or
    RFC 5649 will be used, depending on the value of pad.

    If pad is False, unpadding as described in RFC 5649 will be disabled,
    otherwise checking and removing the padding is automatically done.
    """
    if iv is not None:
        pad = False

    if len(ciphertext) % 8 != 0 or (pad is False and len(ciphertext) < 24):
        raise DecryptionError('Ciphertext length wrong')

    cipher = Cipher(algorithm(key), modes.ECB(), default_backend())
    decryptor = cipher.decryptor()
    n = len(ciphertext) // 8 - 1

    if n == 1:
        A, plaintext = _split(decryptor.update(ciphertext))  # noqa: N806
    else:
        A = ciphertext[:8]  # noqa: N806
        R = [
            ciphertext[(i + 1) * 8:(i + 2) * 8]  # noqa: N806
            for i in range(n)
        ]
        for j in reversed(range(6)):
            for i in reversed(range(n)):
                A = _strxor(A, struct.pack('>Q', n * j + i + 1))  # noqa: N806
                A, R[i] = _split(decryptor.update(A + R[i]))  # noqa: N806
        plaintext = b''.join(R)

    if iv is None:
        if A == RFC3394_IV and pad is not True:
            return plaintext
        elif A[:4] == RFC5649_IV and pad is not False:
            mli = struct.unpack('>I', A[4:])[0]
            # check padding length is valid and plaintext only contains zeros
            if 8 * (n - 1) < mli <= 8 * n and \
               plaintext.endswith((len(plaintext) - mli) * b'\0'):
                return plaintext[:mli]
    elif A == iv:
        return plaintext
    raise DecryptionError('IV does not match')
Example #7
0
def unpad(value, block_size):
    """Remove padding from the plaintext."""
    from pskc.exceptions import DecryptionError
    padding = ord(value[-1:])
    # only unpad if all padding bytes are the same
    if (padding > 0 and padding <= block_size
            and value[-padding:] == padding * chr(padding).encode('ascii')):
        return value[:-padding]
    raise DecryptionError('Invalid padding')
Example #8
0
def unwrap(ciphertext, key):
    """Unwrap a key (typically Triple DES key ) with another Triple DES key.

    This uses the algorithm from RFC 3217 to decrypt the ciphertext (the
    previously wrapped key) using the provided key.
    """
    if 8 * len(ciphertext) % algorithms.TripleDES.block_size != 0:
        raise DecryptionError('Ciphertext length wrong')
    backend = default_backend()
    cipher = Cipher(algorithms.TripleDES(key), modes.CBC(RFC3217_IV), backend)
    decryptor = cipher.decryptor()
    tmp = (decryptor.update(ciphertext) + decryptor.finalize())[::-1]
    cipher = Cipher(algorithms.TripleDES(key), modes.CBC(tmp[:8]), backend)
    decryptor = cipher.decryptor()
    tmp = decryptor.update(tmp[8:]) + decryptor.finalize()
    if tmp[-8:] == _cms_hash(tmp[:-8]):
        return tmp[:-8]
    raise DecryptionError('CMS key checksum error')
Example #9
0
def algorithm_key_lengths(algorithm):
    """Return the possible key lengths for the configured algorithm."""
    from pskc.exceptions import DecryptionError
    if algorithm is None:
        raise DecryptionError('No algorithm specified')
    elif algorithm.endswith('#aes128-cbc') or \
            algorithm.endswith('#aes192-cbc') or \
            algorithm.endswith('#aes256-cbc'):
        return [int(algorithm[-7:-4]) // 8]
    elif algorithm.endswith('#tripledes-cbc') or \
            algorithm.endswith('#kw-tripledes'):
        from Crypto.Cipher import DES3
        return list(DES3.key_size)
    elif algorithm.endswith('#kw-aes128') or \
            algorithm.endswith('#kw-aes192') or \
            algorithm.endswith('#kw-aes256'):
        return [int(algorithm[-3:]) // 8]
    else:
        raise DecryptionError('Unsupported algorithm: %r' % algorithm)
Example #10
0
def unwrap(ciphertext, key, iv=None, pad=None):
    """Apply the AES key unwrap algorithm to the ciphertext.

    The iv can specify an initial value, otherwise the value from RFC 3394 or
    RFC 5649 will be used, depending on the value of pad.

    If pad is False, unpadding as described in RFC 5649 will be disabled,
    otherwise checking and removing the padding is automatically done."""

    if iv is not None:
        pad = False

    if len(ciphertext) % 8 != 0 or (pad is False and len(ciphertext) < 24):
        raise DecryptionError('Ciphertext length wrong')

    decrypt = AES.new(key).decrypt
    n = len(ciphertext) // 8 - 1

    if n == 1:
        A, plaintext = _split(decrypt(ciphertext))
    else:
        A = ciphertext[:8]
        R = [ciphertext[(i + 1) * 8:(i + 2) * 8]
             for i in range(n)]
        for j in reversed(range(6)):
            for i in reversed(range(n)):
                A = strxor(A, long_to_bytes(n * j + i + 1, 8))
                A, R[i] = _split(decrypt(A + R[i]))
        plaintext = b''.join(R)

    if iv is None:
        if A == RFC3394_IV and pad is not True:
            return plaintext
        elif A[:4] == RFC5649_IV and pad is not False:
            mli = bytes_to_long(A[4:])
            # check padding length is valid and plaintext only contains zeros
            if 8 * (n - 1) < mli <= 8 * n and \
               plaintext.endswith((len(plaintext) - mli) * b'\0'):
                return plaintext[:mli]
    elif A == iv:
        return plaintext
    raise DecryptionError('IV does not match')
Example #11
0
def _get_hash_obj(algorithm, *args):
    """Return an instantiated hash object."""
    import hashlib
    from pskc.algorithms import normalise_algorithm
    from pskc.exceptions import DecryptionError
    match = _hmac_url_re.search(normalise_algorithm(algorithm) or '')
    if match:
        try:
            return hashlib.new(match.group('hash'), *args)
        except ValueError:
            pass
    raise DecryptionError('Unsupported MAC algorithm: %r' % algorithm)
Example #12
0
 def get_value(self, pskc):
     """Provide the decrypted value."""
     from pskc.exceptions import DecryptionError
     plaintext = pskc.encryption.decrypt_value(
         self.cipher_value, self.algorithm)
     # allow MAC over plaintext or cipertext
     # (RFC 6030 implies MAC over ciphertext but older draft used
     # MAC over plaintext)
     if self.mac_value and self.mac_value not in (
             pskc.mac.generate_mac(self.cipher_value),
             pskc.mac.generate_mac(plaintext)):
         raise DecryptionError('MAC value does not match')
     return plaintext
Example #13
0
 def get_value(self):
     """Provide the attribute value, decrypting as needed."""
     from pskc.exceptions import DecryptionError
     if self.value is not None:
         return self.value
     if self.cipher_value:
         plaintext = self.pskc.encryption.decrypt_value(
             self.cipher_value, self.algorithm)
         # allow MAC over plaintext or cipertext
         # (RFC6030 implies MAC over ciphertext but older draft used
         # MAC over plaintext)
         if self.value_mac and self.value_mac not in (
                 self.pskc.mac.generate_mac(self.cipher_value),
                 self.pskc.mac.generate_mac(plaintext)):
             raise DecryptionError('MAC value does not match')
         return self._from_bin(plaintext)
Example #14
0
def _decrypt_cbc(algorithm, key, ciphertext, iv=None):
    """Decrypt the ciphertext and return the plaintext value."""
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import padding
    from cryptography.hazmat.primitives.ciphers import Cipher, modes
    from pskc.exceptions import DecryptionError
    if not iv:
        iv = ciphertext[:algorithm.block_size // 8]
        ciphertext = ciphertext[algorithm.block_size // 8:]
    cipher = Cipher(algorithm(key), modes.CBC(iv), backend=default_backend())
    decryptor = cipher.decryptor()
    unpadder = padding.PKCS7(algorithm.block_size).unpadder()
    try:
        return unpadder.update(
            decryptor.update(ciphertext) +
            decryptor.finalize()) + unpadder.finalize()
    except ValueError:
        raise DecryptionError('Invalid padding')