]> www.wagner.pp.ru Git - oss/ctypescrypto.git/commitdiff
Added tests for ec key creation from raw material and for private key
authorVictor Wagner <vitus@wagner.pp.ru>
Sun, 26 Oct 2014 11:43:56 +0000 (14:43 +0300)
committerVictor Wagner <vitus@wagner.pp.ru>
Sun, 26 Oct 2014 11:44:10 +0000 (14:44 +0300)
serialization.

Added skeleton for cms module (never run)

ctypescrypto/cms.py [new file with mode: 0644]
ctypescrypto/digest.py
ctypescrypto/ec.py
ctypescrypto/pkey.py
tests/testec.py [new file with mode: 0644]
tests/testpkey.py

diff --git a/ctypescrypto/cms.py b/ctypescrypto/cms.py
new file mode 100644 (file)
index 0000000..9defb1d
--- /dev/null
@@ -0,0 +1,231 @@
+"""
+Implements operations with CMS EnvelopedData and SignedData messages
+
+Contains function CMS() which parses CMS message and creates either
+EnvelopedData or SignedData objects (EncryptedData and CompressedData
+can be easily added, because OpenSSL contain nessesary function)
+
+Each of these objects contains create() static method which is used to
+create it from raw data and neccessary certificates.
+
+
+"""
+
+from ctypescrypto.exception import LibCryptoError
+from ctypescrypto.bio import Membio
+from ctypescrypto.oid import Oid
+
+class CMSError(LibCryptoError):
+       """
+       Exception which is raised when error occurs
+       """
+       pass
+
+class Flags:
+       """
+       Constants for flags passed to the CMS methods. 
+       Can be OR-ed together
+       """
+       TEXT=1
+       NOCERTS=2
+       NO_CONTENT_VERIFY=4
+       NO_ATTR_VERIFY=8
+       NO_SIGS=NO_CONTENT_VERIFY|NO_ATTR_VERIFY
+       NOINTERN=0x10
+       NO_SIGNER_CERT_VERIFY=0x20
+       NO_VERIFY=0x20
+       DETACHED=0x40
+       BINARY=0x80
+       NOATTR=0x100
+       NOSMIMECAP =0x200
+       NOOLDMIMETYPE=0x400
+       CRLFEOL=0x800
+       STREAM=0x1000
+       NOCRL=0x2000
+       PARTIAL=0x4000
+       REUSE_DIGEST=0x8000
+       USE_KEYID=0x10000
+       DEBUG_DECRYPT=0x20000
+
+def CMS(data,format="PEM"):
+       """
+       Parses CMS data and returns either SignedData or EnvelopedData
+       object
+       """
+       b=Membio(data)
+       if format == "PEM":
+               ptr=libcrypto.PEM_read_bio_CMS(b.bio,None,None,None)
+       else:
+               ptr=libcrypto.d2i_CMS_bio(b.bio,None)
+       typeoid = Oid(libcrypto.OBJ_obj2nid(libcrypto.CMS_get0_type(ptr)))
+       if typeoid.shortname()=="pkcs7-signedData":
+               return SignedData(ptr)
+       elif typeoid.shortname()=="pkcs7-envelopedData":
+               return EnvelopedData(ptr)
+       else:
+               raise NotImplementedError("cannot handle "+typeoid.shortname())
+
+
+       
+               
+class SignedData:
+       def __init__(self,ptr=None):
+               self.ptr=ptr
+       @staticmethod
+       def create(data,cert,pkey,flags=Flags.BINARY):
+               """
+                       Creates SignedData message by signing data with pkey and
+                       certificate.
+
+                       @param data - data to sign
+                       @param pkey - pkey object with private key to sign
+               """
+               if not pkey.cansign:
+                       raise ValueError("Specified keypair has no private part")
+               if cert.pubkey!=pkey:
+                       raise ValueError("Certificate doesn't match public key")
+               b=Membio(data)
+               ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,None,b.bio,flags)
+               if ptr is None:
+                       raise CMSError("signing message")
+               return SignedData(ptr)
+       def sign(self,cert,pkey,md=None,data=None,flags=Flags.BINARY):
+               """
+                       Adds another signer to already signed message
+                       @param cert - signer's certificate
+                       @param pkey - signer's private key
+                       @param data - data to sign (if detached)
+                       @param md - message digest to use as DigestType object (if None - default for key
+                               would be used)
+                       @param flags - flags
+               """
+               if not pkey.cansign:
+                       raise ValueError("Specified keypair has no private part")
+               if cert.pubkey!=pkey:
+                       raise ValueError("Certificate doesn't match public key")
+               p1=libcrypto.CMS_sign_add1_Signer(self.ptr,cert.cert,pkey.ptr,
+                       md.digest,flags)
+               if p1 is None:
+                       raise CMSError("adding signer")
+               if flags & Flags.REUSE_DIGEST==0:
+                       if data is not None:
+                               b=Membio(data)
+                               biodata=b.bio
+                       else:
+                               biodata=None
+                       res= libcrypto.CMS_final(self.ptr,biodata,None,flags)
+                       if res<=0:
+                               raise CMSError
+       def verify(self,store,flags,data=None):
+               bio=None
+               if data!=None:
+                       b=Membio(data)
+                       bio=b.bio
+               res=libcrypto.CMS_verify(self.ptr,store.store,bio,None,flags)
+               return res>0
+       def __str__(self):
+               """
+               Serialize in DER format
+               """
+               b=Membio()
+               if not libcrypto.i2d_CMS_bio(b.bio,self.ptr):
+                       raise CMSError("writing CMS to PEM")
+               return str(b)
+
+       def pem(self):
+               """
+               Serialize in PEM format
+               """
+               b=Membio()
+               if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr):
+                       raise CMSError("writing CMS to PEM")
+               return str(b)
+               
+       @property       
+       def signers(self,store=None):
+               """
+               Return list of signer's certificates
+               """
+               raise NotImplementedError
+       @property
+       def data(self):
+               """
+               Returns signed data if present
+               """
+               raise NotImplementedError
+       def addcert(self,cert):
+               """
+               Adds a certificate (probably intermediate CA) to the SignedData
+               structure
+               """
+               raise NotImplementedError
+       def addcrl(self,crl):
+               """
+               Adds a CRL to the signed data structure
+               """
+               raise NotImplementedError
+       @property
+       def certs(self):
+               """
+               List of the certificates contained in the structure
+               """
+               raise NotImplementedError
+       @property
+       def crls(self):
+               """
+               List of the CRLs contained in the structure
+               """
+               raise NotImplementedError
+
+class EnvelopedData:
+       def __init__(self,ptr):
+               """
+               Initializes an object. For internal use
+               """
+               self.ptr=ptr
+       def __str__(self):
+               """
+               Serialize in DER format
+               """
+               b=Membio()
+               if not libcrypto.i2d_CMS_bio(b.bio,self.ptr):
+                       raise CMSError("writing CMS to PEM")
+               return str(b)
+
+       def pem(self):
+               """
+               Serialize in PEM format
+               """
+               b=Membio()
+               if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr):
+                       raise CMSError("writing CMS to PEM")
+               return str(b)
+       @staticmethod
+       def create(recipients,data,cipher,flags=0):
+               """
+               Creates and encrypts message
+               @param recipients - list of X509 objects
+               @param data - contents of the message
+               @param cipher - CipherType object
+               @param flags - flag
+               """
+               # Need to be able to handle OPENSSL stacks
+               raise NotImplementedError       
+       def decrypt(self,pkey,cert,flags=0):
+               """
+               Decrypts message
+               @param pkey - private key to decrypt
+               @param cert - certificate of this private key (to find
+                       neccessary RecipientInfo
+               @param flags - flags
+               @returns - decrypted data
+               """
+               if not pkey.cansign:
+                       raise ValueError("Specified keypair has no private part")
+               if pkey != cert.pubkey:
+                       raise ValueError("Certificate doesn't match private key")
+               b=Membio()
+               res=libcrypto.CMS_decrypt(self.ptr,pkey.ptr,cert.ccert,None,b.bio,flags)
+               if res<=0:
+                       raise CMSError("decrypting CMS")
+               return str(b)
index 4e33d92bb3e147b28a05fd22dd18ef315078dff2..df4e58071a13c98e96b05e56710e86141407bd5b 100644 (file)
@@ -1,5 +1,6 @@
 """\r
 """\r
