Folded PKCS#5 algos into asn1crypto.algos
diff --git a/asn1crypto/algos.py b/asn1crypto/algos.py
index fb801e2..eb32a60 100644
--- a/asn1crypto/algos.py
+++ b/asn1crypto/algos.py
@@ -2,11 +2,13 @@
 from __future__ import unicode_literals
 from __future__ import absolute_import
 
-from .core import Any, Integer, ObjectIdentifier, OctetString, Sequence
+from .core import Any, Choice, Integer, ObjectIdentifier, OctetString, Sequence
 
 
-# OID in this file are pulled from https://tools.ietf.org/html/rfc3279,
-# https://tools.ietf.org/html/rfc4055 and https://tools.ietf.org/html/rfc5758
+# Structures and OIDs in this file are pulled from
+# https://tools.ietf.org/html/rfc3279, https://tools.ietf.org/html/rfc4055,
+# https://tools.ietf.org/html/rfc5758, https://tools.ietf.org/html/rfc7292,
+# http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf
 
 class AlgorithmIdentifier(Sequence):
     _fields = [
@@ -100,6 +102,39 @@
     ]
 
 
+class Pbkdf2Salt(Choice):
+    _fields = [
+        ('specified', OctetString),
+        ('other_source', AlgorithmIdentifier),
+    ]
+
+
+class Pbkdf2Params(Sequence):
+    _fields = [
+        ('salt', Pbkdf2Salt),
+        ('iteration_count', Integer),
+        ('key_length', Integer, {'optional': True}),
+        ('prf', HmacAlgorithm, {'optional': True}),
+    ]
+
+
+class KdfAlgorithmId(ObjectIdentifier):
+    _map = {
+        '1.2.840.113549.1.5.12': 'pbkdf2'
+    }
+
+
+class KdfAlgorithm(Sequence):
+    _fields = [
+        ('algorithm', KdfAlgorithmId),
+        ('parameters', Any, {'optional': True}),
+    ]
+    _oid_pair = ('algorithm', 'parameters')
+    _oid_specs = {
+        'pbkdf2': Pbkdf2Params
+    }
+
+
 class Rc2Params(Sequence):
     _fields = [
         ('rc2_parameter_version', Integer, {'optional': True}),
@@ -122,6 +157,13 @@
     ]
 
 
+class Pbes1Params(Sequence):
+    _fields = [
+        ('salt', OctetString),
+        ('iterations', Integer),
+    ]
+
+
 class EncryptionAlgorithmId(ObjectIdentifier):
     _map = {
         '1.3.14.3.2.7': 'des',
@@ -131,6 +173,21 @@
         '2.16.840.1.101.3.4.1.2': 'aes128',
         '2.16.840.1.101.3.4.1.22': 'aes192',
         '2.16.840.1.101.3.4.1.42': 'aes256',
+        # From PKCS#5
+        '1.2.840.113549.1.5.13': 'pbes2',
+        '1.2.840.113549.1.5.1': 'pbes1_md2_des',
+        '1.2.840.113549.1.5.3': 'pbes1_md5_des',
+        '1.2.840.113549.1.5.4': 'pbes1_md2_rc2',
+        '1.2.840.113549.1.5.6': 'pbes1_md5_rc2',
+        '1.2.840.113549.1.5.10': 'pbes1_sha1_des',
+        '1.2.840.113549.1.5.11': 'pbes1_sha1_rc2',
+        # From PKCS#12
+        '1.2.840.113549.1.12.1.1': 'pkcs12_sha1_rc4_128',
+        '1.2.840.113549.1.12.1.2': 'pkcs12_sha1_rc4_40',
+        '1.2.840.113549.1.12.1.3': 'pkcs12_sha1_tripledes_3key',
+        '1.2.840.113549.1.12.1.4': 'pkcs12_sha1_tripledes_2key',
+        '1.2.840.113549.1.12.1.5': 'pkcs12_sha1_rc2_128',
+        '1.2.840.113549.1.12.1.6': 'pkcs12_sha1_rc2_40',
     }
 
 
@@ -149,12 +206,126 @@
         'aes128': OctetString,
         'aes192': OctetString,
         'aes256': OctetString,
