X-Git-Url: https://wagner.pp.ru/gitweb/?a=blobdiff_plain;f=ctypescrypto%2Fcipher.py;h=d4035c1a34a8a5ec909578491b19a7cc5670138b;hb=287e8a5b1d7f5a8129619f733adbfc8b2de3e4c7;hp=63373d6bf404d0cd339c8391339db6fa5a6bf764;hpb=954b6dc9e3312f8d8b49f20f8466e6d2a8342f35;p=oss%2Fctypescrypto.git diff --git a/ctypescrypto/cipher.py b/ctypescrypto/cipher.py index 63373d6..d4035c1 100644 --- a/ctypescrypto/cipher.py +++ b/ctypescrypto/cipher.py @@ -2,108 +2,122 @@ access to symmetric ciphers from libcrypto """ -from ctypes import create_string_buffer,c_char_p,c_void_p,c_int,c_long,byref,POINTER +from ctypes import create_string_buffer, c_char_p, c_void_p, c_int +from ctypes import byref, POINTER from ctypescrypto import libcrypto from ctypescrypto.exception import LibCryptoError from ctypescrypto.oid import Oid CIPHER_ALGORITHMS = ("DES", "DES-EDE3", "BF", "AES-128", "AES-192", "AES-256") -CIPHER_MODES = ("STREAM","ECB","CBC", "CFB", "OFB", "CTR","GCM") +CIPHER_MODES = ("STREAM", "ECB", "CBC", "CFB", "OFB", "CTR", "GCM") # -__all__ = ['CipherError','new','Cipher','CipherType'] +__all__ = ['CipherError', 'new', 'Cipher', 'CipherType'] class CipherError(LibCryptoError): + """ + Exception raise when OpenSSL function returns error + """ pass -def new(algname,key,encrypt=True,iv=None): +def new(algname, key, encrypt=True, iv=None): """ - Returns new cipher object ready to encrypt-decrypt data + Returns new cipher object ready to encrypt-decrypt data - @param algname - string algorithm name like in opemssl command - line - @param key - binary string representing ciher key - @param encrypt - if True (default) cipher would be initialized + @param algname - string algorithm name like in opemssl command + line + @param key - binary string representing ciher key + @param encrypt - if True (default) cipher would be initialized for encryption, otherwise - for decrypton - @param iv - initialization vector + @param iv - initialization vector """ - ct=CipherType(algname) - return Cipher(ct,key,iv,encrypt) + ciph_type = CipherType(algname) + return Cipher(ciph_type, key, iv, encrypt) -class CipherType: +class CipherType(object): """ - Describes cihper algorihm. Can be used to produce cipher - instance and to get various information about cihper + Describes cihper algorihm. Can be used to produce cipher + instance and to get various information about cihper """ def __init__(self, cipher_name): """ - Constructs cipher algortihm using textual name as in openssl - command line + Constructs cipher algortihm using textual name as in openssl + command line """ self.cipher = libcrypto.EVP_get_cipherbyname(cipher_name) if self.cipher is None: raise CipherError("Unknown cipher: %s" % cipher_name) def __del__(self): + """ + It is constant object with do-nothing del + """ pass + def block_size(self): """ - Returns block size of the cipher + Returns block size of the cipher """ return libcrypto.EVP_CIPHER_block_size(self.cipher) + def key_length(self): """ - Returns key length of the cipher + Returns key length of the cipher """ return libcrypto.EVP_CIPHER_key_length(self.cipher) + def iv_length(self): """ - Returns initialization vector length of the cipher + Returns initialization vector length of the cipher """ return libcrypto.EVP_CIPHER_iv_length(self.cipher) + def flags(self): """ - Return cipher flags. Low three bits of the flags encode - cipher mode (see mode). Higher bits is combinatuon of - EVP_CIPH* constants defined in the + Return cipher flags. Low three bits of the flags encode + cipher mode (see mode). Higher bits is combinatuon of + EVP_CIPH* constants defined in the """ return libcrypto.EVP_CIPHER_flags(self.cipher) + def mode(self): """ - Returns cipher mode as string constant like CBC, OFB etc. + Returns cipher mode as string constant like CBC, OFB etc. """ return CIPHER_MODES[self.flags() & 0x7] + def algo(self): """ - Return cipher's algorithm name, derived from OID + Return cipher's algorithm name, derived from OID """ - return self.oid().shortname() + return self.oid().shortname() + def oid(self): """ - Returns ASN.1 object identifier of the cipher as - ctypescrypto.oid.Oid object + Returns ASN.1 object identifier of the cipher as + ctypescrypto.oid.Oid object """ return Oid(libcrypto.EVP_CIPHER_nid(self.cipher)) -class Cipher: +class Cipher(object): """ - Performs actual encrypton decryption - Note that object keeps some internal state. - To obtain full ciphertext (or plaintext during decihpering) - user should concatenate results of all calls of update with - result of finish + Performs actual encrypton decryption + Note that object keeps some internal state. + To obtain full ciphertext (or plaintext during decihpering) + user should concatenate results of all calls of update with + result of finish """ - def __init__(self, cipher_type, key, iv, encrypt=True): + def __init__(self, cipher_type, key, iv, encrypt=True): """ - Initializing cipher instance. + Initializing cipher instance. - @param cipher_type - CipherType object - @param key = binary string representing the key - @param iv - binary string representing initializtion vector - @param encrypt - if True(default) we ere encrypting. - Otherwise decrypting + @param cipher_type - CipherType object + @param key = binary string representing the key + @param iv - binary string representing initializtion vector + @param encrypt - if True(default) we ere encrypting. + Otherwise decrypting """ self._clean_ctx() @@ -117,23 +131,31 @@ class Cipher: if self.ctx == 0: raise CipherError("Unable to create cipher context") self.encrypt = encrypt - enc=1 if encrypt else 0 + enc = 1 if encrypt else 0 if not iv is None and len(iv) != cipher_type.iv_length(): raise ValueError("Invalid IV length for this algorithm") - + if len(key) != cipher_type.key_length(): if (cipher_type.flags() & 8) != 0: # Variable key length cipher. - result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, None, None, c_int(enc)) - result = libcrypto.EVP_CIPHER_CTX_set_key_length(self.ctx,len(key)) + result = libcrypto.EVP_CipherInit_ex(self.ctx, + cipher_type.cipher, + None, None, None, + c_int(enc)) + result = libcrypto.EVP_CIPHER_CTX_set_key_length(self.ctx, + len(key)) if result == 0: self._clean_ctx() raise CipherError("Unable to set key length") - result = libcrypto.EVP_CipherInit_ex(self.ctx, None, None, key_ptr, iv_ptr, c_int(enc)) + result = libcrypto.EVP_CipherInit_ex(self.ctx, None, None, + key_ptr, iv_ptr, + c_int(enc)) else: raise ValueError("Invalid key length for this algorithm") else: - result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, key_ptr, iv_ptr, c_int(enc)) + result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, + None, key_ptr, iv_ptr, + c_int(enc)) if result == 0: self._clean_ctx() raise CipherError("Unable to initialize cipher") @@ -142,69 +164,74 @@ class Cipher: self.cipher_finalized = False def __del__(self): + """ + We define _clean_ctx() to do all the cleanup + """ self._clean_ctx() def padding(self, padding=True): """ - Sets padding mode of the cipher + Sets padding mode of the cipher """ - padding_flag=1 if padding else 0 + padding_flag = 1 if padding else 0 libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag) def update(self, data): """ - Performs actual encrypton/decrypion + Performs actual encrypton/decrypion - @param data - part of the plain text/ciphertext to process - @returns - part of ciphercext/plain text + @param data - part of the plain text/ciphertext to process + @returns - part of ciphercext/plain text - Passd chunk of text doeesn't need to contain full ciher - blocks. If neccessery, part of passed data would be kept - internally until next data would be received or finish - called + Passed chunk of text doesn't need to contain full ciher + blocks. If neccessery, part of passed data would be kept + internally until next data would be received or finish + called """ - if self.cipher_finalized : + if self.cipher_finalized: raise CipherError("No updates allowed") - if not isinstance(data,str): + if not isinstance(data, str): raise TypeError("A string is expected") if len(data) == 0: return "" - outbuf=create_string_buffer(self.block_size+len(data)) - outlen=c_int(0) - ret=libcrypto.EVP_CipherUpdate(self.ctx,outbuf,byref(outlen), - data,len(data)) - if ret <=0: + outbuf = create_string_buffer(self.block_size+len(data)) + outlen = c_int(0) + ret = libcrypto.EVP_CipherUpdate(self.ctx, outbuf, byref(outlen), + data, len(data)) + if ret <= 0: self._clean_ctx() - self.cipher_finalized=True - del self.ctx + self.cipher_finalized = True raise CipherError("problem processing data") - return outbuf.raw[:outlen.value] - + return outbuf.raw[:int(outlen.value)] + def finish(self): """ - Finalizes processing. If some data are kept in the internal - state, they would be processed and returned. + Finalizes processing. If some data are kept in the internal + state, they would be processed and returned. """ - if self.cipher_finalized : + if self.cipher_finalized: raise CipherError("Cipher operation is already completed") - outbuf=create_string_buffer(self.block_size) + outbuf = create_string_buffer(self.block_size) self.cipher_finalized = True - outlen=c_int(0) - result = libcrypto.EVP_CipherFinal_ex(self.ctx,outbuf , byref(outlen)) + outlen = c_int(0) + result = libcrypto.EVP_CipherFinal_ex(self.ctx, outbuf, byref(outlen)) if result == 0: self._clean_ctx() raise CipherError("Unable to finalize cipher") - if outlen.value>0: - return outbuf.raw[:outlen.value] + if outlen.value > 0: + return outbuf.raw[:int(outlen.value)] else: return "" - + def _clean_ctx(self): + """ + Cleans up cipher ctx and deallocates it + """ try: if self.ctx is not None: libcrypto.EVP_CIPHER_CTX_cleanup(self.ctx) libcrypto.EVP_CIPHER_CTX_free(self.ctx) - del(self.ctx) + del self.ctx except AttributeError: pass self.cipher_finalized = True @@ -213,18 +240,20 @@ class Cipher: # # Used C function block_size # -libcrypto.EVP_CIPHER_block_size.argtypes=(c_void_p,) -libcrypto.EVP_CIPHER_CTX_cleanup.argtypes=(c_void_p,) -libcrypto.EVP_CIPHER_CTX_free.argtypes=(c_void_p,) -libcrypto.EVP_CIPHER_CTX_new.restype=c_void_p -libcrypto.EVP_CIPHER_CTX_set_padding.argtypes=(c_void_p,c_int) -libcrypto.EVP_CipherFinal_ex.argtypes=(c_void_p,c_char_p,POINTER(c_int)) -libcrypto.EVP_CIPHER_flags.argtypes=(c_void_p,) -libcrypto.EVP_CipherInit_ex.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_char_p,c_int) -libcrypto.EVP_CIPHER_iv_length.argtypes=(c_void_p,) -libcrypto.EVP_CIPHER_key_length.argtypes=(c_void_p,) -libcrypto.EVP_CIPHER_nid.argtypes=(c_void_p,) -libcrypto.EVP_CipherUpdate.argtypes=(c_void_p,c_char_p,POINTER(c_int),c_char_p,c_int) -libcrypto.EVP_get_cipherbyname.restype=c_void_p -libcrypto.EVP_get_cipherbyname.argtypes=(c_char_p,) -libcrypto.EVP_CIPHER_CTX_set_key_length.argtypes=(c_void_p,c_int) +libcrypto.EVP_CIPHER_block_size.argtypes = (c_void_p, ) +libcrypto.EVP_CIPHER_CTX_cleanup.argtypes = (c_void_p, ) +libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p, ) +libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p +libcrypto.EVP_CIPHER_CTX_set_padding.argtypes = (c_void_p, c_int) +libcrypto.EVP_CipherFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_int)) +libcrypto.EVP_CIPHER_flags.argtypes = (c_void_p, ) +libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_void_p, c_char_p, + c_char_p, c_int) +libcrypto.EVP_CIPHER_iv_length.argtypes = (c_void_p, ) +libcrypto.EVP_CIPHER_key_length.argtypes = (c_void_p, ) +libcrypto.EVP_CIPHER_nid.argtypes = (c_void_p, ) +libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_char_p, POINTER(c_int), + c_char_p, c_int) +libcrypto.EVP_get_cipherbyname.restype = c_void_p +libcrypto.EVP_get_cipherbyname.argtypes = (c_char_p, ) +libcrypto.EVP_CIPHER_CTX_set_key_length.argtypes = (c_void_p, c_int)