2 access to symmetric ciphers from libcrypto
5 from ctypes import create_string_buffer, c_char_p, c_void_p, c_int
6 from ctypes import byref, POINTER
7 from ctypescrypto import libcrypto
8 from ctypescrypto.exception import LibCryptoError
9 from ctypescrypto.oid import Oid
11 CIPHER_ALGORITHMS = ("DES", "DES-EDE3", "BF", "AES-128", "AES-192", "AES-256")
12 CIPHER_MODES = ("STREAM", "ECB", "CBC", "CFB", "OFB", "CTR", "GCM")
16 __all__ = ['CipherError', 'new', 'Cipher', 'CipherType']
18 class CipherError(LibCryptoError):
20 Exception raise when OpenSSL function returns error
24 def new(algname, key, encrypt=True, iv=None):
26 Returns new cipher object ready to encrypt-decrypt data
28 @param algname - string algorithm name like in opemssl command
30 @param key - binary string representing ciher key
31 @param encrypt - if True (default) cipher would be initialized
32 for encryption, otherwise - for decrypton
33 @param iv - initialization vector
35 ciph_type = CipherType(algname)
36 return Cipher(ciph_type, key, iv, encrypt)
38 class CipherType(object):
40 Describes cihper algorihm. Can be used to produce cipher
41 instance and to get various information about cihper
44 def __init__(self, cipher_name):
46 Constructs cipher algortihm using textual name as in openssl
49 self.cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
50 if self.cipher is None:
51 raise CipherError("Unknown cipher: %s" % cipher_name)
55 It is constant object with do-nothing del
61 Returns block size of the cipher
63 return libcrypto.EVP_CIPHER_block_size(self.cipher)
67 Returns key length of the cipher
69 return libcrypto.EVP_CIPHER_key_length(self.cipher)
73 Returns initialization vector length of the cipher
75 return libcrypto.EVP_CIPHER_iv_length(self.cipher)
79 Return cipher flags. Low three bits of the flags encode
80 cipher mode (see mode). Higher bits is combinatuon of
81 EVP_CIPH* constants defined in the <openssl/evp.h>
83 return libcrypto.EVP_CIPHER_flags(self.cipher)
87 Returns cipher mode as string constant like CBC, OFB etc.
89 return CIPHER_MODES[self.flags() & 0x7]
93 Return cipher's algorithm name, derived from OID
95 return self.oid().shortname()
99 Returns ASN.1 object identifier of the cipher as
100 ctypescrypto.oid.Oid object
102 return Oid(libcrypto.EVP_CIPHER_nid(self.cipher))
104 class Cipher(object):
106 Performs actual encrypton decryption
107 Note that object keeps some internal state.
108 To obtain full ciphertext (or plaintext during decihpering)
109 user should concatenate results of all calls of update with
112 def __init__(self, cipher_type, key, iv, encrypt=True):
114 Initializing cipher instance.
116 @param cipher_type - CipherType object
117 @param key = binary string representing the key
118 @param iv - binary string representing initializtion vector
119 @param encrypt - if True(default) we ere encrypting.
124 # Check key and iv length
126 raise ValueError("No key specified")
128 key_ptr = c_char_p(key)
129 iv_ptr = c_char_p(iv)
130 self.ctx = libcrypto.EVP_CIPHER_CTX_new()
132 raise CipherError("Unable to create cipher context")
133 self.encrypt = encrypt
134 enc = 1 if encrypt else 0
135 if not iv is None and len(iv) != cipher_type.iv_length():
136 raise ValueError("Invalid IV length for this algorithm")
138 if len(key) != cipher_type.key_length():
139 if (cipher_type.flags() & 8) != 0:
140 # Variable key length cipher.
141 result = libcrypto.EVP_CipherInit_ex(self.ctx,
145 result = libcrypto.EVP_CIPHER_CTX_set_key_length(self.ctx,
149 raise CipherError("Unable to set key length")
150 result = libcrypto.EVP_CipherInit_ex(self.ctx, None, None,
154 raise ValueError("Invalid key length for this algorithm")
156 result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher,
157 None, key_ptr, iv_ptr,
161 raise CipherError("Unable to initialize cipher")
162 self.cipher_type = cipher_type
163 self.block_size = self.cipher_type.block_size()
164 self.cipher_finalized = False
168 We define _clean_ctx() to do all the cleanup
172 def padding(self, padding=True):
174 Sets padding mode of the cipher
176 padding_flag = 1 if padding else 0
177 libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag)
179 def update(self, data):
181 Performs actual encrypton/decrypion
183 @param data - part of the plain text/ciphertext to process
184 @returns - part of ciphercext/plain text
186 Passed chunk of text doesn't need to contain full ciher
187 blocks. If neccessery, part of passed data would be kept
188 internally until next data would be received or finish
191 if self.cipher_finalized:
192 raise CipherError("No updates allowed")
193 if not isinstance(data, str):
194 raise TypeError("A string is expected")
197 outbuf = create_string_buffer(self.block_size+len(data))
199 ret = libcrypto.EVP_CipherUpdate(self.ctx, outbuf, byref(outlen),
203 self.cipher_finalized = True
204 raise CipherError("problem processing data")
205 return outbuf.raw[:int(outlen.value)]
209 Finalizes processing. If some data are kept in the internal
210 state, they would be processed and returned.
212 if self.cipher_finalized:
213 raise CipherError("Cipher operation is already completed")
214 outbuf = create_string_buffer(self.block_size)
215 self.cipher_finalized = True
217 result = libcrypto.EVP_CipherFinal_ex(self.ctx, outbuf, byref(outlen))
220 raise CipherError("Unable to finalize cipher")
222 return outbuf.raw[:int(outlen.value)]
226 def _clean_ctx(self):
228 Cleans up cipher ctx and deallocates it
231 if self.ctx is not None:
232 self.__ctxcleanup(self.ctx)
233 libcrypto.EVP_CIPHER_CTX_free(self.ctx)
235 except AttributeError:
237 self.cipher_finalized = True
241 # Used C function block_size
243 libcrypto.EVP_CIPHER_block_size.argtypes = (c_void_p, )
245 #Function EVP_CIPHER_CTX_cleanup renamed to EVP_CIPHER_CTX_reset
246 # in the OpenSSL 1.1.0
247 if hasattr(libcrypto,"EVP_CIPHER_CTX_cleanup"):
248 Cipher.__ctxcleanup = libcrypto.EVP_CIPHER_CTX_cleanup
250 Cipher.__ctxcleanup = libcrypto.EVP_CIPHER_CTX_reset
251 Cipher.__ctxcleanup.argtypes = (c_void_p, )
252 libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p, )
253 libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p
254 libcrypto.EVP_CIPHER_CTX_set_padding.argtypes = (c_void_p, c_int)
255 libcrypto.EVP_CipherFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_int))
256 libcrypto.EVP_CIPHER_flags.argtypes = (c_void_p, )
257 libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_void_p, c_char_p,
259 libcrypto.EVP_CIPHER_iv_length.argtypes = (c_void_p, )
260 libcrypto.EVP_CIPHER_key_length.argtypes = (c_void_p, )
261 libcrypto.EVP_CIPHER_nid.argtypes = (c_void_p, )
262 libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_char_p, POINTER(c_int),
264 libcrypto.EVP_get_cipherbyname.restype = c_void_p
265 libcrypto.EVP_get_cipherbyname.argtypes = (c_char_p, )
266 libcrypto.EVP_CIPHER_CTX_set_key_length.argtypes = (c_void_p, c_int)