+        # From PKCS#5
+        'pbes1_md2_des': Pbes1Params,
+        'pbes1_md5_des': Pbes1Params,
+        'pbes1_md2_rc2': Pbes1Params,
+        'pbes1_md5_rc2': Pbes1Params,
+        'pbes1_sha1_des': Pbes1Params,
+        'pbes1_sha1_rc2': Pbes1Params,
+        # From PKCS#12
+        'pkcs12_sha1_rc4_128': Pbes1Params,
+        'pkcs12_sha1_rc4_40': Pbes1Params,
+        'pkcs12_sha1_tripledes_3key': Pbes1Params,
+        'pkcs12_sha1_tripledes_2key': Pbes1Params,
+        'pkcs12_sha1_rc2_128': Pbes1Params,
+        'pkcs12_sha1_rc2_40': Pbes1Params,
     }
 
     @property
+    def kdf(self):
+        """
+        Returns the name of the key derivation function to use.
+
+        :return:
+            A unicode from of one of the following: "pbkdf1", "pbkdf2", "pkcs12_kdf"
+        """
+
+        encryption_algo = self['algorithm'].native
+
+        if encryption_algo == 'pbes2':
+            return self['parameters'].parsed['key_derivation_func']['algorithm'].native
+
+        if encryption_algo.find('.') == -1:
+            if encryption_algo.find('_') != -1:
+                encryption_algo, _ = encryption_algo.split('_', 1)
+
+                if encryption_algo == 'pbes1':
+                    return 'pbkdf1'
+
+                if encryption_algo == 'pkcs12':
+                    return 'pkcs12_kdf'
+
+            raise ValueError('Encryption algorithm "%s" does not have a registered key derivation function' % encryption_algo)
+
+        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation function' % encryption_algo)
+
+    @property
+    def kdf_hmac(self):
+        """
+        Returns the HMAC algorithm to use with the KDF.
+
+        :return:
+            A unicode string of one of the following: "md2", "md5", "sha1", "sha224", "sha256", "sha384", "sha512"
+        """
+
+        encryption_algo = self['algorithm'].native
+
+        if encryption_algo == 'pbes2':
+            return self['parameters'].parsed['key_derivation_func']['parameters']['prf']['algorithm'].native
+
+        if encryption_algo.find('.') == -1:
+            if encryption_algo.find('_') != -1:
+                _, hmac_algo, _ = encryption_algo.split('_', 2)
+                return hmac_algo
+
+            raise ValueError('Encryption algorithm "%s" does not have a registered key derivation function' % encryption_algo)
+
+        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation hmac algorithm' % encryption_algo)
+
+    @property
+    def kdf_salt(self):
+        """
+        Returns the byte string to use as the salt for the KDF.
+
+        :return:
+            A byte string
+        """
+
+        encryption_algo = self['algorithm'].native
+
+        if encryption_algo == 'pbes2':
+            salt = self['parameters'].parsed['key_derivation_func']['algorithm']['salt']
+
+            if salt.name == 'other_source':
+                raise ValueError('Can not determine key derivation salt - the reversed-for-future-use other source salt choice was specified in the PBKDF2 params structure')
+
+            return salt.native
+
+        if encryption_algo.find('.') == -1:
+            if encryption_algo.find('_') != -1:
+                return self['parameters']['salt'].native
+
+            raise ValueError('Encryption algorithm "%s" does not have a registered key derivation function' % encryption_algo)
+
+        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation salt' % encryption_algo)
+
+    @property
+    def kdf_iterations(self):
+        """
+        Returns the number of iterations that should be run via the KDF.
+
+        :return:
+            An integer
+        """
+
+        encryption_algo = self['algorithm'].native
+
+        if encryption_algo == 'pbes2':
+            return self['parameters']['key_derivation_func']['algorithm']['iteration_count'].native
+
+        if encryption_algo.find('.') == -1:
+            if encryption_algo.find('_') != -1:
+                return self['parameters']['iterations'].native
+
+            raise ValueError('Encryption algorithm "%s" does not have a registered key derivation function' % encryption_algo)
+
+        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation iterations' % encryption_algo)
+
+    @property
     def key_length(self):
         """
-        Returns the key length to pass to the cipher. The PKCS#5 spec does
+        Returns the key length to pass to the cipher/kdf. The PKCS#5 spec does
         not specify a way to store the RC5 key length, however this tends not
         to be a problem since OpenSSL does not support RC5 in PKCS#8 and OS X
         does not provide an RC5 cipher for use in the Security Transforms
@@ -167,7 +338,7 @@
             An integer representing the length in bytes
         """
 
