serialization.
Added skeleton for cms module (never run)
--- /dev/null
+"""
+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)
"""\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
"""
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
"""
- 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)
- 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)
- 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.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)
+
+
"""
-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.
"""
-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
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
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
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:
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)
--- /dev/null
+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()
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:
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_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)