-       Implmenets interface to OpenSSL EVP_Digest* functions.\r
+       Implements interface to OpenSSL EVP_Digest* functions.\r
+\r
        Interface  made as close to hashlib as possible.\r
 \r
        This module is really an excess effort. Hashlib allows access to\r
        Interface  made as close to hashlib as possible.\r
 \r
        This module is really an excess effort. Hashlib allows access to\r
index d325212c7781d196b1d416575436a5e1845005fe..3d880cb6a5c174ec671b24e42de555ec11680dab 100644 (file)
@@ -1,33 +1,74 @@
 """
 Support for EC keypair operation missing form public libcrypto API
 """
 """
 Support for EC keypair operation missing form public libcrypto API
 """
+from ctypescrypto.pkey import PKey, PKeyError
+from ctypes import c_void_p,c_char_p,c_int,byref
+from ctypescrypto import libcrypto
 
 
-
-def create(curve,num):
+def create(curve,data):
        """
                Creates EC keypair from the just secret key and curve name
                
                @param curve - name of elliptic curve
                @param num - long number representing key
        """
        """
                Creates EC keypair from the just secret key and curve name
                
                @param curve - name of elliptic curve
                @param num - long number representing key
        """
-       p=libcrypto.EVP_PKEY_new()
-       ec=libcrypto.EC_KEY_new_by_curvename(curve.nid)
+       ec=libcrypto.EC_KEY_new_by_curve_name(curve.nid)
+       if ec is None:  
+               raise PKeyError("EC_KEY_new_by_curvename")
        group=libcrypto.EC_KEY_get0_group(ec)
        group=libcrypto.EC_KEY_get0_group(ec)
