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