def test1(self): padded = pad(b(""), 4) self.assertTrue(padded == uh(b("04040404"))) padded = pad(b(""), 4, 'pkcs7') self.assertTrue(padded == uh(b("04040404"))) back = unpad(padded, 4) self.assertTrue(back == b(""))
def encode(data, marker, passphrase=None, randfunc=None): """Encode a piece of binary data into PEM format. Args: data (byte string): The piece of binary data to encode. marker (string): The marker for the PEM block (e.g. "PUBLIC KEY"). Note that there is no official master list for all allowed markers. Still, you can refer to the OpenSSL_ source code. passphrase (byte string): If given, the PEM block will be encrypted. The key is derived from the passphrase. randfunc (callable): Random number generation function; it accepts an integer N and returns a byte string of random data, N bytes long. If not given, a new one is instantiated. Returns: The PEM block, as a string. .. _OpenSSL: https://github.com/openssl/openssl/blob/master/include/openssl/pem.h """ if randfunc is None: randfunc = get_random_bytes out = "-----BEGIN %s-----\n" % marker if passphrase: # We only support 3DES for encryption salt = randfunc(8) key = PBKDF1(passphrase, salt, 16, 1, MD5) key += PBKDF1(key + passphrase, salt, 8, 1, MD5) objenc = DES3.new(key, DES3.MODE_CBC, salt) out += "Proc-Type: 4,ENCRYPTED\nDEK-Info: DES-EDE3-CBC,%s\n\n" %\ tostr(hexlify(salt).upper()) # Encrypt with PKCS#7 padding data = objenc.encrypt(pad(data, objenc.block_size)) elif passphrase is not None: raise ValueError("Empty password") # Each BASE64 line can take up to 64 characters (=48 bytes of data) # b2a_base64 adds a new line character! chunks = [ tostr(b2a_base64(data[i:i + 48])) for i in range(0, len(data), 48) ] out += "".join(chunks) out += "-----END %s-----" % marker return out
def encrypt(data, passphrase, protection, prot_params=None, randfunc=None): """Encrypt a piece of data using a passphrase and *PBES2*. :Parameters: data : byte string The piece of data to encrypt. passphrase : byte string The passphrase to use for encrypting the data. protection : string The identifier of the encryption algorithm to use. The default value is '``PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC``'. prot_params : dictionary Parameters of the protection algorithm. +------------------+-----------------------------------------------+ | Key | Description | +==================+===============================================+ | iteration_count | The KDF algorithm is repeated several times to| | | slow down brute force attacks on passwords | | | (called *N* or CPU/memory cost in scrypt). | | | | | | The default value for PBKDF2 is 1 000. | | | The default value for scrypt is 16 384. | +------------------+-----------------------------------------------+ | salt_size | Salt is used to thwart dictionary and rainbow | | | attacks on passwords. The default value is 8 | | | bytes. | +------------------+-----------------------------------------------+ | block_size | *(scrypt only)* Memory-cost (r). The default | | | value is 8. | +------------------+-----------------------------------------------+ | parallelization | *(scrypt only)* CPU-cost (p). The default | | | value is 1. | +------------------+-----------------------------------------------+ randfunc : callable Random number generation function; it should accept a single integer N and return a string of random data, N bytes long. If not specified, a new RNG will be instantiated from ``Crypto.Random``. :Returns: The encrypted data, as a binary string. """ if prot_params is None: prot_params = {} if randfunc is None: randfunc = Random.new().read if protection == 'PBKDF2WithHMAC-SHA1AndDES-EDE3-CBC': key_size = 24 module = DES3 cipher_mode = DES3.MODE_CBC enc_oid = "1.2.840.113549.3.7" elif protection in ('PBKDF2WithHMAC-SHA1AndAES128-CBC', 'scryptAndAES128-CBC'): key_size = 16 module = AES cipher_mode = AES.MODE_CBC enc_oid = "2.16.840.1.101.3.4.1.2" elif protection in ('PBKDF2WithHMAC-SHA1AndAES192-CBC', 'scryptAndAES192-CBC'): key_size = 24 module = AES cipher_mode = AES.MODE_CBC enc_oid = "2.16.840.1.101.3.4.1.22" elif protection in ('PBKDF2WithHMAC-SHA1AndAES256-CBC', 'scryptAndAES256-CBC'): key_size = 32 module = AES cipher_mode = AES.MODE_CBC enc_oid = "2.16.840.1.101.3.4.1.42" else: raise ValueError("Unknown PBES2 mode") # Get random data iv = randfunc(module.block_size) salt = randfunc(prot_params.get("salt_size", 8)) # Derive key from password if protection.startswith('PBKDF2'): count = prot_params.get("iteration_count", 1000) key = PBKDF2(passphrase, salt, key_size, count) kdf_info = DerSequence([ DerObjectId("1.2.840.113549.1.5.12"), # PBKDF2 DerSequence([DerOctetString(salt), DerInteger(count)]) ]) else: # It must be scrypt count = prot_params.get("iteration_count", 16384) scrypt_r = prot_params.get('block_size', 8) scrypt_p = prot_params.get('parallelization', 1) key = scrypt(passphrase, salt, key_size, count, scrypt_r, scrypt_p) kdf_info = DerSequence([ DerObjectId("1.3.6.1.4.1.11591.4.11"), # scrypt DerSequence([ DerOctetString(salt), DerInteger(count), DerInteger(scrypt_r), DerInteger(scrypt_p) ]) ]) # Create cipher and use it cipher = module.new(key, cipher_mode, iv) encrypted_data = cipher.encrypt(pad(data, cipher.block_size)) enc_info = DerSequence([DerObjectId(enc_oid), DerOctetString(iv)]) # Result enc_private_key_info = DerSequence([ # encryptionAlgorithm DerSequence([ DerObjectId("1.2.840.113549.1.5.13"), # PBES2 DerSequence([kdf_info, enc_info]), ]), DerOctetString(encrypted_data) ]) return enc_private_key_info.encode()
def encrypt(self, raw): raw = pad(raw) iv = Random.new().read(AES.block_size) cipher = AES.new(self.key, AES.MODE_CBC, iv) return base64.b64encode(iv + cipher.encrypt(raw))
def test3(self): padded = pad(uh(b("123456")), 4, 'x923') self.assertTrue(padded == uh(b("12345601"))) back = unpad(padded, 4, 'x923') self.assertTrue(back == uh(b("123456")))
def test2(self): padded = pad(uh(b("12345678")), 4, 'x923') self.assertTrue(padded == uh(b("1234567800000004"))) back = unpad(padded, 4, 'x923') self.assertTrue(back == uh(b("12345678")))
def test1(self): padded = pad(b(""), 4, 'x923') self.assertTrue(padded == uh(b("00000004"))) back = unpad(padded, 4, 'x923') self.assertTrue(back == b(""))
def test4(self): padded = pad(uh(b("1234567890")), 4) self.assertTrue(padded == uh(b("1234567890030303"))) back = unpad(padded, 4) self.assertTrue(back == uh(b("1234567890")))
def test4(self): padded = pad(uh(b("1234567890")), 4, 'iso7816') self.assertTrue(padded == uh(b("1234567890800000"))) back = unpad(padded, 4, 'iso7816') self.assertTrue(back == uh(b("1234567890")))
def test3(self): padded = pad(uh(b("123456")), 4, 'iso7816') self.assertTrue(padded == uh(b("12345680"))) #import pdb; pdb.set_trace() back = unpad(padded, 4, 'iso7816') self.assertTrue(back == uh(b("123456")))
def test1(self): padded = pad(b(""), 4, 'iso7816') self.assertTrue(padded == uh(b("80000000"))) back = unpad(padded, 4, 'iso7816') self.assertTrue(back == b(""))