-        cipher = self['algorithm'].native
+        encryption_algo = self['algorithm'].native
 
         cipher_lengths = {
             'des': 8,
@@ -177,10 +348,10 @@
             'aes256': 32,
         }
 
-        if cipher in cipher_lengths:
-            return cipher_lengths[cipher]
+        if encryption_algo in cipher_lengths:
+            return cipher_lengths[encryption_algo]
 
-        if cipher == 'rc2':
+        if encryption_algo == 'rc2':
             rc2_params = self['parameters'].parsed['encryption_scheme']['parameters'].parsed
             rc2_parameter_version = rc2_params['rc2_parameter_version'].native
 
@@ -202,7 +373,35 @@
 
             raise ValueError('Invalid RC2 parameter version found in EncryptionAlgorithm parameters')
 
-        raise ValueError('Unable to determine the key length for the encryption scheme "%s"' % cipher)
+        if encryption_algo == 'pbes2':
+            key_length = self['parameters']['key_derivation_func']['algorithm']['key_length'].native
+            if key_length is not None:
+                return key_length
+
+            # If the KDF params don't specify the key size, we can infer it from
+            # the encryption scheme for all schemes except for RC5. However, in
+            # practical terms, neither OpenSSL or OS X support RC5 for PKCS#8
+            # so it is unlikely to be an issue that is run into.
+
+            return self['parameters']['encryption_scheme'].key_length
+
+        if encryption_algo.find('.') == -1:
+            return {
+                'pbes1_md2_des': 8,
+                'pbes1_md5_des': 8,
+                'pbes1_md2_rc2': 8,
+                'pbes1_md5_rc2': 8,
+                'pbes1_sha1_des': 8,
+                'pbes1_sha1_rc2': 8,
+                'pkcs12_sha1_rc4_128': 16,
+                'pkcs12_sha1_rc4_40': 5,
+                'pkcs12_sha1_tripledes_3key': 24,
+                'pkcs12_sha1_tripledes_2key': 16,
+                'pkcs12_sha1_rc2_128': 16,
+                'pkcs12_sha1_rc2_40': 5,
+            }[encryption_algo]
+
+        raise ValueError('Unrecognized encryption algorithm "%s"' % encryption_algo)
 
     @property
     def encryption_cipher(self):
@@ -215,7 +414,7 @@
             A unicode string from one of the following: "rc2", "rc5", "des", "tripledes", "aes"
         """
 
-        cipher = self['algorithm'].native
+        encryption_algo = self['algorithm'].native
 
         cipher_map = {
             'des': 'des',
@@ -226,10 +425,29 @@
             'rc2': 'rc2',
             'rc5': 'rc5',
         }
-        if cipher in cipher_map:
-            return cipher_map[cipher]
+        if encryption_algo in cipher_map:
+            return cipher_map[encryption_algo]
 
-        raise ValueError('Unrecognized encryption cipher "%s"' % cipher)
+        if encryption_algo == 'pbes2':
+            return self['parameters']['encryption_scheme'].encryption_cipher
+
+        if encryption_algo.find('.') == -1:
+            return {
+                'pbes1_md2_des': 'des',
+                'pbes1_md5_des': 'des',
+                'pbes1_md2_rc2': 'rc2',
+                'pbes1_md5_rc2': 'rc2',
+                'pbes1_sha1_des': 'des',
+                'pbes1_sha1_rc2': 'rc2',
+                'pkcs12_sha1_rc4_128': 'rc4',
+                'pkcs12_sha1_rc4_40': 'rc4',
+                'pkcs12_sha1_tripledes_3key': 'tripledes',
+                'pkcs12_sha1_tripledes_2key': 'tripledes',
+                'pkcs12_sha1_rc2_128': 'rc2',
+                'pkcs12_sha1_rc2_40': 'rc2',
+            }[encryption_algo]
+
+        raise ValueError('Unrecognized encryption algorithm "%s"' % encryption_algo)
 
     @property
     def encryption_block_size(self):
@@ -240,7 +458,7 @@
             An integer that is the block size in bytes
         """
 
-        cipher = self['algorithm'].native
+        encryption_algo = self['algorithm'].native
 
         cipher_map = {
             'des': 8,
@@ -250,13 +468,32 @@
             'aes256': 16,
             'rc2': 8,
         }
-        if cipher in cipher_map:
-            return cipher_map[cipher]
+        if encryption_algo in cipher_map:
+            return cipher_map[encryption_algo]
 
