├── CHANGES.txt ├── LICENSE ├── README.md ├── pyelliptic ├── __init__.py ├── cipher.py ├── hash.py └── openssl.py ├── setup.py └── test.py /CHANGES.txt: -------------------------------------------------------------------------------- 1 | v1.5.8, 2017-04-25 2 | ------------------ 3 | 4 | - Remove ECC part (issue #50) 5 | 6 | v1.5.7, 2015-08-31 7 | ------------------ 8 | 9 | - Rewrite ECC key encoding (issue #39) 10 | 11 | v1.5.6, 2015-02-28 12 | ------------------ 13 | 14 | - replace EVP_DigestInit and EVP_DigestFinal by *_ex (issue #32) 15 | - SHA256 for sign/verify operations (issue #32) 16 | - fix arithmetic.py (issue #30) 17 | 18 | v1.5.5, 2014-10-09 19 | ------------------ 20 | 21 | - Change licence to BSD (issue #24) 22 | - Rewrite test.py with unittest (issue #27) 23 | - Add constant time equality comparison (issue #23) 24 | - add arithmetic.py (pull request #21) 25 | - add some functions in openssl.py (pull request #22) 26 | - raise exception if RAND_bytes failed (pull request #22) 27 | 28 | v1.5.4, 2014-08-25 29 | ------------------ 30 | 31 | - Add CHANGES.txt according to pip conventions (issue #19) 32 | - PEP8 (issue #20) 33 | - Relative imports (issue #18) 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Yann GUIBET . 2 | All rights reserved. 3 | 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are 7 | met: 8 | 9 | 1. Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright 13 | notice, this list of conditions and the following disclaimer in 14 | the documentation and/or other materials provided with the 15 | distribution. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 19 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 20 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 21 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 25 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 26 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 27 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## WARNING 2 | 3 | PyElliptic is **DEPRECATED**. 4 | 5 | See https://github.com/yann2192/pyelliptic/issues/50 6 | 7 | # PyElliptic 8 | 9 | PyElliptic is a high level wrapper for the cryptographic library : OpenSSL. 10 | Under the Berkeley software distribution license (see LICENSE). 11 | 12 | Python3 compatible. For GNU/Linux and Windows. 13 | Require OpenSSL 14 | 15 | ## Features 16 | 17 | ### Asymmetric cryptography using Elliptic Curve Cryptography (ECC) 18 | 19 | * Key agreement : ECDH 20 | * Digital signatures : ECDSA 21 | * Hybrid encryption : ECIES (like RSA) 22 | 23 | ### Symmetric cryptography 24 | 25 | * AES-128 (CBC, OFB, CFB, CTR) 26 | * AES-256 (CBC, OFB, CFB, CTR) 27 | * Blowfish (CFB and CBC) 28 | * RC4 29 | 30 | ### Other 31 | 32 | * CSPRNG 33 | * HMAC (using SHA512) 34 | * PBKDF2 (SHA256 and SHA512) 35 | 36 | ## Example 37 | 38 | ```python 39 | #!/usr/bin/python 40 | 41 | from binascii import hexlify 42 | 43 | import pyelliptic 44 | 45 | # Symmetric encryption 46 | iv = pyelliptic.Cipher.gen_IV('aes-256-cfb') 47 | ctx = pyelliptic.Cipher("secretkey", iv, 1, ciphername='aes-256-cfb') 48 | 49 | ciphertext = ctx.update('test1') 50 | ciphertext += ctx.update('test2') 51 | ciphertext += ctx.final() 52 | 53 | ctx2 = pyelliptic.Cipher("secretkey", iv, 0, ciphername='aes-256-cfb') 54 | print(ctx2.ciphering(ciphertext)) 55 | 56 | # Asymmetric encryption 57 | alice = pyelliptic.ECC() # default curve: sect283r1 58 | bob = pyelliptic.ECC(curve='sect571r1') 59 | 60 | ciphertext = alice.encrypt("Hello Bob", bob.get_pubkey(), 61 | ephemcurve='sect571r1') 62 | print(bob.decrypt(ciphertext)) 63 | 64 | signature = bob.sign("Hello Alice") 65 | # alice's job : 66 | print(pyelliptic.ECC(pubkey=bob.get_pubkey(), 67 | curve='sect571r1').verify(signature, "Hello Alice")) 68 | 69 | # ERROR !!! 70 | try: 71 | key = alice.get_ecdh_key(bob.get_pubkey()) 72 | except: 73 | print("For ECDH key agreement, the keys must be defined on the same curve !") 74 | 75 | alice = pyelliptic.ECC(curve='sect571r1') 76 | print(hexlify(alice.get_ecdh_key(bob.get_pubkey()))) 77 | print(hexlify(bob.get_ecdh_key(alice.get_pubkey()))) 78 | ``` 79 | -------------------------------------------------------------------------------- /pyelliptic/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (C) 2014 5 | # Author: Yann GUIBET 6 | # Contact: 7 | # All rights reserved. 8 | # 9 | # 10 | # Redistribution and use in source and binary forms, with or without 11 | # modification, are permitted provided that the following conditions are 12 | # met: 13 | # 14 | # 1. Redistributions of source code must retain the above copyright 15 | # notice, this list of conditions and the following disclaimer. 16 | # 17 | # 2. Redistributions in binary form must reproduce the above copyright 18 | # notice, this list of conditions and the following disclaimer in 19 | # the documentation and/or other materials provided with the 20 | # distribution. 21 | # 22 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 23 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 24 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 25 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 26 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 27 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 28 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 29 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 30 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 31 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 32 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | 34 | __version__ = '1.5.7' 35 | 36 | __all__ = [ 37 | 'OpenSSL', 38 | 'ecc', 39 | 'cipher', 40 | 'hash', 41 | ] 42 | 43 | from .openssl import OpenSSL 44 | from .cipher import Cipher 45 | from .hash import hmac_sha256, hmac_sha512, pbkdf2, equals 46 | -------------------------------------------------------------------------------- /pyelliptic/cipher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (c) 2014 Yann GUIBET . 5 | # All rights reserved. 6 | # 7 | # 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # 1. Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # 15 | # 2. Redistributions in binary form must reproduce the above copyright 16 | # notice, this list of conditions and the following disclaimer in 17 | # the documentation and/or other materials provided with the 18 | # distribution. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 24 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 30 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from .openssl import OpenSSL 33 | 34 | 35 | class Cipher: 36 | """ 37 | Symmetric encryption 38 | 39 | import pyelliptic 40 | iv = pyelliptic.Cipher.gen_IV('aes-256-cfb') 41 | ctx = pyelliptic.Cipher("secretkey", iv, 1, ciphername='aes-256-cfb') 42 | ciphertext = ctx.update('test1') 43 | ciphertext += ctx.update('test2') 44 | ciphertext += ctx.final() 45 | 46 | ctx2 = pyelliptic.Cipher("secretkey", iv, 0, ciphername='aes-256-cfb') 47 | print ctx2.ciphering(ciphertext) 48 | """ 49 | def __init__(self, key, iv, do, ciphername='aes-256-cbc'): 50 | """ 51 | do == 1 => Encrypt; do == 0 => Decrypt 52 | """ 53 | self.cipher = OpenSSL.get_cipher(ciphername) 54 | self.ctx = OpenSSL.EVP_CIPHER_CTX_new() 55 | if do == 1 or do == 0: 56 | k = OpenSSL.malloc(key, len(key)) 57 | IV = OpenSSL.malloc(iv, len(iv)) 58 | OpenSSL.EVP_CipherInit_ex( 59 | self.ctx, self.cipher.get_pointer(), 0, k, IV, do) 60 | else: 61 | raise Exception("RTFM ...") 62 | 63 | @staticmethod 64 | def get_all_cipher(): 65 | """ 66 | static method, returns all ciphers available 67 | """ 68 | return OpenSSL.cipher_algo.keys() 69 | 70 | @staticmethod 71 | def get_blocksize(ciphername): 72 | cipher = OpenSSL.get_cipher(ciphername) 73 | return cipher.get_blocksize() 74 | 75 | @staticmethod 76 | def gen_IV(ciphername): 77 | cipher = OpenSSL.get_cipher(ciphername) 78 | return OpenSSL.rand(cipher.get_blocksize()) 79 | 80 | def update(self, input): 81 | i = OpenSSL.c_int(0) 82 | buffer = OpenSSL.malloc(b"", len(input) + self.cipher.get_blocksize()) 83 | inp = OpenSSL.malloc(input, len(input)) 84 | if OpenSSL.EVP_CipherUpdate(self.ctx, OpenSSL.byref(buffer), 85 | OpenSSL.byref(i), inp, len(input)) == 0: 86 | raise Exception("[OpenSSL] EVP_CipherUpdate FAIL ...") 87 | return buffer.raw[0:i.value] 88 | 89 | def final(self): 90 | i = OpenSSL.c_int(0) 91 | buffer = OpenSSL.malloc(b"", self.cipher.get_blocksize()) 92 | if (OpenSSL.EVP_CipherFinal_ex(self.ctx, OpenSSL.byref(buffer), 93 | OpenSSL.byref(i))) == 0: 94 | raise Exception("[OpenSSL] EVP_CipherFinal_ex FAIL ...") 95 | return buffer.raw[0:i.value] 96 | 97 | def ciphering(self, input): 98 | """ 99 | Do update and final in one method 100 | """ 101 | buff = self.update(input) 102 | return buff + self.final() 103 | 104 | def __del__(self): 105 | OpenSSL.EVP_CIPHER_CTX_reset(self.ctx) 106 | OpenSSL.EVP_CIPHER_CTX_free(self.ctx) 107 | -------------------------------------------------------------------------------- /pyelliptic/hash.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (c) 2014 Yann GUIBET . 5 | # All rights reserved. 6 | # 7 | # 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # 1. Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # 15 | # 2. Redistributions in binary form must reproduce the above copyright 16 | # notice, this list of conditions and the following disclaimer in 17 | # the documentation and/or other materials provided with the 18 | # distribution. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 24 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 30 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from .openssl import OpenSSL 33 | 34 | 35 | # For python3 36 | def _equals_bytes(a, b): 37 | if len(a) != len(b): 38 | return False 39 | result = 0 40 | for x, y in zip(a, b): 41 | result |= x ^ y 42 | return result == 0 43 | 44 | 45 | def _equals_str(a, b): 46 | if len(a) != len(b): 47 | return False 48 | result = 0 49 | for x, y in zip(a, b): 50 | result |= ord(x) ^ ord(y) 51 | return result == 0 52 | 53 | 54 | def equals(a, b): 55 | if isinstance(a, str): 56 | return _equals_str(a, b) 57 | else: 58 | return _equals_bytes(a, b) 59 | 60 | 61 | def hmac_sha256(k, m): 62 | """ 63 | Compute the key and the message with HMAC SHA5256 64 | """ 65 | key = OpenSSL.malloc(k, len(k)) 66 | d = OpenSSL.malloc(m, len(m)) 67 | md = OpenSSL.malloc(0, 32) 68 | i = OpenSSL.pointer(OpenSSL.c_int(0)) 69 | OpenSSL.HMAC(OpenSSL.EVP_sha256(), key, len(k), d, len(m), md, i) 70 | return md.raw 71 | 72 | 73 | def hmac_sha512(k, m): 74 | """ 75 | Compute the key and the message with HMAC SHA512 76 | """ 77 | key = OpenSSL.malloc(k, len(k)) 78 | d = OpenSSL.malloc(m, len(m)) 79 | md = OpenSSL.malloc(0, 64) 80 | i = OpenSSL.pointer(OpenSSL.c_int(0)) 81 | OpenSSL.HMAC(OpenSSL.EVP_sha512(), key, len(k), d, len(m), md, i) 82 | return md.raw 83 | 84 | 85 | def pbkdf2(password, salt=None, i=10000, keylen=64): 86 | if salt is None: 87 | salt = OpenSSL.rand(8) 88 | p_password = OpenSSL.malloc(password, len(password)) 89 | p_salt = OpenSSL.malloc(salt, len(salt)) 90 | output = OpenSSL.malloc(0, keylen) 91 | OpenSSL.PKCS5_PBKDF2_HMAC(p_password, len(password), p_salt, 92 | len(p_salt), i, OpenSSL.EVP_sha256(), 93 | keylen, output) 94 | return salt, output.raw 95 | -------------------------------------------------------------------------------- /pyelliptic/openssl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (c) 2014 Yann GUIBET . 5 | # All rights reserved. 6 | # 7 | # 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # 1. Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # 15 | # 2. Redistributions in binary form must reproduce the above copyright 16 | # notice, this list of conditions and the following disclaimer in 17 | # the documentation and/or other materials provided with the 18 | # distribution. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 24 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 30 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | import sys 33 | import ctypes 34 | import ctypes.util 35 | 36 | OpenSSL = None 37 | 38 | 39 | class CipherName: 40 | def __init__(self, name, pointer, blocksize): 41 | self._name = name 42 | self._pointer = pointer 43 | self._blocksize = blocksize 44 | 45 | def __str__(self): 46 | return ("Cipher : %s | Blocksize : %s | Fonction pointer : %s" % 47 | (self._name, str(self._blocksize), str(self._pointer))) 48 | 49 | def get_pointer(self): 50 | return self._pointer() 51 | 52 | def get_name(self): 53 | return self._name 54 | 55 | def get_blocksize(self): 56 | return self._blocksize 57 | 58 | 59 | class _OpenSSL: 60 | """ 61 | Wrapper for OpenSSL using ctypes 62 | """ 63 | def __init__(self, library): 64 | """ 65 | Build the wrapper 66 | """ 67 | self._lib = ctypes.CDLL(library) 68 | 69 | self.pointer = ctypes.pointer 70 | self.c_int = ctypes.c_int 71 | self.byref = ctypes.byref 72 | self.create_string_buffer = ctypes.create_string_buffer 73 | 74 | self.ERR_error_string = self._lib.ERR_error_string 75 | self.ERR_error_string.restype = ctypes.c_char_p 76 | self.ERR_error_string.argtypes = [ctypes.c_ulong, ctypes.c_char_p] 77 | 78 | self.ERR_get_error = self._lib.ERR_get_error 79 | self.ERR_get_error.restype = ctypes.c_ulong 80 | self.ERR_get_error.argtypes = [] 81 | 82 | self.EVP_CipherInit_ex = self._lib.EVP_CipherInit_ex 83 | self.EVP_CipherInit_ex.restype = ctypes.c_int 84 | self.EVP_CipherInit_ex.argtypes = [ctypes.c_void_p, 85 | ctypes.c_void_p, ctypes.c_void_p] 86 | 87 | self.EVP_CIPHER_CTX_new = self._lib.EVP_CIPHER_CTX_new 88 | self.EVP_CIPHER_CTX_new.restype = ctypes.c_void_p 89 | self.EVP_CIPHER_CTX_new.argtypes = [] 90 | 91 | # Cipher 92 | self.EVP_aes_128_cfb128 = self._lib.EVP_aes_128_cfb128 93 | self.EVP_aes_128_cfb128.restype = ctypes.c_void_p 94 | self.EVP_aes_128_cfb128.argtypes = [] 95 | 96 | self.EVP_aes_256_cfb128 = self._lib.EVP_aes_256_cfb128 97 | self.EVP_aes_256_cfb128.restype = ctypes.c_void_p 98 | self.EVP_aes_256_cfb128.argtypes = [] 99 | 100 | self.EVP_aes_128_cbc = self._lib.EVP_aes_128_cbc 101 | self.EVP_aes_128_cbc.restype = ctypes.c_void_p 102 | self.EVP_aes_128_cbc.argtypes = [] 103 | 104 | self.EVP_aes_256_cbc = self._lib.EVP_aes_256_cbc 105 | self.EVP_aes_256_cbc.restype = ctypes.c_void_p 106 | self.EVP_aes_256_cbc.argtypes = [] 107 | 108 | try: 109 | self.EVP_aes_128_ctr = self._lib.EVP_aes_128_ctr 110 | except AttributeError: 111 | pass 112 | else: 113 | self.EVP_aes_128_ctr.restype = ctypes.c_void_p 114 | self.EVP_aes_128_ctr.argtypes = [] 115 | 116 | try: 117 | self.EVP_aes_256_ctr = self._lib.EVP_aes_256_ctr 118 | except AttributeError: 119 | pass 120 | else: 121 | self.EVP_aes_256_ctr.restype = ctypes.c_void_p 122 | self.EVP_aes_256_ctr.argtypes = [] 123 | 124 | self.EVP_aes_128_ofb = self._lib.EVP_aes_128_ofb 125 | self.EVP_aes_128_ofb.restype = ctypes.c_void_p 126 | self.EVP_aes_128_ofb.argtypes = [] 127 | 128 | self.EVP_aes_256_ofb = self._lib.EVP_aes_256_ofb 129 | self.EVP_aes_256_ofb.restype = ctypes.c_void_p 130 | self.EVP_aes_256_ofb.argtypes = [] 131 | 132 | self.EVP_bf_cbc = self._lib.EVP_bf_cbc 133 | self.EVP_bf_cbc.restype = ctypes.c_void_p 134 | self.EVP_bf_cbc.argtypes = [] 135 | 136 | self.EVP_bf_cfb64 = self._lib.EVP_bf_cfb64 137 | self.EVP_bf_cfb64.restype = ctypes.c_void_p 138 | self.EVP_bf_cfb64.argtypes = [] 139 | 140 | self.EVP_rc4 = self._lib.EVP_rc4 141 | self.EVP_rc4.restype = ctypes.c_void_p 142 | self.EVP_rc4.argtypes = [] 143 | 144 | self.EVP_CIPHER_CTX_reset = self._lib.EVP_CIPHER_CTX_reset 145 | self.EVP_CIPHER_CTX_reset.restype = ctypes.c_int 146 | self.EVP_CIPHER_CTX_reset.argtypes = [ctypes.c_void_p] 147 | 148 | self.EVP_CIPHER_CTX_free = self._lib.EVP_CIPHER_CTX_free 149 | self.EVP_CIPHER_CTX_free.restype = None 150 | self.EVP_CIPHER_CTX_free.argtypes = [ctypes.c_void_p] 151 | 152 | self.EVP_CipherUpdate = self._lib.EVP_CipherUpdate 153 | self.EVP_CipherUpdate.restype = ctypes.c_int 154 | self.EVP_CipherUpdate.argtypes = [ctypes.c_void_p, 155 | ctypes.c_void_p, 156 | ctypes.c_void_p, 157 | ctypes.c_void_p, 158 | ctypes.c_int] 159 | 160 | self.EVP_CipherFinal_ex = self._lib.EVP_CipherFinal_ex 161 | self.EVP_CipherFinal_ex.restype = ctypes.c_int 162 | self.EVP_CipherFinal_ex.argtypes = 3 * [ctypes.c_void_p] 163 | 164 | self.EVP_DigestInit = self._lib.EVP_DigestInit 165 | self.EVP_DigestInit.restype = ctypes.c_int 166 | self._lib.EVP_DigestInit.argtypes = 2 * [ctypes.c_void_p] 167 | 168 | self.EVP_DigestInit_ex = self._lib.EVP_DigestInit_ex 169 | self.EVP_DigestInit_ex.restype = ctypes.c_int 170 | self._lib.EVP_DigestInit_ex.argtypes = 3 * [ctypes.c_void_p] 171 | 172 | self.EVP_DigestUpdate = self._lib.EVP_DigestUpdate 173 | self.EVP_DigestUpdate.restype = ctypes.c_int 174 | self.EVP_DigestUpdate.argtypes = [ctypes.c_void_p, 175 | ctypes.c_void_p, 176 | ctypes.c_int] 177 | 178 | self.EVP_DigestFinal = self._lib.EVP_DigestFinal 179 | self.EVP_DigestFinal.restype = ctypes.c_int 180 | self.EVP_DigestFinal.argtypes = [ctypes.c_void_p, 181 | ctypes.c_void_p, ctypes.c_void_p] 182 | 183 | self.EVP_DigestFinal_ex = self._lib.EVP_DigestFinal_ex 184 | self.EVP_DigestFinal_ex.restype = ctypes.c_int 185 | self.EVP_DigestFinal_ex.argtypes = [ctypes.c_void_p, 186 | ctypes.c_void_p, ctypes.c_void_p] 187 | 188 | self.RAND_bytes = self._lib.RAND_bytes 189 | self.RAND_bytes.restype = ctypes.c_int 190 | self.RAND_bytes.argtypes = [ctypes.c_void_p, ctypes.c_int] 191 | 192 | self.EVP_sha256 = self._lib.EVP_sha256 193 | self.EVP_sha256.restype = ctypes.c_void_p 194 | self.EVP_sha256.argtypes = [] 195 | 196 | self.EVP_sha512 = self._lib.EVP_sha512 197 | self.EVP_sha512.restype = ctypes.c_void_p 198 | self.EVP_sha512.argtypes = [] 199 | 200 | self.HMAC = self._lib.HMAC 201 | self.HMAC.restype = ctypes.c_void_p 202 | self.HMAC.argtypes = [ctypes.c_void_p, ctypes.c_void_p, ctypes.c_int, 203 | ctypes.c_void_p, ctypes.c_int, 204 | ctypes.c_void_p, ctypes.c_void_p] 205 | 206 | try: 207 | self.PKCS5_PBKDF2_HMAC = self._lib.PKCS5_PBKDF2_HMAC 208 | except: 209 | # The above is not compatible with all versions of OSX. 210 | self.PKCS5_PBKDF2_HMAC = self._lib.PKCS5_PBKDF2_HMAC_SHA1 211 | self.PKCS5_PBKDF2_HMAC.restype = ctypes.c_int 212 | self.PKCS5_PBKDF2_HMAC.argtypes = [ctypes.c_void_p, ctypes.c_int, 213 | ctypes.c_void_p, ctypes.c_int, 214 | ctypes.c_int, ctypes.c_void_p, 215 | ctypes.c_int, ctypes.c_void_p] 216 | 217 | self._set_ciphers() 218 | 219 | def _set_ciphers(self): 220 | self.cipher_algo = { 221 | 'aes-128-cbc': CipherName('aes-128-cbc', 222 | self.EVP_aes_128_cbc, 223 | 16), 224 | 'aes-256-cbc': CipherName('aes-256-cbc', 225 | self.EVP_aes_256_cbc, 226 | 16), 227 | 'aes-128-cfb': CipherName('aes-128-cfb', 228 | self.EVP_aes_128_cfb128, 229 | 16), 230 | 'aes-256-cfb': CipherName('aes-256-cfb', 231 | self.EVP_aes_256_cfb128, 232 | 16), 233 | 'aes-128-ofb': CipherName('aes-128-ofb', 234 | self._lib.EVP_aes_128_ofb, 235 | 16), 236 | 'aes-256-ofb': CipherName('aes-256-ofb', 237 | self._lib.EVP_aes_256_ofb, 238 | 16), 239 | # 'aes-128-ctr': CipherName('aes-128-ctr', 240 | # self._lib.EVP_aes_128_ctr, 241 | # 16), 242 | # 'aes-256-ctr': CipherName('aes-256-ctr', 243 | # self._lib.EVP_aes_256_ctr, 244 | # 16), 245 | 'bf-cfb': CipherName('bf-cfb', 246 | self.EVP_bf_cfb64, 247 | 8), 248 | 'bf-cbc': CipherName('bf-cbc', 249 | self.EVP_bf_cbc, 250 | 8), 251 | 'rc4': CipherName('rc4', 252 | self.EVP_rc4, 253 | # 128 is the initialisation size not block size 254 | 128), 255 | } 256 | 257 | if hasattr(self, 'EVP_aes_128_ctr'): 258 | self.cipher_algo['aes-128-ctr'] = CipherName( 259 | 'aes-128-ctr', 260 | self._lib.EVP_aes_128_ctr, 261 | 16 262 | ) 263 | if hasattr(self, 'EVP_aes_256_ctr'): 264 | self.cipher_algo['aes-256-ctr'] = CipherName( 265 | 'aes-256-ctr', 266 | self._lib.EVP_aes_256_ctr, 267 | 16 268 | ) 269 | 270 | def get_cipher(self, name): 271 | """ 272 | returns the OpenSSL cipher instance 273 | """ 274 | if name not in self.cipher_algo: 275 | raise Exception("Unknown cipher") 276 | return self.cipher_algo[name] 277 | 278 | def rand(self, size): 279 | """ 280 | OpenSSL random function 281 | """ 282 | buffer = self.malloc(0, size) 283 | if self.RAND_bytes(buffer, size) != 1: 284 | raise RuntimeError("OpenSSL RAND_bytes failed") 285 | return buffer.raw 286 | 287 | def malloc(self, data, size): 288 | """ 289 | returns a create_string_buffer (ctypes) 290 | """ 291 | buffer = None 292 | if data != 0: 293 | if sys.version_info.major == 3 and isinstance(data, type('')): 294 | data = data.encode() 295 | buffer = self.create_string_buffer(data, size) 296 | else: 297 | buffer = self.create_string_buffer(size) 298 | return buffer 299 | 300 | def get_error(self): 301 | return OpenSSL.ERR_error_string(OpenSSL.ERR_get_error(), None) 302 | 303 | 304 | libname = ctypes.util.find_library('crypto') 305 | if libname is None: 306 | # For Windows ... 307 | libname = ctypes.util.find_library('libeay32.dll') 308 | if libname is None: 309 | raise Exception("Couldn't load OpenSSL lib ...") 310 | OpenSSL = _OpenSSL(libname) 311 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (c) 2014 Yann GUIBET . 5 | # All rights reserved. 6 | # 7 | # 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # 1. Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # 15 | # 2. Redistributions in binary form must reproduce the above copyright 16 | # notice, this list of conditions and the following disclaimer in 17 | # the documentation and/or other materials provided with the 18 | # distribution. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 24 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 30 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | from setuptools import setup, find_packages 33 | 34 | setup( 35 | name="pyelliptic", 36 | version='1.5.8', 37 | url='https://github.com/yann2192/pyelliptic', 38 | license='BSD', 39 | description= 40 | "Python OpenSSL wrapper for modern cryptography with " + 41 | "ECC, AES, HMAC, Blowfish, ...", 42 | author='Yann GUIBET', 43 | author_email='yannguibet@gmail.com', 44 | packages=find_packages(), 45 | classifiers=[ 46 | 'Operating System :: Unix', 47 | 'Operating System :: Microsoft :: Windows', 48 | 'Environment :: MacOS X', 49 | 'Programming Language :: Python', 50 | 'Programming Language :: Python :: 2.7', 51 | 'Programming Language :: Python :: 3', 52 | 'Topic :: Security :: Cryptography', 53 | ], 54 | ) 55 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright (c) 2014 Yann GUIBET . 5 | # All rights reserved. 6 | # 7 | # 8 | # Redistribution and use in source and binary forms, with or without 9 | # modification, are permitted provided that the following conditions are 10 | # met: 11 | # 12 | # 1. Redistributions of source code must retain the above copyright 13 | # notice, this list of conditions and the following disclaimer. 14 | # 15 | # 2. Redistributions in binary form must reproduce the above copyright 16 | # notice, this list of conditions and the following disclaimer in 17 | # the documentation and/or other materials provided with the 18 | # distribution. 19 | # 20 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 21 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 22 | # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS 24 | # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 | # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 | # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 27 | # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 | # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 29 | # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN 30 | # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 | 32 | 33 | import unittest 34 | from binascii import hexlify, unhexlify 35 | 36 | from pyelliptic import Cipher 37 | from pyelliptic import hash as _hash 38 | 39 | 40 | class TestCipher(unittest.TestCase): 41 | def setUp(self): 42 | pass 43 | 44 | def test_aes256ctr(self): 45 | ciphername = "aes-256-ctr" 46 | print("\nTEST: AES-256-CTR") 47 | 48 | iv_hex = b"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" 49 | iv = unhexlify(iv_hex) 50 | key_hex = b"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 51 | key = unhexlify(key_hex) 52 | plain_hex = b"6bc1bee22e409f96e93d7e117393172a" 53 | plaintext = unhexlify(plain_hex) 54 | 55 | ctx = Cipher(key, iv, 1, ciphername=ciphername) 56 | enc = ctx.ciphering(plaintext) 57 | print(hexlify(enc)) 58 | 59 | ctx = Cipher(key, iv, 0, ciphername=ciphername) 60 | self.assertEqual(plaintext, ctx.ciphering(enc)) 61 | 62 | 63 | def test_aes256cfb(self): 64 | print("\nTEST: AES-256-CFB") 65 | ciphername = "aes-256-cfb" 66 | key_hex = b"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 67 | key = unhexlify(key_hex) 68 | iv_hex = b"000102030405060708090A0B0C0D0E0F" 69 | iv = unhexlify(iv_hex) 70 | plain_hex = b"6bc1bee22e409f96e93d7e117393172a" 71 | plaintext = unhexlify(plain_hex) 72 | 73 | ctx = Cipher(key, iv, 1, ciphername=ciphername) 74 | enc = ctx.ciphering(plaintext) 75 | print(hexlify(enc)) 76 | 77 | ctx = Cipher(key, iv, 0, ciphername=ciphername) 78 | self.assertEqual(plaintext, ctx.ciphering(enc)) 79 | 80 | def test_aes256cbc(self): 81 | print("\nTEST: AES-256-CBC") 82 | ciphername = "aes-256-cbc" 83 | iv_hex = b"000102030405060708090A0B0C0D0E0F" 84 | iv = unhexlify(iv_hex) 85 | key_hex = b"603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4" 86 | key = unhexlify(key_hex) 87 | plain_hex = b"6bc1bee22e409f96e93d7e117393172a" 88 | plaintext = unhexlify(plain_hex) 89 | 90 | ctx = Cipher(key, iv, 1, ciphername=ciphername) 91 | enc = ctx.ciphering(plaintext) 92 | print(hexlify(enc)) 93 | 94 | ctx = Cipher(key, iv, 0, ciphername=ciphername) 95 | self.assertEqual(plaintext, ctx.ciphering(enc)) 96 | 97 | 98 | class TestEquals(unittest.TestCase): 99 | def setUp(self): 100 | pass 101 | 102 | def test_equals(self): 103 | print("\nTEST: hash.equals") 104 | a = '\xb5\x85/\xe80\xfa\x04\xdf\x07\x83\x17P\x9dw\x02\x89' 105 | 106 | b = '\xb5\x85/\xe80\xfa\x04\xdf\x07\x83\x17P\x9dw\x02\x89' 107 | self.assertTrue(_hash.equals(a, b)) 108 | 109 | b = '\xb4\x85/\xe80\xfa\x04\xdf\x07\x83\x17P\x9dw\x02\x89' 110 | self.assertFalse(_hash.equals(a, b)) 111 | 112 | b = '\xb5\x85/\xe80\xfa\x04\xdf\x07\x83\x17P\x9dw\x02\x90' 113 | self.assertFalse(_hash.equals(a, b)) 114 | 115 | b = '\xb4\x85/\xe80\xfa\x04\xdf\x07\x83\x17P\x9dw\x02' 116 | self.assertFalse(_hash.equals(a, b)) 117 | 118 | 119 | if __name__ == "__main__": 120 | unittest.main() 121 | --------------------------------------------------------------------------------