-       EC_KEY_set_private_key(ec,bn)
-       priv_key=libcrypt.BN_new()
-       ctx=BN_CTX_new()
-       h="%x"%(num)
-       libcrypto.BN_hex2bn(byref(priv_key),h)
-       libcrypto.EC_KEY_set_private_key(ec,priv_key)
+       if group is None:
+               raise PKeyError("EC_KEY_get0_group")
+       libcrypto.EC_GROUP_set_asn1_flag(group,1)
+       raw_key=libcrypto.BN_new()
+       if raw_key is None:
+               raise PKeyError("BN_new")
+       ctx=libcrypto.BN_CTX_new()
+       if ctx is None:
+               raise PKeyError("BN_CTX_new")
+       if libcrypto.BN_bin2bn(data,len(data),raw_key) is None:
+               raise PKeyError("BN_bin2bn")
+       order=libcrypto.BN_new()
+       if order is None:
+               raise PKeyError("BN_new")
+       priv_key = libcrypto.BN_new()
+       if priv_key is None:
+               raise PKeyError("BN_new")
+       if libcrypto.EC_GROUP_get_order(group,order,ctx) <=0:
+               raise PKeyError("EC_GROUP_get_order")
+       if libcrypto.BN_nnmod(priv_key,raw_key,order,ctx) <=0:
+               raise PKeyError("BN_nnmod")
+       if libcrypto.EC_KEY_set_private_key(ec,priv_key)<=0:
+               raise PKeyError("EC_KEY_set_private_key")
        pub_key=libcrypto.EC_POINT_new(group)
        pub_key=libcrypto.EC_POINT_new(group)
-       libcrypto.EC_POINT_mul(group,pub_key,priv_key,None,None,ctx)
-       libcrypto.BN_free(a)
-       libcrypto.EVP_PKEY_set1_EC_KEY(p,ec)
+       if pub_key is None:
+               raise PKeyError("EC_POINT_new")
+       if libcrypto.EC_POINT_mul(group,pub_key,priv_key,None,None,ctx)<=0:
+               raise PKeyError("EC_POINT_mul")
+       if libcrypto.EC_KEY_set_public_key(ec,pub_key)<=0:
+               raise PKeyError("EC_KEY_set_public_key")
+       libcrypto.BN_free(raw_key)
+       libcrypto.BN_free(order)
+       libcrypto.BN_free(priv_key)
+       libcrypto.BN_CTX_free(ctx)
+       p=libcrypto.EVP_PKEY_new()
+       if p is None:
+               raise PKeyError("EVP_PKEY_new") 
+       if libcrypto.EVP_PKEY_set1_EC_KEY(p,ec)<=0:
+               raise PKeyError("EVP_PKEY_set1_EC_KEY")
        libcrypto.EC_KEY_free(ec)
        return PKey(ptr=p,cansign=True)
 
 
 libcrypto.EVP_PKEY_new.restype=c_void_p
 libcrypto.BN_new.restype=c_void_p
        libcrypto.EC_KEY_free(ec)
        return PKey(ptr=p,cansign=True)
 
 
 libcrypto.EVP_PKEY_new.restype=c_void_p
 libcrypto.BN_new.restype=c_void_p
