]> wagner.pp.ru Git - oss/ctypescrypto.git/commitdiff
rewrote cipher module
authorVictor Wagner <wagner@atlas-card.ru>
Thu, 5 Jun 2014 14:11:37 +0000 (18:11 +0400)
committerVictor Wagner <wagner@atlas-card.ru>
Thu, 5 Jun 2014 14:11:37 +0000 (18:11 +0400)
README.md
ctypescrypto/cipher.py

index 9abd63b5d14175d49d7c4e11c4e36fda71bcef6e..dfcab2bd7d53abdd289ae9f9f5007fa17893d911 100644 (file)
--- a/README.md
+++ b/README.md
@@ -37,13 +37,11 @@ digests.py  - Interface  to EVP\_Digest\* family of functions.
        Status: fully implemented and covered by tests
 
 ciphers.py - Interface to EVP\_Cipher family of function. 
-       Status: Needs complete rewriting and test coverage. Idea to keep
-       cleartext in python variable until entire text would be passed to
-       update is EVIL.
+       Status: Needs documenting and test coverage
 
 pkey.py - Low-level private key operations (like pkey, genpkey and p
     keyutl command line ops), all via algorithm-agnostic EVP interface.
-       Status: Designed and mostly implemented but not yet covered by tests
+       Status: Designed and started to implement but not yet covered by tests
 
 exception.py OpenSSL error stack to python exception conversion
        Implemented.
index 73a14ce1de988e1c993991b3afb77ad4a729cf32..4fb576773a603529a40eb08156c1269039322cd9 100644 (file)
@@ -1,39 +1,52 @@
-from ctypes import *\r
+from ctypes import create_string_buffer,c_char_p,c_void_p,c_int,c_long,byref\r
+from ctypescrypto import libcrypto\r
+from ctypescryto.exception import LibCrytoError\r
 \r
 CIPHER_ALGORITHMS = ("DES", "DES-EDE3", "BF", "AES-128", "AES-192", "AES-256")\r
-CIPHER_MODES = ("CBC", "CFB", "OFB", "ECB")\r
+CIPHER_MODES = ("STREAM","ECB","CBC", "CFB", "OFB", "CTR","GCM")\r
 \r
-class CipherError(Exception):\r
+#\r
+\r
+class CipherError(LibCryptoError):\r
     pass\r
 \r
+def new(algname,key,encrypt=True,iv=None):\r
+       ct=CipherType(algname)\r
+       return Cipher(ct,key,iv,encrypt)\r
+\r
 class CipherType:\r
 \r
-    def __init__(self, libcrypto, cipher_algo, cipher_mode):\r
-        self.libcrypto = libcrypto\r
-        self.cipher_algo = cipher_algo\r
-        self.cipher_mode = cipher_mode\r
-        cipher_name = "-".join([self.cipher_algo, self.cipher_mode])\r
-        self.cipher = self.libcrypto.EVP_get_cipherbyname(cipher_name)\r
-        if self.cipher == 0:\r
+    def __init__(self, cipher_name):\r
+        self.cipher = libcrypto.EVP_get_cipherbyname(cipher_name)\r
+        if self.cipher is None:\r
             raise CipherError, "Unknown cipher: %s" % cipher_name\r
 \r
     def __del__(self):\r
         pass\r
-\r
+       def block_size(self):\r
+               return libcrypto.EVP_CIHPER_block_size(self.cipher)\r
+       def key_length(self):\r
+               return libcrypto.EVP_CIPHER_key_length(self.cipher)\r
+       def iv_length(self):\r
+               return libcrypto.EVP_CIPHER_iv_length(self.cipher)\r
+       def flags(self):\r
+               return libcrypto.EVP_CIPHER_flags(self.cipher)\r
+       def mode(self):\r
+               return CIPHER_MODES[self.flags & 0x7]\r
     def algo(self):\r
-        return self.cipher_algo\r
-\r
+        return self.oid().short_name() \r
     def mode(self):\r
         return self.cipher_mode\r
+       def oid(self):\r
+               return Oid(libcrypto.EVP_CIPHER_nid(self.cipher))\r
 \r
 class Cipher:\r
 \r
