]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/cipher.py
Fixed syntax of exceptions to python3 compatibility
[oss/ctypescrypto.git] / ctypescrypto / cipher.py
1 """
2 access to symmetric ciphers from libcrypto
3
4 """
5 from ctypes import create_string_buffer,c_char_p,c_void_p,c_int,c_long,byref,POINTER
6 from ctypescrypto import libcrypto
7 from ctypescrypto.exception import LibCryptoError
8 from ctypescrypto.oid import Oid
9
10 CIPHER_ALGORITHMS = ("DES", "DES-EDE3", "BF", "AES-128", "AES-192", "AES-256")
11 CIPHER_MODES = ("STREAM","ECB","CBC", "CFB", "OFB", "CTR","GCM")
12
13 #
14
15 class CipherError(LibCryptoError):
16         pass
17
18 def new(algname,key,encrypt=True,iv=None):
19         """
20                 Returns new cipher object ready to encrypt-decrypt data
21
22                 @param algname - string algorithm name like in opemssl command
23                                                 line
24                 @param key - binary string representing ciher key
25                 @param encrypt - if True (default) cipher would be initialized
26                                         for encryption, otherwise - for decrypton
27                 @param iv - initialization vector
28         """
29         ct=CipherType(algname)
30         return Cipher(ct,key,iv,encrypt)
31
32 class CipherType:
33         """
34                 Describes cihper algorihm. Can be used to produce cipher
35                 instance and to get various information about cihper
36         """
37
38         def __init__(self, cipher_name):
39                 """
40                         Constructs cipher algortihm using textual name as in openssl
41                         command line
42                 """
43                 self.cipher = libcrypto.EVP_get_cipherbyname(cipher_name)
44                 if self.cipher is None:
45                         raise CipherError("Unknown cipher: %s" % cipher_name)
46
47         def __del__(self):
48                 pass
49         def block_size(self):
50                 """
51                         Returns block size of the cipher
52                 """
53                 return libcrypto.EVP_CIPHER_block_size(self.cipher)
54         def key_length(self):
55                 """
56                         Returns key length of the cipher
57                 """
58                 return libcrypto.EVP_CIPHER_key_length(self.cipher)
59         def iv_length(self):
60                 """
61                         Returns initialization vector length of the cipher
62                 """
63                 return libcrypto.EVP_CIPHER_iv_length(self.cipher)
64         def flags(self):
65                 """
66                         Return cipher flags. Low three bits of the flags encode 
67                         cipher mode (see mode). Higher bits  is combinatuon of
68                         EVP_CIPH* constants defined in the <openssl/evp.h>
69                 """
70                 return libcrypto.EVP_CIPHER_flags(self.cipher)
71         def mode(self):
72                 """
73                         Returns cipher mode as string constant like CBC, OFB etc.
74                 """
75                 return CIPHER_MODES[self.flags() & 0x7]
76         def algo(self):
77                 """
78                         Return cipher's algorithm name, derived from OID
79                 """
80                 return self.oid().short_name() 
81         def oid(self):
82                 """
83                         Returns ASN.1 object identifier of the cipher as
84                         ctypescrypto.oid.Oid object
85                 """
86                 return Oid(libcrypto.EVP_CIPHER_nid(self.cipher))
87
88 class Cipher:
89         """
90                 Performs actual encrypton decryption
91                 Note that object keeps some internal state. 
92                 To obtain full ciphertext (or plaintext during decihpering)
93                 user should concatenate results of all calls of update with
94                 result of finish
95         """
96         def __init__(self,      cipher_type, key, iv, encrypt=True):
97                 """
98                         Initializing cipher instance.
99
100                         @param  cipher_type - CipherType object
101                         @param key = binary string representing the key
102                         @param iv - binary string representing initializtion vector
103                         @param encrypt - if True(default) we ere encrypting.
104                                         Otherwise decrypting
105
106                 """
107                 self._clean_ctx()
108                 # Check key and iv length
109                 if key is None:
110                         raise ValueError("No key specified")
111
112                 key_ptr = c_char_p(key)
113                 iv_ptr = c_char_p(iv)
114                 self.ctx = libcrypto.EVP_CIPHER_CTX_new()
115                 if self.ctx == 0:
116                         raise CipherError("Unable to create cipher context")
117                 self.encrypt = encrypt
118                 if encrypt: 
119                         enc = 1
120                 else: 
121                         enc = 0
122                 if not iv is None and len(iv) != cipher_type.iv_length():
123                         raise ValueError("Invalid IV length for this algorithm")
124                         
125                 if len(key) != cipher_type.key_length():
126                         if (cipher_type.flags() & 8) != 0:
127                                 # Variable key length cipher.
128                                 result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, None, None, c_int(enc))
129                                 result=libcrypto.EVP_CIPHER_CTX_set_key_length(self.ctx,len(key))
130                                 if result == 0:
131                                         self._clean_ctx()
132                                         raise CipherError("Unable to set key length")
133                                 result = libcrypto.EVP_CipherInit_ex(self.ctx, None, None, key_ptr, iv_ptr, c_int(enc))
134                         else:
135                                 raise ValueError("Invalid key length for this algorithm")
136                 else:
137                         result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, key_ptr, iv_ptr, c_int(enc))
138                 if result == 0:
139                         self._clean_ctx()
140                         raise CipherError("Unable to initialize cipher")
141                 self.cipher_type = cipher_type
142                 self.block_size = self.cipher_type.block_size()
143                 self.cipher_finalized = False
144
145         def __del__(self):
146                 self._clean_ctx()
147
148         def padding(self, padding=True):
149                 """
150                         Sets padding mode of the cipher
151                 """
152                 if padding:
153                         padding_flag = 1
154                 else:
155                         padding_flag = 0
156                 libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag)
157
158         def update(self, data):
159                 """
160                         Performs actual encrypton/decrypion
161
162                         @param data - part of the plain text/ciphertext to process
163                         @returns - part of ciphercext/plain text
164
165                         Passd chunk of text doeesn't need to contain full ciher
166                         blocks. If neccessery, part of passed data would be kept
167                         internally until next data would be received or finish
168                         called
169                 """
170                 if self.cipher_finalized :
171                         raise CipherError("No updates allowed")
172                 if type(data) != type(""):
173                         raise TypeError("A string is expected")
174                 if len(data) <= 0:
175                         return ""
176                 outbuf=create_string_buffer(self.block_size+len(data))
177                 outlen=c_int(0)
178                 ret=libcrypto.EVP_CipherUpdate(self.ctx,outbuf,byref(outlen),
179                         data,len(data))
180                 if ret <=0:
181                         self._clean_ctx()
182                         self.cipher_finalized=True
183                         del self.ctx
184                         raise CipherError("problem processing data")
185                 return outbuf.raw[:outlen.value]
186         
187         def finish(self):
188                 """
189                         Finalizes processing. If some data are kept in the internal
190                         state, they would be processed and returned.
191                 """
192                 if self.cipher_finalized :
193                         raise CipherError("Cipher operation is already completed")
194                 outbuf=create_string_buffer(self.block_size)
195                 self.cipher_finalized = True
196                 outlen=c_int(0)
197                 result = libcrypto.EVP_CipherFinal_ex(self.ctx,outbuf , byref(outlen))
198                 if result == 0:
199                         self._clean_ctx()
200                         raise CipherError("Unable to finalize cipher")
201                 if outlen.value>0:
202                         return outbuf.raw[:outlen.value]
203                 else:
204                         return ""
205                 
206         def _clean_ctx(self):
207                 try:
208                         if self.ctx is not None:
209                                 self.libcrypto.EVP_CIPHER_CTX_cleanup(self.ctx)
210                                 self.libcrypto.EVP_CIPHER_CTX_free(self.ctx)
211                                 del(self.ctx)
212                 except AttributeError:
213                         pass
214                 self.cipher_finalized = True
215
216
217 #
218 # Used C function block_size
219 #
220 libcrypto.EVP_CIPHER_block_size.argtypes=(c_void_p,)
221 libcrypto.EVP_CIPHER_CTX_cleanup.argtypes=(c_void_p,)
222 libcrypto.EVP_CIPHER_CTX_free.argtypes=(c_void_p,)
223 libcrypto.EVP_CIPHER_CTX_new.restype=c_void_p
224 libcrypto.EVP_CIPHER_CTX_set_padding.argtypes=(c_void_p,c_int)
225 libcrypto.EVP_CipherFinal_ex.argtypes=(c_void_p,c_char_p,POINTER(c_int))
226 libcrypto.EVP_CIPHER_flags.argtypes=(c_void_p,)
227 libcrypto.EVP_CipherInit_ex.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_char_p,c_int)
228 libcrypto.EVP_CIPHER_iv_length.argtypes=(c_void_p,)
229 libcrypto.EVP_CIPHER_key_length.argtypes=(c_void_p,)
230 libcrypto.EVP_CIPHER_nid.argtypes=(c_void_p,)
231 libcrypto.EVP_CipherUpdate.argtypes=(c_void_p,c_char_p,POINTER(c_int),c_char_p,c_int)
232 libcrypto.EVP_get_cipherbyname.restype=c_void_p
233 libcrypto.EVP_get_cipherbyname.argtypes=(c_char_p,)
234 libcrypto.EVP_CIPHER_CTX_set_key_length.argtypes=(c_void_p,c_int)