-libcrypto.BN_hex2bn.argtypes(POINTER(c_void_p),c_char_p)
-libcrypto.EC_KEY_set_private_key
+libcrypto.BN_free.argtypes=(c_void_p,)
+libcrypto.BN_CTX_new.restype=c_void_p
+libcrypto.BN_CTX_free.argtypes=(c_void_p,)
+libcrypto.BN_bin2bn.argtypes=(c_char_p,c_int,c_void_p)
+libcrypto.EC_KEY_set_private_key.argtypes=(c_void_p,c_void_p)
+libcrypto.EC_POINT_new.argtypes=(c_void_p,)
+libcrypto.EC_POINT_new.restype=c_void_p
+libcrypto.EC_POINT_mul.argtypes=(c_void_p,c_void_p,c_void_p,c_void_p,c_void_p,c_void_p)
+libcrypto.EC_KEY_set_public_key.argtypes=(c_void_p,c_void_p)
+
+
index f011443a2173ede8058e21c4323ba2967462ace2..10366ee0b318e1947b4448a1be4f9419884af5b3 100644 (file)
@@ -1,10 +1,9 @@
 """
 """
-low-level private/public keypair operation
+This module provides interface for low-level private/public keypair operation
 
 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
 """
 
 
 PKey object of this module is wrapper around OpenSSL EVP_PKEY object.
 """
 
-This module provides interface for 
 
 from ctypes import c_char_p,c_void_p,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER
 from ctypescrypto import libcrypto
 
 from ctypes import c_char_p,c_void_p,byref,c_int,c_long, c_longlong, create_string_buffer,CFUNCTYPE,POINTER
 from ctypescrypto import libcrypto
@@ -16,11 +15,11 @@ class PKeyError(LibCryptoError):
 
 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
 def password_callback(buf,length,rwflag,u):
 
 CALLBACK_FUNC=CFUNCTYPE(c_int,c_char_p,c_int,c_int,c_char_p)
 def password_callback(buf,length,rwflag,u):
-"""
-Example password callback for private key. Assumes that 
-password is store in the userdata parameter, so allows to pass password
-from constructor arguments to the libcrypto keyloading functions
-"""
+       """
+       Example password callback for private key. Assumes that 
+       password is store in the userdata parameter, so allows to pass password
+       from constructor arguments to the libcrypto keyloading functions
+       """
        cnt=len(u)
        if length<cnt:
                cnt=length
        cnt=len(u)
        if length<cnt:
                cnt=length
@@ -202,7 +201,7 @@ class PKey:
                return str(b)
        def exportpriv(self,format="PEM",password=None,cipher=None):
                """
                return str(b)
        def exportpriv(self,format="PEM",password=None,cipher=None):
                """
-                       Returns public key as PEM or DER Structure.
+                       Returns private key as PEM or DER Structure.
                        If password and cipher are specified, encrypts key
                        on given password, using given algorithm. Cipher must be
                        an ctypescrypto.cipher.CipherType object
                        If password and cipher are specified, encrypts key
                        on given password, using given algorithm. Cipher must be
                        an ctypescrypto.cipher.CipherType object
@@ -215,7 +214,7 @@ class PKey:
                                raise NotImplementedError("Interactive password entry is not supported")
                        evp_cipher=cipher.cipher
                if format == "PEM":
                                raise NotImplementedError("Interactive password entry is not supported")
                        evp_cipher=cipher.cipher
                if format == "PEM":
-                       r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,_cb,
+                       r=libcrypto.PEM_write_bio_PrivateKey(b.bio,self.key,evp_cipher,None,0,_cb,
                                password)
                else:
                        if cipher is not None:
                                password)
                else:
                        if cipher is not None:
@@ -286,7 +285,7 @@ libcrypto.EVP_PKEY_verify.restype=c_int
 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
 libcrypto.EVP_PKEY_verify_init.restype=c_int
 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
 libcrypto.EVP_PKEY_verify.argtypes=(c_void_p,c_char_p,c_long,c_char_p,c_long)
 libcrypto.EVP_PKEY_verify_init.restype=c_int
 libcrypto.EVP_PKEY_verify_init.argtypes=(c_void_p,)
-libcrypto.PEM_write_bio_PrivateKey.argtypes=(c_void_p,c_void_p,CALLBACK_FUNC,c_char_p)
+libcrypto.PEM_write_bio_PrivateKey.argtypes=(c_void_p,c_void_p,c_void_p,c_char_p,c_int,CALLBACK_FUNC,c_char_p)
 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
 libcrypto.PEM_write_bio_PUBKEY.argtypes=(c_void_p,c_void_p)
 libcrypto.i2d_PUBKEY_bio.argtypes=(c_void_p,c_void_p)
 libcrypto.i2d_PrivateKey_bio.argtypes=(c_void_p,c_void_p)
