]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/cipher.py
1ea4f797d7f4d87e88e86832ae438651d18dde68
[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                 key_ptr = c_char_p(key)
105                 iv_ptr = c_char_p(iv)
106                 self.ctx = libcrypto.EVP_CIPHER_CTX_new()
107                 if self.ctx == 0:
108                         raise CipherError, "Unable to create cipher context"
109                 self.encrypt = encrypt
110                 if encrypt: 
111                         enc = 1
112                 else: 
113                         enc = 0
114                 result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, key_ptr, iv_ptr, c_int(enc))
115                 if result == 0:
116                         self._clean_ctx()
117                         raise CipherError, "Unable to initialize cipher"
118                 self.cipher_type = cipher_type
119                 self.block_size = self.cipher_type.block_size()
120                 self.cipher_finalized = False
121
122         def __del__(self):
123                 self._clean_ctx()
124
125         def padding(self, padding=True):
126                 """
127                         Sets padding mode of the cipher
128                 """
129                 if padding:
130                         padding_flag = 1
131                 else:
132                         padding_flag = 0
133                 libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag)
134
135         def update(self, data):
136                 """
137                         Performs actual encrypton/decrypion
138
139                         @param data - part of the plain text/ciphertext to process
140                         @returns - part of ciphercext/plain text
141
142                         Passd chunk of text doeesn't need to contain full ciher
143                         blocks. If neccessery, part of passed data would be kept
144                         internally until next data would be received or finish
145                         called
146                 """
147                 if self.cipher_finalized :
148                         raise CipherError, "No updates allowed"
149                 if type(data) != type(""):
150                         raise TypeError, "A string is expected"
151                 if len(data) <= 0:
152                         return ""
153                 outbuf=create_string_buffer(self.block_size+len(data))
154                 outlen=c_int(0)
155                 ret=libcrypto.EVP_CipherUpdate(self.ctx,outbuf,byref(outlen),
156                         data,len(data))
157                 if ret <=0:
158                         self._clean_ctx()
159                         self.cipher_finalized=True
160                         del self.ctx
161                         raise CipherError("problem processing data")
162                 return outbuf.raw[:outlen.value]
163         
164         def finish(self):
165                 """
166                         Finalizes processing. If some data are kept in the internal
167                         state, they would be processed and returned.
168                 """
169                 if self.cipher_finalized :
170                         raise CipherError, "Cipher operation is already completed"
171                 outbuf=create_string_buffer(self.block_size)
172                 self.cipher_finalized = True
173                 outlen=c_int()
174                 result = libcrypto.EVP_CipherFinal_ex(self.ctx,outbuf , byref(outlen))
175                 if result == 0:
176                         self._clean_ctx()
177                         raise CipherError, "Unable to finalize cipher"
178                 if outlen.value>0:
179                         return outbuf.raw[:outlen.value]
180                 else:
181                         return ""
182                 
183         def _clean_ctx(self):
184                 try:
185                         if self.ctx is not None:
186                                 self.libcrypto.EVP_CIPHER_CTX_cleanup(self.ctx)
187                                 self.libcrypto.EVP_CIPHER_CTX_free(self.ctx)
188                                 del(self.ctx)
189                 except AttributeError:
190                         pass
191                 self.cipher_finalized = True
192
193
194 #
195 # Used C function block_size
196 #
197 libcrypto.EVP_CIPHER_block_size.argtypes=(c_void_p,)
198 libcrypto.EVP_CIPHER_CTX_cleanup.argtypes=(c_void_p,)
199 libcrypto.EVP_CIPHER_CTX_free.argtypes=(c_void_p,)
200 libcrypto.EVP_CIPHER_CTX_new.restype=c_void_p
201 libcrypto.EVP_CIPHER_CTX_set_padding.argtypes=(c_void_p,c_int)
202 libcrypto.EVP_CipherFinal_ex.argtypes=(c_void_p,c_char_p,POINTER(c_int))
203 libcrypto.EVP_CIPHER_flags.argtypes=(c_void_p,)
204 libcrypto.EVP_CipherInit_ex.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_char_p,c_int)
205 libcrypto.EVP_CIPHER_iv_length.argtypes=(c_void_p,)
206 libcrypto.EVP_CIPHER_key_length.argtypes=(c_void_p,)
207 libcrypto.EVP_CIPHER_nid.argtypes=(c_void_p,)
208 libcrypto.EVP_CipherUpdate.argtypes=(c_void_p,c_char_p,POINTER(c_int),c_char_p,c_int)
209 libcrypto.EVP_get_cipherbyname.restype=c_void_p
210 libcrypto.EVP_get_cipherbyname.argtypes=(c_char_p,)
211