-        if cipher == 'rc5':
+        if encryption_algo == 'rc5':
             return self['parameters'].parsed['block_size_in_bits'].native / 8
 
-        raise ValueError('Unrecognized encryption cipher "%s", can not determine block size' % cipher)
+        if encryption_algo == 'pbes2':
+            return self['parameters']['encryption_scheme'].encryption_block_size
+
+        if encryption_algo.find('.') == -1:
+            return {
+                'pbes1_md2_des': 8,
+                'pbes1_md5_des': 8,
+                'pbes1_md2_rc2': 8,
+                'pbes1_md5_rc2': 8,
+                'pbes1_sha1_des': 8,
+                'pbes1_sha1_rc2': 8,
+                'pkcs12_sha1_rc4_128': 0,
+                'pkcs12_sha1_rc4_40': 0,
+                'pkcs12_sha1_tripledes_3key': 8,
+                'pkcs12_sha1_tripledes_2key': 8,
+                'pkcs12_sha1_rc2_128': 8,
+                'pkcs12_sha1_rc2_40': 8,
+            }[encryption_algo]
+
+        raise ValueError('Unrecognized encryption algorithm "%s"' % encryption_algo)
 
     @property
     def encryption_iv(self):
@@ -269,13 +506,59 @@
             A byte string or None
         """
 
-        cipher = self['algorithm'].native
+        encryption_algo = self['algorithm'].native
 
-        if cipher == 'rc2' or cipher == 'rc5':
+        if encryption_algo in ('rc2', 'rc5'):
             return self['parameters'].parsed['iv'].native
 
         # For DES/Triple DES and AES the IV is the entirety of the parameters
-        if cipher.find('.') == -1:
+        if encryption_algo in ('des', 'tripledes_3key', 'aes128', 'aes192', 'aes256'):
             return self['parameters'].native
 
-        raise ValueError('Unrecognized encryption cipher "%s", can not determine initialization vector' % cipher)
+        if encryption_algo == 'pbes2':
+            return self['parameters']['encryption_scheme'].encryption_iv
+
+        # All of the PBES1 algos use their KDF to create the IV. For the pbkdf1,
+        # the KDF is told to generate a key that is an extra 8 bytes long, and
+        # that is used for the IV. For the PKCS#12 KDF, it is called with an id
+        # of 2 to generate the IV. In either case, we can't return the IV
+        # without knowing the user's password.
+        if encryption_algo.find('.') == -1:
+            return None
+
+        raise ValueError('Unrecognized encryption algorithm "%s"' % encryption_algo)
+
+
+class Pbes2Params(Sequence):
+    _fields = [
+        ('key_derivation_func', KdfAlgorithm),
+        ('encryption_scheme', EncryptionAlgorithm),
+    ]
+
+
+class Pbmac1Params(Sequence):
+    _fields = [
+        ('key_derivation_func', KdfAlgorithm),
+        ('message_auth_scheme', HmacAlgorithm),
+    ]
+
+
+class Pkcs5MacId(ObjectIdentifier):
+    _map = {
+        '1.2.840.113549.1.5.14': 'pbmac1',
+    }
+
+
+class Pkcs5MacAlgorithm(Sequence):
+    _fields = [
+        ('algorithm', Pkcs5MacId),
+        ('parameters', Any),
+    ]
+
+    _oid_pair = ('algorithm', 'parameters')
+    _oid_specs = {
+        'pbmac1': Pbmac1Params,
+    }
+
+
+EncryptionAlgorithm._oid_specs['pbes2'] = Pbes2Params  #pylint: disable=W0212
diff --git a/asn1crypto/cms.py b/asn1crypto/cms.py
index 0c2c42f..85fe9d0 100644
--- a/asn1crypto/cms.py
+++ b/asn1crypto/cms.py
@@ -9,8 +9,10 @@
 
 from .algos import (
     DigestAlgorithm,
-    SignedDigestAlgorithm,
+    EncryptionAlgorithm,
     HmacAlgorithm,
+    KdfAlgorithm,
+    SignedDigestAlgorithm,
 )
 from .core import (
     Any,
@@ -29,7 +31,6 @@
 from .crl import CertificateList
 from .keys import PublicKeyInfo
 from .ocsp import OCSPResponse
-from .pkcs5 import KdfAlgorithm, Pkcs5EncryptionAlgorithm
 from .x509 import Attributes, Certificate, Extensions, GeneralNames, Name
 
 
@@ -521,7 +522,7 @@
 class EncryptedContentInfo(Sequence):
     _fields = [
         ('content_type', ContentType),
-        ('content_encryption_algorithm', Pkcs5EncryptionAlgorithm),
+        ('content_encryption_algorithm', EncryptionAlgorithm),
         ('encrypted_content', OctetString, {'tag_type': 'implicit', 'tag': 0, 'optional': True}),
     ]
 
diff --git a/asn1crypto/keys.py b/asn1crypto/keys.py
index 76ba948..9632b33 100644
--- a/asn1crypto/keys.py
+++ b/asn1crypto/keys.py
@@ -5,7 +5,7 @@
 import hashlib
 from decimal import localcontext
 
-from .algos import DigestAlgorithm
+from .algos import DigestAlgorithm, EncryptionAlgorithm
 from .core import (
     Any,
     Choice,
@@ -20,7 +20,6 @@
     SequenceOf,
     SetOf,
 )
-from .pkcs5 import Pkcs5EncryptionAlgorithm
 
 try:
     # Python 2
@@ -497,7 +496,7 @@
     """
 
     _fields = [
-        ('encryption_algorithm', Pkcs5EncryptionAlgorithm),
+        ('encryption_algorithm', EncryptionAlgorithm),
         ('encrypted_data', OctetString),
     ]
 