diff --git a/tests/testec.py b/tests/testec.py
new file mode 100644 (file)
index 0000000..496a747
--- /dev/null
@@ -0,0 +1,32 @@
+from ctypescrypto.oid import Oid
+from ctypescrypto.ec import create
+from base64 import b16decode
+import unittest
+
+
+
+class TestEcCreation(unittest.TestCase):
+       ec1priv="""-----BEGIN PRIVATE KEY-----
+MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgKnG6neqZvB98EEuuxnHs
+fv+L/5abuNNG20wzUqRpncOhRANCAARWKXWeUZ6WiCKZ2kHx87jmJyx0G3ZB1iQC
++Gp2AJYswbQPhGPigKolzIbZYfwnn7QOca6N8QDhPAn3QQK8trZI
+-----END PRIVATE KEY-----
+"""
+       bigkey="""-----BEGIN PRIVATE KEY-----
+MHUCAQAwEAYHKoZIzj0CAQYFK4EEAAoEXjBcAgEBBBEBRVEjGVC3X8RALaFzL8m+
+vqFEA0IABJFmwom5+QXlX549+fadfzVrSiIJX4lPRxVxSqS1Zgav8YHrlmvkrLXP
++eFrZtgJvpTiFPBsk/0JEJmvmEmSVec=
+-----END PRIVATE KEY-----
+"""
+       def test_keyone(self):
+               key=create(Oid("secp256k1"),b16decode("2A71BA9DEA99BC1F7C104BAEC671EC7EFF8BFF969BB8D346DB4C3352A4699DC3",True))
+                       
+               out=key.exportpriv()
+               self.assertEqual(out,self.ec1priv)
+
+       def test_bignum(self):
+               keyval='\xff'*32
+               key=create(Oid("secp256k1"),keyval)
+               self.assertEqual(key.exportpriv(),self.bigkey)
+if __name__ == "__main__":
+       unittest.main()
index c54b3e179c6ffb646781aaf6c2523d8f96567c77..00aee7a506bd9445518a57ffd18a62e87189efb9 100644 (file)
@@ -39,11 +39,11 @@ Modulus:
     1b:a4:85:ab:b0:87:7b:78:2f
 Exponent: 65537 (0x10001)
 """
     1b:a4:85:ab:b0:87:7b:78:2f
 Exponent: 65537 (0x10001)
 """
-       ec1priv="""-----BEGIN EC PRIVATE KEY-----
-MHQCAQEEICpxup3qmbwffBBLrsZx7H7/i/+Wm7jTRttMM1KkaZ3DoAcGBSuBBAAK
-oUQDQgAEVil1nlGelogimdpB8fO45icsdBt2QdYkAvhqdgCWLMG0D4Rj4oCqJcyG
-2WH8J5+0DnGujfEA4TwJ90ECvLa2SA==
------END EC PRIVATE KEY-----
+       ec1priv="""-----BEGIN PRIVATE KEY-----
+MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgKnG6neqZvB98EEuuxnHs
+fv+L/5abuNNG20wzUqRpncOhRANCAARWKXWeUZ6WiCKZ2kHx87jmJyx0G3ZB1iQC
++Gp2AJYswbQPhGPigKolzIbZYfwnn7QOca6N8QDhPAn3QQK8trZI
+-----END PRIVATE KEY-----
 """
        ec1keytext="""Public-Key: (256 bit)
 pub: 
 """
        ec1keytext="""Public-Key: (256 bit)
 pub: 
@@ -59,10 +59,15 @@ MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAEVil1nlGelogimdpB8fO45icsdBt2QdYk
 AvhqdgCWLMG0D4Rj4oCqJcyG2WH8J5+0DnGujfEA4TwJ90ECvLa2SA==
 -----END PUBLIC KEY-----
 """
 AvhqdgCWLMG0D4Rj4oCqJcyG2WH8J5+0DnGujfEA4TwJ90ECvLa2SA==
 -----END PUBLIC KEY-----
 """
+       
        def test_unencrypted_pem(self):
                key=PKey(privkey=self.rsa)
                self.assertIsNotNone(key.key)
                self.assertEqual(str(key),self.rsakeytext)
        def test_unencrypted_pem(self):
                key=PKey(privkey=self.rsa)
                self.assertIsNotNone(key.key)
                self.assertEqual(str(key),self.rsakeytext)
+       def test_export_priv_pem(self):
+               key=PKey(privkey=self.ec1priv)
+               out=key.exportpriv()
+               self.assertEqual(self.ec1priv,out)
        def test_unencrypted_pem_ec(self):
                
                key=PKey(privkey=self.ec1priv)
        def test_unencrypted_pem_ec(self):
                
                key=PKey(privkey=self.ec1priv)