]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/cms.py
Added tests for ec key creation from raw material and for private key
[oss/ctypescrypto.git] / ctypescrypto / cms.py
1 """
2 Implements operations with CMS EnvelopedData and SignedData messages
3
4 Contains function CMS() which parses CMS message and creates either
5 EnvelopedData or SignedData objects (EncryptedData and CompressedData
6 can be easily added, because OpenSSL contain nessesary function)
7
8 Each of these objects contains create() static method which is used to
9 create it from raw data and neccessary certificates.
10
11
12 """
13
14 from ctypescrypto.exception import LibCryptoError
15 from ctypescrypto.bio import Membio
16 from ctypescrypto.oid import Oid
17
18 class CMSError(LibCryptoError):
19         """
20         Exception which is raised when error occurs
21         """
22         pass
23
24 class Flags:
25         """
26         Constants for flags passed to the CMS methods. 
27         Can be OR-ed together
28         """
29         TEXT=1
30         NOCERTS=2
31         NO_CONTENT_VERIFY=4
32         NO_ATTR_VERIFY=8
33         NO_SIGS=NO_CONTENT_VERIFY|NO_ATTR_VERIFY
34         NOINTERN=0x10
35         NO_SIGNER_CERT_VERIFY=0x20
36         NO_VERIFY=0x20
37         DETACHED=0x40
38         BINARY=0x80
39         NOATTR=0x100
40         NOSMIMECAP =0x200
41         NOOLDMIMETYPE=0x400
42         CRLFEOL=0x800
43         STREAM=0x1000
44         NOCRL=0x2000
45         PARTIAL=0x4000
46         REUSE_DIGEST=0x8000
47         USE_KEYID=0x10000
48         DEBUG_DECRYPT=0x20000
49
50 def CMS(data,format="PEM"):
51         """
52         Parses CMS data and returns either SignedData or EnvelopedData
53         object
54         """
55         b=Membio(data)
56         if format == "PEM":
57                 ptr=libcrypto.PEM_read_bio_CMS(b.bio,None,None,None)
58         else:
59                 ptr=libcrypto.d2i_CMS_bio(b.bio,None)
60         typeoid = Oid(libcrypto.OBJ_obj2nid(libcrypto.CMS_get0_type(ptr)))
61         if typeoid.shortname()=="pkcs7-signedData":
62                 return SignedData(ptr)
63         elif typeoid.shortname()=="pkcs7-envelopedData":
64                 return EnvelopedData(ptr)
65         else:
66                 raise NotImplementedError("cannot handle "+typeoid.shortname())
67
68
69         
70                 
71 class SignedData:
72         def __init__(self,ptr=None):
73                 self.ptr=ptr
74         @staticmethod
75         def create(data,cert,pkey,flags=Flags.BINARY):
76                 """
77                         Creates SignedData message by signing data with pkey and
78                         certificate.
79
80                         @param data - data to sign
81                         @param pkey - pkey object with private key to sign
82                 """
83                 if not pkey.cansign:
84                         raise ValueError("Specified keypair has no private part")
85                 if cert.pubkey!=pkey:
86                         raise ValueError("Certificate doesn't match public key")
87                 b=Membio(data)
88                 ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,None,b.bio,flags)
89                 if ptr is None:
90                         raise CMSError("signing message")
91                 return SignedData(ptr)
92         def sign(self,cert,pkey,md=None,data=None,flags=Flags.BINARY):
93                 """
94                         Adds another signer to already signed message
95                         @param cert - signer's certificate
96                         @param pkey - signer's private key
97                         @param data - data to sign (if detached)
98                         @param md - message digest to use as DigestType object (if None - default for key
99                                 would be used)
100                         @param flags - flags
101                 """
102                 if not pkey.cansign:
103                         raise ValueError("Specified keypair has no private part")
104                 if cert.pubkey!=pkey:
105                         raise ValueError("Certificate doesn't match public key")
106                 p1=libcrypto.CMS_sign_add1_Signer(self.ptr,cert.cert,pkey.ptr,
107                         md.digest,flags)
108                 if p1 is None:
109                         raise CMSError("adding signer")
110                 if flags & Flags.REUSE_DIGEST==0:
111                         if data is not None:
112                                 b=Membio(data)
113                                 biodata=b.bio
114                         else:
115                                 biodata=None
116                         res= libcrypto.CMS_final(self.ptr,biodata,None,flags)
117                         if res<=0:
118                                 raise CMSError
119         def verify(self,store,flags,data=None):
120                 bio=None
121                 if data!=None:
122                         b=Membio(data)
123                         bio=b.bio
124                 res=libcrypto.CMS_verify(self.ptr,store.store,bio,None,flags)
125                 return res>0
126         def __str__(self):
127                 """
128                 Serialize in DER format
129                 """
130                 b=Membio()
131                 if not libcrypto.i2d_CMS_bio(b.bio,self.ptr):
132                         raise CMSError("writing CMS to PEM")
133                 return str(b)
134
135         def pem(self):
136                 """
137                 Serialize in PEM format
138                 """
139                 b=Membio()
140                 if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr):
141                         raise CMSError("writing CMS to PEM")
142                 return str(b)
143                 
144         @property       
145         def signers(self,store=None):
146                 """
147                 Return list of signer's certificates
148                 """
149                 raise NotImplementedError
150         @property
151         def data(self):
152                 """
153                 Returns signed data if present
154                 """
155                 raise NotImplementedError
156         def addcert(self,cert):
157                 """
158                 Adds a certificate (probably intermediate CA) to the SignedData
159                 structure
160                 """
161                 raise NotImplementedError
162         def addcrl(self,crl):
163                 """
164                 Adds a CRL to the signed data structure
165                 """
166                 raise NotImplementedError
167         @property
168         def certs(self):
169                 """
170                 List of the certificates contained in the structure
171                 """
172                 raise NotImplementedError
173         @property
174         def crls(self):
175                 """
176                 List of the CRLs contained in the structure
177                 """
178                 raise NotImplementedError
179
180 class EnvelopedData:
181         def __init__(self,ptr):
182                 """
183                 Initializes an object. For internal use
184                 """
185                 self.ptr=ptr
186         def __str__(self):
187                 """
188                 Serialize in DER format
189                 """
190                 b=Membio()
191                 if not libcrypto.i2d_CMS_bio(b.bio,self.ptr):
192                         raise CMSError("writing CMS to PEM")
193                 return str(b)
194
195         def pem(self):
196                 """
197                 Serialize in PEM format
198                 """
199                 b=Membio()
200                 if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr):
201                         raise CMSError("writing CMS to PEM")
202                 return str(b)
203         @staticmethod
204         def create(recipients,data,cipher,flags=0):
205                 """
206                 Creates and encrypts message
207                 @param recipients - list of X509 objects
208                 @param data - contents of the message
209                 @param cipher - CipherType object
210                 @param flags - flag
211                 """
212                 # Need to be able to handle OPENSSL stacks
213                 raise NotImplementedError       
214         def decrypt(self,pkey,cert,flags=0):
215                 """
216                 Decrypts message
217                 @param pkey - private key to decrypt
218                 @param cert - certificate of this private key (to find
219                         neccessary RecipientInfo
220                 @param flags - flags
221                 @returns - decrypted data
222                 """
223                 if not pkey.cansign:
224                         raise ValueError("Specified keypair has no private part")
225                 if pkey != cert.pubkey:
226                         raise ValueError("Certificate doesn't match private key")
227                 b=Membio()
228                 res=libcrypto.CMS_decrypt(self.ptr,pkey.ptr,cert.ccert,None,b.bio,flags)
229                 if res<=0:
230                         raise CMSError("decrypting CMS")
231                 return str(b)