-    def __init__(self, libcrypto, cipher_type, key, iv, encrypt=True):\r
-        self.libcrypto = libcrypto\r
+    def __init__(self,  cipher_type, key, iv, encrypt=True):\r
         self._clean_ctx()\r
         key_ptr = c_char_p(key)\r
         iv_ptr = c_char_p(iv)\r
-        self.ctx = self.libcrypto.EVP_CIPHER_CTX_new(cipher_type.cipher, None, key_ptr, iv_ptr)\r
+        self.ctx = libcrypto.EVP_CIPHER_CTX_new(cipher_type.cipher, None, key_ptr, iv_ptr)\r
         if self.ctx == 0:\r
             raise CipherError, "Unable to create cipher context"\r
         self.encrypt = encrypt\r
@@ -41,8 +54,9 @@ class Cipher:
             enc = 1\r
         else: \r
             enc = 0\r
-        result = self.libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, key_ptr, iv_ptr, c_int(enc))\r
+        result = libcrypto.EVP_CipherInit_ex(self.ctx, cipher_type.cipher, None, key_ptr, iv_ptr, c_int(enc))\r
         self.cipher_type = cipher_type\r
+               self.block_size = self.cipher_type.block_size()\r
         if result == 0:\r
             self._clean_ctx()\r
             raise CipherError, "Unable to initialize cipher"\r
@@ -55,7 +69,7 @@ class Cipher:
             padding_flag = 1\r
         else:\r
             padding_flag = 0\r
-        self.libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag)\r
+        libcrypto.EVP_CIPHER_CTX_set_padding(self.ctx, padding_flag)\r
 \r
     def update(self, data):\r
         if self.cipher_finalized :\r
@@ -64,29 +78,30 @@ class Cipher:
             raise TypeError, "A string is expected"\r
         if len(data) <= 0:\r
             return ""\r
-        self.data = self.data + data\r
+               outbuf=string_buffer_create(self.blocsize+len(data))\r
+               outlen=c_int(0)\r
+               ret=libcrypto.EVP_CipherUpdate(self.ctx,outbuf,byref(outlen),\r
+                       data,len(data))\r
+               if ret <=0:\r
+                       self._clean_ctx()\r
+                       self.cipher_finalized=True\r
+                       del self.ctx\r
+                       raise CipherError("problem processing data")\r
+               return outbuf.raw[:outlen]\r
     \r
-    def finish(self, data=None):\r
-        if data is not None:\r
-            self.update(data)\r
-        return self._finish()\r
-        \r
-    def _finish(self):\r
+    def finish(self):\r
         if self.cipher_finalized :\r
             raise CipherError, "Cipher operation is already completed"\r
-        self.cipher_out = create_string_buffer(len(self.data) + 32)\r
-        result = self.libcrypto.EVP_CipherUpdate(self.ctx, byref(self.cipher_out), byref(self.cipher_out_len), c_char_p(self.data), len(self.data))\r
-        if result == 0:\r
-            self._clean_ctx()\r
-            raise CipherError, "Unable to update cipher"\r
+               outbuf=create_string_buffer(self.block_size)\r
         self.cipher_finalized = True\r
-        update_data = self.cipher_out.raw[:self.cipher_out_len.value]\r
-        result = self.libcrypto.EVP_CipherFinal_ex(self.ctx, byref(self.cipher_out), byref(self.cipher_out_len))\r
+        result = self.libcrypto.EVP_CipherFinal_ex(self.ctx,outbuf , byref(outlen))\r
         if result == 0:\r
             self._clean_ctx()\r
             raise CipherError, "Unable to finalize cipher"\r
-        final_data = self.cipher_out.raw[:self.cipher_out_len.value]\r
-        return update_data + final_data\r
+               if outlen>0:\r
+               return outbuf.raw[:outlen]\r
+               else\r
+                       return ""\r
         \r
     def _clean_ctx(self):\r
         try:\r
@@ -96,7 +111,4 @@ class Cipher:
                 del(self.ctx)\r
         except AttributeError:\r
             pass\r
-        self.cipher_out = None\r
-        self.cipher_out_len = c_long(0)\r
-        self.data = ""\r
-        self.cipher_finalized = False
\ No newline at end of file
+        self.cipher_finalized = True\r