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, pyver, bintype
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
50 cipher_name = cipher_name.encode('utf-8')
51 self.cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
52 if self.cipher is None:
53 raise CipherError("Unknown cipher: %s" % cipher_name)
57 It is constant object with do-nothing del
63 Returns block size of the cipher
65 return libcrypto.EVP_CIPHER_block_size(self.cipher)
69 Returns key length of the cipher
71 return libcrypto.EVP_CIPHER_key_length(self.cipher)
75 Returns initialization vector length of the cipher
77 return libcrypto.EVP_CIPHER_iv_length(self.cipher)
81 Return cipher flags. Low three bits of the flags encode
82 cipher mode (see mode). Higher bits is combinatuon of
83 EVP_CIPH* constants defined in the <openssl/evp.h>
85 return libcrypto.EVP_CIPHER_flags(self.cipher)
89 Returns cipher mode as string constant like CBC, OFB etc.
91 return CIPHER_MODES[self.flags() & 0x7]
95 Return cipher's algorithm name, derived from OID
97 return self.oid().shortname()
101 Returns ASN.1 object identifier of the cipher as
102 ctypescrypto.oid.Oid object
104 return Oid(libcrypto.EVP_CIPHER_nid(self.cipher))
106 class Cipher(object):
108 Performs actual encrypton decryption
109 Note that object keeps some internal state.
110 To obtain full ciphertext (or plaintext during decihpering)
111 user should concatenate results of all calls of update with
114 def __init__(self, cipher_type, key, iv, encrypt=True):
116 Initializing cipher instance.
118 @param cipher_type - CipherType object
119 @param key = binary string representing the key
120 @param iv - binary string representing initializtion vector
121 @param encrypt - if True(default) we ere encrypting.
126 # Check key and iv length
128 raise ValueError("No key specified")
130 key_ptr = c_char_p(key)
131 iv_ptr = c_char_p(iv)
132 self.ctx = libcrypto.EVP_CIPHER_CTX_new()
134 raise CipherError("Unable to create cipher context")
135 self.encrypt = encrypt
136 enc = 1 if encrypt else 0
137 if not iv is None and len(iv) != cipher_type.iv_length():
138 raise ValueError("Invalid IV length for this algorithm")
140 if len(key) != cipher_type.key_length():
141 if (cipher_type.flags() & 8) != 0:
142 # Variable key length cipher.
143 result = libcrypto.EVP_CipherInit_ex(self.ctx,
147 result = libcrypto.EVP_CIPHER_CTX_set_key_length(self.ctx,
151 raise CipherError("Unable to set key length")
152 result = libcrypto.EVP_CipherInit_ex(self.ctx, None, None,
156 raise ValueError("Invalid key length for this algorithm")
158 result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher,
159 None, key_ptr, iv_ptr,
163 raise CipherError("Unable to initialize cipher")
164 self.cipher_type = cipher_type
165 self.block_size = self.cipher_type.block_size()
166 self.cipher_finalized = False
170 We define _clean_ctx() to do all the cleanup
174 def padding(self, padding=True):
176 Sets padding mode of the cipher
178 padding_flag = 1 if padding else 0
179 libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag)
181 def update(self, data):
183 Performs actual encrypton/decrypion
185 @param data - part of the plain text/ciphertext to process
186 @returns - part of ciphercext/plain text
188 Passed chunk of text doesn't need to contain full ciher
189 blocks. If neccessery, part of passed data would be kept
190 internally until next data would be received or finish
193 if self.cipher_finalized:
194 raise CipherError("No updates allowed")
195 if not isinstance(data, bintype):
196 raise TypeError("A byte string is expected")
199 outbuf = create_string_buffer(self.block_size+len(data))
201 ret = libcrypto.EVP_CipherUpdate(self.ctx, outbuf, byref(outlen),
205 self.cipher_finalized = True
206 raise CipherError("problem processing data")
207 return outbuf.raw[:int(outlen.value)]
211 Finalizes processing. If some data are kept in the internal
212 state, they would be processed and returned.
214 if self.cipher_finalized:
215 raise CipherError("Cipher operation is already completed")
216 outbuf = create_string_buffer(self.block_size)
217 self.cipher_finalized = True
219 result = libcrypto.EVP_CipherFinal_ex(self.ctx, outbuf, byref(outlen))
222 raise CipherError("Unable to finalize cipher")
224 return outbuf.raw[:int(outlen.value)]
228 def _clean_ctx(self):
230 Cleans up cipher ctx and deallocates it
233 if self.ctx is not None:
234 self.__ctxcleanup(self.ctx)
235 libcrypto.EVP_CIPHER_CTX_free(self.ctx)
237 except AttributeError:
239 self.cipher_finalized = True
243 # Used C function block_size
245 libcrypto.EVP_CIPHER_block_size.argtypes = (c_void_p, )
247 #Function EVP_CIPHER_CTX_cleanup renamed to EVP_CIPHER_CTX_reset
248 # in the OpenSSL 1.1.0
249 if hasattr(libcrypto,"EVP_CIPHER_CTX_cleanup"):
250 Cipher.__ctxcleanup = libcrypto.EVP_CIPHER_CTX_cleanup
252 Cipher.__ctxcleanup = libcrypto.EVP_CIPHER_CTX_reset
253 Cipher.__ctxcleanup.argtypes = (c_void_p, )
254 libcrypto.EVP_CIPHER_CTX_free.argtypes = (c_void_p, )
255 libcrypto.EVP_CIPHER_CTX_new.restype = c_void_p
256 libcrypto.EVP_CIPHER_CTX_set_padding.argtypes = (c_void_p, c_int)
257 libcrypto.EVP_CipherFinal_ex.argtypes = (c_void_p, c_char_p, POINTER(c_int))
258 libcrypto.EVP_CIPHER_flags.argtypes = (c_void_p, )
259 libcrypto.EVP_CipherInit_ex.argtypes = (c_void_p, c_void_p, c_void_p, c_char_p,
261 libcrypto.EVP_CIPHER_iv_length.argtypes = (c_void_p, )
262 libcrypto.EVP_CIPHER_key_length.argtypes = (c_void_p, )
263 libcrypto.EVP_CIPHER_nid.argtypes = (c_void_p, )
264 libcrypto.EVP_CipherUpdate.argtypes = (c_void_p, c_char_p, POINTER(c_int),
266 libcrypto.EVP_get_cipherbyname.restype = c_void_p
267 libcrypto.EVP_get_cipherbyname.argtypes = (c_char_p, )
268 libcrypto.EVP_CIPHER_CTX_set_key_length.argtypes = (c_void_p, c_int)