def seed(self, state, version=2, entropy=None): """Initialize internal state from hashable object. None or no argument seeds from current time or from an operating system specific randomness source if available. For version 2 (the default), all of the bits are used if *a *is a str, bytes, or bytearray. For version 1, the hash() of *a* is used instead. If *a* is an int, all bits are used. """ if state is None: if not api.RAND_status(): while not api.RAND_status(): data = api.new('unsigned char[]', os.urandom(256)) api.RAND_seed(data, len(data)) return elif isinstance(state, (str, bytes, bytearray)): if version > 1: if isinstance(state, str): state = state.encode() data = api.new('unsigned char[]', state) else: state = hash(state) data = self._int_to_ubyte(state) else: data = self._int_to_ubyte(state) entropy = entropy if entropy is not None else 8 * len(data) api.RAND_add(data, len(data), entropy)
def set_cipher(self, enc): cipher = api.EVP_get_cipherbyname(self.algorithm) key = api.new('unsigned char[]', self.hexstr_to_numbers(self.key)) iv = api.NULL if self.iv is not None: iv = api.new('unsigned char[]', self.hexstr_to_numbers(self.iv)) api.BIO_set_cipher(self.filter, cipher, key, iv, 1 if enc else 0)
def __init__(self, key, msg=None, digestmod=None): """Create a new HMAC object. key: key for the keyed hash object. msg: Initial input for the hash, if provided. digestmod: A message digest name. *OR* A module supporting PEP 247. *OR* A hashlib constructor returning a new hash object. If module or hashlib constuctor is passed as digestmod the '__name__' and 'args' attributes are searched to find a message digest name. If not provied the digestmod defaults to 'md5'. Note: key and msg must be a bytes objects. """ if digestmod is None: self._md = api.EVP_md5() else: self._md = self._get_md(digestmod) ctx = api.new('HMAC_CTX*') self._key = api.new('char[]', key) api.HMAC_Init_ex(ctx, api.cast('void*', self._key), len(key), self._md, api.NULL) cleanup = lambda _: api.HMAC_CTX_cleanup(ctx) self._weakref = weakref.ref(self, cleanup) self._ctx = ctx if msg is not None: self.update(msg)
def initialise(self, key, ivector): """Initialise this cipher's state with a key and optional ivector The key must be a byte string of the same length as this cipher's key_len property. If the ivector is required, it must also be a byte string of ivector_len length. If not required it may be an empty string or None. """ if self._ctx == api.NULL: raise ValueError("Cipher object failed to be initialised") if len(key) != self.key_len: msg = "Key must be {0} bytes. Received {1}".format( self.key_len, len(key)) raise ValueError(msg) c_key = api.new('char[]', key) if bool(key) else api.NULL if (len(ivector) if ivector is not None else 0) != self.ivector_len: msg = "IVector must be {0} bytes. Received{1}".format( self.ivector_len, len(ivector)) raise ValueError(msg) c_iv = api.new('char[]', ivector) if bool(ivector) else api.NULL if not api.EVP_CipherInit_ex(self._ctx, api.NULL, api.NULL, c_key, c_iv, -1): raise ValueError("Unable to initialise cipher") if self.digest is not None: self._hmac = hmac.HMAC(key, digestmod=self.digest) self._initialised = True
def test_long(self): data = api.new('char[]', self.data_long) buf = api.new('unsigned char[]', api.EVP_MAX_MD_SIZE) size = api.new('unsigned int*') api.EVP_DigestUpdate(self.ctx, api.cast('void*', data), len(self.data_long)) api.EVP_DigestFinal_ex(self.ctx, buf, size) hash_value = b''.join('{0:02x}'.format(v).encode() for v in islice(buf, size[0])) self.assertEqual(hash_value, self.hash_long)
def test_filter_write(self): self.set_cipher(enc=True) plaintext = api.new('unsigned char[]', self.hexstr_to_numbers(self.plaintext)) ciphertext = api.new('unsigned char[]', self.hexstr_to_numbers(self.ciphertext)) output = api.new('unsigned char[]', len(ciphertext)) api.BIO_write(self.bio, plaintext, len(plaintext)) api.BIO_flush(self.bio) api.BIO_read(self.sink, output, len(output)) self.assertEqual(api.buffer(ciphertext)[:], api.buffer(output)[:])
def set_mode(self, enc): mode = 1 if enc else 0 key = api.new('unsigned char[]', self.hexstr_to_numbers(self.key)) iv = api.NULL if self.iv is not None: iv = api.new('unsigned char[]', self.hexstr_to_numbers(self.iv)) api.EVP_CipherInit_ex(self.ctx, api.NULL, api.NULL, key, iv, mode) if not enc: api.EVP_CIPHER_CTX_set_padding(self.ctx, 0)
def test_filter_read(self): self.set_cipher(enc=False) plaintext = api.new('unsigned char[]', self.hexstr_to_numbers(self.plaintext)) ciphertext = api.new('unsigned char[]', self.hexstr_to_numbers(self.ciphertext)) output = api.new('unsigned char[]', len(plaintext)) api.BIO_write(self.bio, ciphertext, len(ciphertext)) api.BIO_write(self.bio, api.new('unsigned char[]', [0] * 1), 1) api.BIO_flush(self.bio) api.BIO_read(self.sink, output, len(output)) self.assertEqual(bytes(api.buffer(plaintext)), bytes(api.buffer(output)))
def test_quick(self): buff = api.new('unsigned char[]', api.EVP_MAX_MD_SIZE) key = api.new('char[]', self.key) data = api.new('char[]', self.data) size = api.new('unsigned int*') api.HMAC(self.md, api.cast('void*', key), len(self.key), api.cast('void*', data), len(self.data), api.cast('void*', buff), size) self.assertEqual(self.digest, api.buffer(buff, size[0])[:])
def test_0001_iteration(self): key = b'\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6' password = api.new('char[]', b'password') passlen = 8 salt = api.new('char[]', b'salt') saltlen = 4 iterations = 1 keylen = 20 out = api.new('unsigned char[]', keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, api.buffer(out)[:])
def pbkdf2(password, length, salt=None, iterations=1000): "Derive a shared secret including encryption key from password" if salt is None: salt = random.getrandbytes(8) c_password = api.new('char[]', password) c_salt = api.new('char[]', salt) c_key = api.new('unsigned char[]', length) api.PKCS5_PBKDF2_HMAC_SHA1(c_password, len(password), c_salt, len(salt), iterations, length, c_key) secret = Secret(bytes(api.buffer(c_key)), salt, iterations) return secret
def test_single_decrypt(self): self.set_mode(enc=False) plaintext = api.new('unsigned char[]', self.hexstr_to_numbers(self.plaintext)) ciphertext = api.new('unsigned char[]', self.hexstr_to_numbers(self.ciphertext)) output = api.new('unsigned char[]', len(ciphertext) + api.EVP_CIPHER_CTX_block_size(self.ctx) - 1) outlen = api.new('int*') api.EVP_CipherUpdate(self.ctx, output, outlen, ciphertext, len(ciphertext)) self.assertEqual(api.buffer(plaintext), api.buffer(output, outlen[0])) rval = api.EVP_CipherFinal_ex(self.ctx, output, outlen) self.assertEqual(rval, 1)
def pbkdf2(password, length, salt=None, iterations=1000): "Derive a shared secret including encryption key from password" if salt is None: salt = random.getrandbytes(8) c_password = api.new('char[]', password) c_salt = api.new('char[]', salt) c_key = api.new('unsigned char[]', length) api.PKCS5_PBKDF2_HMAC_SHA1(c_password, len(password), c_salt, len(salt), iterations, length, c_key) secret = Secret(api.buffer(c_key)[:], salt, iterations) return secret
def test_0002_iteration(self): key = b'\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57' password = api.new('char[]', b'password') passlen = 8 salt = api.new('char[]', b'salt') saltlen = 4 iterations = 2 keylen = 20 out = api.new('unsigned char[]', keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, api.buffer(out)[:])
def test_4096_iteration(self): key = b'\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1' password = api.new('char[]', b'password') passlen = 8 salt = api.new('char[]', b'salt') saltlen = 4 iterations = 4096 keylen = 20 out = api.new('unsigned char[]', keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, api.buffer(out)[:])
def test_4096_3_iteration(self): key = b"\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3" password = api.new("char[]", b"pass\0word") passlen = 9 salt = api.new("char[]", b"sa\0lt") saltlen = 5 iterations = 4096 keylen = 16 out = api.new("unsigned char[]", keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, bytes(api.buffer(out)))
def test_0002_iteration(self): key = b"\xea\x6c\x01\x4d\xc7\x2d\x6f\x8c\xcd\x1e\xd9\x2a\xce\x1d\x41\xf0\xd8\xde\x89\x57" password = api.new("char[]", b"password") passlen = 8 salt = api.new("char[]", b"salt") saltlen = 4 iterations = 2 keylen = 20 out = api.new("unsigned char[]", keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, bytes(api.buffer(out)))
def test_4096_iteration(self): key = b"\x4b\x00\x79\x01\xb7\x65\x48\x9a\xbe\xad\x49\xd9\x26\xf7\x21\xd0\x65\xa4\x29\xc1" password = api.new("char[]", b"password") passlen = 8 salt = api.new("char[]", b"salt") saltlen = 4 iterations = 4096 keylen = 20 out = api.new("unsigned char[]", keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, bytes(api.buffer(out)))
def test_load_strings(self): lib = api.ERR_get_next_error_library() func = 1 reason = 1 code = api.ERR_PACK(lib, func, reason) array = api.new('ERR_STRING_DATA[2]') array[0].error = code array[0].string = api.new('char[]', b"MY ERROR") array[0].error = 0 array[0].string = api.NULL api.ERR_load_strings(lib, array)
def test_0001_iteration(self): key = b"\x0c\x60\xc8\x0f\x96\x1f\x0e\x71\xf3\xa9\xb5\x24\xaf\x60\x12\x06\x2f\xe0\x37\xa6" password = api.new("char[]", b"password") passlen = 8 salt = api.new("char[]", b"salt") saltlen = 4 iterations = 1 keylen = 20 out = api.new("unsigned char[]", keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, bytes(api.buffer(out)))
def test_4096_2_iteration(self): key = b'\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38' password = api.new('char[]', b'passwordPASSWORDpassword') passlen = 24 salt = api.new('char[]', b'saltSALTsaltSALTsaltSALTsaltSALTsalt') saltlen = 36 iterations = 4096 keylen = 25 out = api.new('unsigned char[]', keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, api.buffer(out)[:])
def test_4096_3_iteration(self): key = b'\x56\xfa\x6a\xa7\x55\x48\x09\x9d\xcc\x37\xd7\xf0\x34\x25\xe0\xc3' password = api.new('char[]', b'pass\0word') passlen = 9 salt = api.new('char[]', b'sa\0lt') saltlen = 5 iterations = 4096 keylen = 16 out = api.new('unsigned char[]', keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, api.buffer(out)[:])
def test_4096_2_iteration(self): key = b"\x3d\x2e\xec\x4f\xe4\x1c\x84\x9b\x80\xc8\xd8\x36\x62\xc0\xe4\x4a\x8b\x29\x1a\x96\x4c\xf2\xf0\x70\x38" password = api.new("char[]", b"passwordPASSWORDpassword") passlen = 24 salt = api.new("char[]", b"saltSALTsaltSALTsaltSALTsaltSALTsalt") saltlen = 36 iterations = 4096 keylen = 25 out = api.new("unsigned char[]", keylen) api.PKCS5_PBKDF2_HMAC_SHA1(password, passlen, salt, saltlen, iterations, keylen, out) self.assertEqual(key, bytes(api.buffer(out)))
def test_bio_read_write(self): try: method = api.BIO_s_mem() mem = api.BIO_new(method) data = api.new('char[]', b"HELLO WORLD") api.BIO_write(mem, data, 11) buf = api.new('char[]', 5) api.BIO_read(mem, buf, len(buf)) self.assertEqual(api.string(buf), b"HELLO") api.BIO_gets(mem, buf, len(buf)) self.assertEqual(api.string(buf), b" WOR") finally: api.BIO_free(mem)
def test_long(self): buff = api.new('unsigned char[]', api.EVP_MAX_MD_SIZE) key = api.new('char[]', self.key) data = api.new('char[]', self.data) size = api.new('unsigned int*') ctx = api.new('HMAC_CTX*') api.HMAC_CTX_init(ctx) api.HMAC_Init_ex(ctx, api.cast('void*', key), len(self.key), self.md, api.NULL) api.HMAC_Update(ctx, api.cast('void*', data), len(self.data)) api.HMAC_Final(ctx, buff, size) api.HMAC_CTX_cleanup(ctx) self.assertEqual(self.digest, api.buffer(buff, size[0])[:])
def test_multiple_updates(self): buff = api.new('unsigned char[]', api.EVP_MAX_MD_SIZE) key = api.new('char[]', self.key) data = api.new('char[]', self.data) size = api.new('unsigned int*') ctx = api.new('HMAC_CTX*') api.HMAC_CTX_init(ctx) api.HMAC_Init_ex(ctx, api.cast('void*', key), len(self.key), self.md, api.NULL) for pos in range(len(self.data)): api.HMAC_Update(ctx, api.cast('void*', data+pos), 1) api.HMAC_Final(ctx, buff, size) api.HMAC_CTX_cleanup(ctx) self.assertEqual(self.digest, bytes(api.buffer(buff, size[0])))
def _digest(self): "Return iterator for digest byte data." buff = api.new('unsigned char[]', api.EVP_MAX_MD_SIZE) size = api.new('unsigned int*') context = api.new('EVP_MD_CTX*') if not api.EVP_DigestInit_ex(context, self._md, api.NULL): raise DigestError('Failed to initialise message digest') if not api.EVP_MD_CTX_copy_ex(context, self._context): raise DigestError('Failed to copy message digest') if not api.EVP_DigestFinal_ex(context, buff, size): raise DigestError('Failed to retrieve digest value') if not api.EVP_MD_CTX_cleanup(context): raise DigestError('Failed to cleanup message digest') return buff, size[0]
def test_multiple_encrypt(self): self.set_mode(enc=True) numbers = self.hexstr_to_numbers(self.plaintext) ciphertext = api.new('unsigned char[]', self.hexstr_to_numbers(self.ciphertext)) outbuf = api.new('unsigned char[]', api.EVP_CIPHER_CTX_block_size(self.ctx)) outlen = api.new('int*') output = b'' for num in numbers: plaintext = api.new('unsigned char[]', [num]) api.EVP_CipherUpdate(self.ctx, outbuf, outlen, plaintext, len(plaintext)) if outlen[0] > 0: output += bytes(api.buffer(outbuf, outlen[0])) self.assertEqual(bytes(api.buffer(ciphertext)), output) rval = api.EVP_CipherFinal_ex(self.ctx, outbuf, outlen) self.assertEqual(rval, 1)
def __init__(self, filename, mode='r'): if mode not in self.MODES: msg = "mode string must begin with one of " msg += " ".join("'{0}'".format(m) for m in sorted(self.MODES)) msg += " not '{0}'".format(mode) raise ValueError(msg) self._filename = filename.encode() self._mode = mode.encode() mode = api.new('char[]', self._mode) filename = api.new('char[]', self._filename) bio = api.BIO_new_file(filename, mode) if api.cast('void*', bio) == api.NULL: messages = err.log_errors() raise IOError(messages[0]) super(BIOFile, self).__init__(bio)
def update(self, msg): """Update this hashing object with the string msg. """ if self._ctx is None: raise ValueError('HMAC already closed') data = api.new('char[]', msg) api.HMAC_Update(self._ctx, api.cast('void*', data), len(msg))
def __init__(self, encrypt=True, algorithm=b'AES-128-CBC', digest=b'SHA1'): self._algorithm = algorithm self._digest = digest # initialise attributes to empty self._encrypting = bool(encrypt) self._initialised = False self._bio = api.NULL self._cipher = api.NULL self._ctx = api.NULL self._sink = api.NULL self._hmac = None self._weakrefs = [] # create cipher object from cipher name cipher = api.EVP_get_cipherbyname(algorithm) if cipher == api.NULL: msg = "Unknown cipher name '{0}'".format(algorithm) raise ValueError(msg) self._cipher = cipher # allocate cipher context pointer self._ctxptr = api.new('EVP_CIPHER_CTX*[]', 1) # create bio chain (cipher, buffer, mem) self._sink = api.BIO_new(api.BIO_s_mem()) bio = api.BIO_push(api.BIO_new(api.BIO_f_buffer()), self._sink) bio = api.BIO_push(api.BIO_new(api.BIO_f_cipher()), bio) cleanup = lambda _: api.BIO_free_all(bio) self._weakrefs.append(weakref.ref(self, cleanup)) self._bio = bio # initialise cipher context api.BIO_get_cipher_ctx(bio, self._ctxptr) self._ctx = self._ctxptr[0] if not api.EVP_CipherInit_ex(self._ctx, cipher, api.NULL, api.NULL, api.NULL, 1 if encrypt else 0): raise ValueError("Unable to initialise cipher")
def setUp(self): self.buff = api.new('char[]', self.DATA) self.filter = api.BIO_new(api.BIO_f_null()) self.sink = api.BIO_new_mem_buf(self.buff, len(self.DATA)) self.bio = api.BIO_push(self.filter, self.sink) self.fileobj = io.BIOChain(self.bio)
def getrandbytes(self, length): "getrandbytes(k) -> 's'. Generate a byte string with k random bytes." buff = api.new('unsigned char[]', length) self._rand_bytes(buff, length) return api.buffer(buff)[:]