diff --git a/asn1crypto/pkcs5.py b/asn1crypto/pkcs5.py
deleted file mode 100644
index 1043567..0000000
--- a/asn1crypto/pkcs5.py
+++ /dev/null
@@ -1,354 +0,0 @@
-# coding: utf-8
-from __future__ import unicode_literals
-from __future__ import absolute_import
-
-from .algos import AlgorithmIdentifier, EncryptionAlgorithm, HmacAlgorithm
-from .core import Any, Choice, Integer, ObjectIdentifier, OctetString, Sequence
-
-
-# The structures in this file are taken from
-# http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf
-# with extra OIDs from https://tools.ietf.org/html/rfc7292 (PKCS#12)
-
-
-class Pbes1Params(Sequence):
-    _fields = [
-        ('salt', OctetString),
-        ('iterations', Integer),
-    ]
-
-
-class Pbkdf2Salt(Choice):
-    _fields = [
-        ('specified', OctetString),
-        ('other_source', AlgorithmIdentifier),
-    ]
-
-
-class Pbkdf2Params(Sequence):
-    _fields = [
-        ('salt', Pbkdf2Salt),
-        ('iteration_count', Integer),
-        ('key_length', Integer, {'optional': True}),
-        ('prf', HmacAlgorithm, {'optional': True}),
-    ]
-
-
-class KdfAlgorithmId(ObjectIdentifier):
-    _map = {
-        '1.2.840.113549.1.5.12': 'pbkdf2'
-    }
-
-
-class KdfAlgorithm(Sequence):
-    _fields = [
-        ('algorithm', KdfAlgorithmId),
-        ('parameters', Any, {'optional': True}),
-    ]
-    _oid_pair = ('algorithm', 'parameters')
-    _oid_specs = {
-        'pbkdf2': Pbkdf2Params
-    }
-
-
-class Pbes2Params(Sequence):
-    _fields = [
-        ('key_derivation_func', KdfAlgorithm),
-        ('encryption_scheme', EncryptionAlgorithm),
-    ]
-
-
-class Pkcs5EncryptionId(ObjectIdentifier):
-    _map = {
-        '1.2.840.113549.1.5.13': 'pbes2',
-        '1.2.840.113549.1.5.1': 'pbes1_md2_des',
-        '1.2.840.113549.1.5.3': 'pbes1_md5_des',
-        '1.2.840.113549.1.5.4': 'pbes1_md2_rc2',
-        '1.2.840.113549.1.5.6': 'pbes1_md5_rc2',
-        '1.2.840.113549.1.5.10': 'pbes1_sha1_des',
-        '1.2.840.113549.1.5.11': 'pbes1_sha1_rc2',
-        '1.2.840.113549.1.12.1.1': 'pkcs12_sha1_rc4_128',
-        '1.2.840.113549.1.12.1.2': 'pkcs12_sha1_rc4_40',
-        '1.2.840.113549.1.12.1.3': 'pkcs12_sha1_tripledes_3key',
-        '1.2.840.113549.1.12.1.4': 'pkcs12_sha1_tripledes_2key',
-        '1.2.840.113549.1.12.1.5': 'pkcs12_sha1_rc2_128',
-        '1.2.840.113549.1.12.1.6': 'pkcs12_sha1_rc2_40',
-    }
-
-
-class Pkcs5EncryptionAlgorithm(Sequence):
-    _fields = [
-        ('algorithm', Pkcs5EncryptionId),
-        ('parameters', Any),
-    ]
-
-    _oid_pair = ('algorithm', 'parameters')
-    _oid_specs = {
-        'pbes2': Pbes2Params,
-        'pbes1_md2_des': Pbes1Params,
-        'pbes1_md5_des': Pbes1Params,
-        'pbes1_md2_rc2': Pbes1Params,
-        'pbes1_md5_rc2': Pbes1Params,
-        'pbes1_sha1_des': Pbes1Params,
-        'pbes1_sha1_rc2': Pbes1Params,
-        'pkcs12_sha1_rc4_128': Pbes1Params,
-        'pkcs12_sha1_rc4_40': Pbes1Params,
-        'pkcs12_sha1_tripledes_3key': Pbes1Params,
-        'pkcs12_sha1_tripledes_2key': Pbes1Params,
-        'pkcs12_sha1_rc2_128': Pbes1Params,
-        'pkcs12_sha1_rc2_40': Pbes1Params,
-    }
-
-    @property
-    def kdf(self):
-        """
-        Returns the name of the key derivation function to use.
-
-        :return:
-            A unicode from of one of the following: "pbkdf1", "pbkdf2", "pkcs12_kdf"
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            return self['parameters'].parsed['key_derivation_func']['algorithm'].native
-
-        if encryption_algo.find('.') == -1:
-            encryption_algo, _ = self['algorithm'].native.split('_', 1)
-
-            if encryption_algo == 'pbes1':
-                return 'pbkdf1'
-
-            if encryption_algo == 'pkcs12':
-                return 'pkcs12_kdf'
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation function' % encryption_algo)
-
-    @property
-    def kdf_hmac(self):
-        """
-        Returns the HMAC algorithm to use with the KDF.
-
-        :return:
-            A unicode string of one of the following: "md2", "md5", "sha1", "sha224", "sha256", "sha384", "sha512"
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            return self['parameters'].parsed['key_derivation_func']['parameters']['prf']['algorithm'].native
-
-        if encryption_algo.find('.') == -1:
-            _, hmac_algo, _ = self['algorithm'].native.split('_', 2)
-            return hmac_algo
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation hmac algorithm' % encryption_algo)
-
-    @property
-    def kdf_salt(self):
-        """
-        Returns the byte string to use as the salt for the KDF.
-
-        :return:
-            A byte string
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            salt = self['parameters'].parsed['key_derivation_func']['algorithm']['salt']
-
-            if salt.name == 'other_source':
-                raise ValueError('Can not determine key derivation salt - the reversed-for-future-use other source salt choice was specified in the PBKDF2 params structure')
-
-            return salt.native
-
-        if encryption_algo.find('.') == -1:
-            return self['parameters']['salt'].native
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation salt' % encryption_algo)
-
-    @property
-    def kdf_iterations(self):
-        """
-        Returns the number of iterations that should be run via the KDF.
-
-        :return:
-            An integer
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            return self['parameters']['key_derivation_func']['algorithm']['iteration_count'].native
-
-        if encryption_algo.find('.') == -1:
-            return self['parameters']['iterations'].native
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation iterations' % encryption_algo)
-
-    @property
-    def key_length(self):
-        """
-        Returns the key length to pass to the KDF/cipher. The PKCS#5 spec does
-        not specify a way to store the RC5 key length, however this tends not
-        to be a problem since OpenSSL does not support RC5 in PKCS#8 and OS X
-        does not provide an RC5 cipher for use in the Security Transforms
-        library.
-
-        :raises:
-            ValueError - when the key length can not be determined
-
-        :return:
-            An integer representing the length in bytes
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            key_length = self['parameters']['key_derivation_func']['algorithm']['key_length'].native
-            if key_length is not None:
-                return key_length
-
-            # If the KDF params don't specify the key size, we can infer it from
-            # the encryption scheme for all schemes except for RC5. However, in
-            # practical terms, neither OpenSSL or OS X support RC5 for PKCS#8
-            # so it is unlikely to be an issue that is run into.
-
-            return self['parameters']['encryption_scheme'].key_length
-
-        if encryption_algo.find('.') == -1:
-            return {
-                'pbes1_md2_des': 8,
-                'pbes1_md5_des': 8,
-                'pbes1_md2_rc2': 8,
-                'pbes1_md5_rc2': 8,
-                'pbes1_sha1_des': 8,
-                'pbes1_sha1_rc2': 8,
-                'pkcs12_sha1_rc4_128': 16,
-                'pkcs12_sha1_rc4_40': 5,
-                'pkcs12_sha1_tripledes_3key': 24,
-                'pkcs12_sha1_tripledes_2key': 16,
-                'pkcs12_sha1_rc2_128': 16,
-                'pkcs12_sha1_rc2_40': 5,
-            }[encryption_algo]
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine key derivation function key length' % encryption_algo)
-
-    @property
-    def encryption_cipher(self):
-        """
-        Returns the name of the symmetric encryption cipher to use. The key
-        length can be retrieved via the .key_length property to disabiguate
-        between different variations of TripleDES, AES, and the RC* ciphers.
-
-        :return:
-            A unicode string from one of the following: "rc2", "rc4", "rc5", "des", "tripledes", "aes"
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            return self['parameters']['encryption_scheme'].encryption_cipher
-
-        if encryption_algo.find('.') == -1:
-            return {
-                'pbes1_md2_des': 'des',
-                'pbes1_md5_des': 'des',
-                'pbes1_md2_rc2': 'rc2',
-                'pbes1_md5_rc2': 'rc2',
-                'pbes1_sha1_des': 'des',
-                'pbes1_sha1_rc2': 'rc2',
-                'pkcs12_sha1_rc4_128': 'rc4',
-                'pkcs12_sha1_rc4_40': 'rc4',
-                'pkcs12_sha1_tripledes_3key': 'tripledes',
-                'pkcs12_sha1_tripledes_2key': 'tripledes',
-                'pkcs12_sha1_rc2_128': 'rc2',
-                'pkcs12_sha1_rc2_40': 'rc2',
-            }[encryption_algo]
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine encryption cipher' % encryption_algo)
-
-    @property
-    def encryption_block_size(self):
-        """
-        Returns the block size of the encryption cipher, in bytes. For RC4, a
-        stream cipher, 0 is returned.
-
-        :return:
-            An integer that is the block size in bytes
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            return self['parameters']['encryption_scheme'].encryption_block_size
-
-        if encryption_algo.find('.') == -1:
-            return {
-                'pbes1_md2_des': 8,
-                'pbes1_md5_des': 8,
-                'pbes1_md2_rc2': 8,
-                'pbes1_md5_rc2': 8,
-                'pbes1_sha1_des': 8,
-                'pbes1_sha1_rc2': 8,
-                'pkcs12_sha1_rc4_128': 0,
-                'pkcs12_sha1_rc4_40': 0,
-                'pkcs12_sha1_tripledes_3key': 8,
-                'pkcs12_sha1_tripledes_2key': 8,
-                'pkcs12_sha1_rc2_128': 8,
-                'pkcs12_sha1_rc2_40': 8,
-            }[encryption_algo]
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine encryption block size' % encryption_algo)
-
-    @property
-    def encryption_iv(self):
-        """
-        Returns the byte string of the initialization vector for the encryption
-        scheme. Only the PBES2 stores the IV in the params. For PBES1, the IV
-        is derived from the KDF and this property will return None.
-
-        :return:
-            A byte string or None
-        """
-
-        encryption_algo = self['algorithm'].native
-
-        if encryption_algo == 'pbes2':
-            return self['parameters']['encryption_scheme'].encryption_iv
-
-        # All of the PBES1 algos use their KDF to create the IV. For the pbkdf1,
-        # the KDF is told to generate a key that is an extra 8 bytes long, and
-        # that is used for the IV. For the PKCS#12 KDF, it is called with an id
-        # of 2 to generate the IV. In either case, we can't return the IV
-        # without knowing the user's password.
-        if encryption_algo.find('.') == -1:
-            return None
-
-        raise ValueError('Unrecognized encryption algorithm "%s", can not determine initialization vector' % encryption_algo)
-
-
-class Pbmac1Params(Sequence):
-    _fields = [
-        ('key_derivation_func', KdfAlgorithm),
-        ('message_auth_scheme', HmacAlgorithm),
-    ]
-
-
-class Pkcs5MacId(ObjectIdentifier):
-    _map = {
-        '1.2.840.113549.1.5.14': 'pbmac1',
-    }
-
-
-class Pkcs5MacAlgorithm(Sequence):
-    _fields = [
-        ('algorithm', Pkcs5MacId),
-        ('parameters', Any),
-    ]
-
-    _oid_pair = ('algorithm', 'parameters')
-    _oid_specs = {
-        'pbmac1': Pbmac1Params,
-    }
diff --git a/docs/readme.md b/docs/readme.md
index b987385..7dc216f 100644
--- a/docs/readme.md
+++ b/docs/readme.md
@@ -14,7 +14,6 @@
  - [X509 certificates](../asn1crypto/x509.py), `asn1crypto.x509`
  - [Certificate revocation lists (CRLs)](../asn1crypto/crl.py), `asn1crypto.crl`
  - [Online certificate status protocol (OCSP)](../asn1crypto/ocsp.py), `asn1crypto.ocsp`
- - [Private key encryption (PKCS#5)](../asn1crypto/pkcs5.py), `asn1crypto.pkcs5`
  - [Private key/certificate containers (PKCS#12)](../asn1crypto/pkcs12.py), `asn1crypto.pkcs12`
  - [Cryptographic message syntax (CMS, PKCS#7)](../asn1crypto/cms.py), `asn1crypto.cms`
  - [Time stamp protocol (TSP)](../asn1crypto/tsp.py), `asn1crypto.tsp`
diff --git a/lint.py b/lint.py
index bead1e8..557f0b9 100644
--- a/lint.py
+++ b/lint.py
@@ -21,7 +21,6 @@
     'ocsp.py',
     'pdf.py',
     'pkcs12.py',
-    'pkcs5.py',
     'teletex_codec.py',
     'tsa.py',
     'x509.py',
diff --git a/readme.md b/readme.md
index c9336e9..2c9df2b 100644
--- a/readme.md
+++ b/readme.md
@@ -14,7 +14,7 @@
 | PKCS#1 v2.1 (RSA keys) | [`asn1crypto.keys`](asn1crypto/keys.py)     | [RFC3447](https://tools.ietf.org/html/rfc3447)                                                                         |
 | DSA keys               | [`asn1crypto.keys`](asn1crypto/keys.py)     | [RFC3279](https://tools.ietf.org/html/rfc3279)                                                                         |
 | Elliptic curve keys    | [`asn1crypto.keys`](asn1crypto/keys.py)     | [SECG SEC1 V2](http://www.secg.org/sec1-v2.pdf)                                                                        |
-| PKCS#5 v2.1            | [`asn1crypto.pkcs5`](asn1crypto/pkcs5.py)   | [PKCS#5 v2.1](http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf) |
+| PKCS#5 v2.1            | [`asn1crypto.algos`](asn1crypto/algos.py)   | [PKCS#5 v2.1](http://www.emc.com/collateral/white-papers/h11302-pkcs5v2-1-password-based-cryptography-standard-wp.pdf) |
 | CMS (and PKCS#7)       | [`asn1crypto.cms`](asn1crypto/cms.py)       | [RFC5652](https://tools.ietf.org/html/rfc5652), [RFC2315](https://tools.ietf.org/html/rfc2315)                         |
 | TSP                    | [`asn1crypto.tsp`](asn1crypto/tsp.py)       | [RFC3161](https://tools.ietf.org/html/rfc3161)                                                                         |
 | PDF signatures         | [`asn1crypto.pdf`](asn1crypto/pdf.py)       | [PDF 1.7](http://wwwimages.adobe.com/content/dam/Adobe/en/devnet/pdf/pdfs/PDF32000_2008.pdf)                           |
@@ -54,7 +54,6 @@
  - [X509 certificates](asn1crypto/x509.py), `asn1crypto.x509`
  - [Certificate revocation lists (CRLs)](asn1crypto/crl.py), `asn1crypto.crl`
  - [Online certificate status protocol (OCSP)](asn1crypto/ocsp.py), `asn1crypto.ocsp`
- - [Private key encryption (PKCS#5)](asn1crypto/pkcs5.py), `asn1crypto.pkcs5`
  - [Private key/certificate containers (PKCS#12)](asn1crypto/pkcs12.py), `asn1crypto.pkcs12`
  - [Cryptographic message syntax (CMS, PKCS#7)](asn1crypto/cms.py), `asn1crypto.cms`
  - [Time stamp protocol (TSP)](asn1crypto/tsp.py), `asn1crypto.tsp`