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