]> www.wagner.pp.ru Git - oss/ctypescrypto.git/blob - ctypescrypto/cms.py
Converted tabs to spaces to make pylint happy
[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 from ctypes import c_int, c_void_p, c_char_p, c_int
14 from ctypescrypto.exception import LibCryptoError
15 from ctypescrypto import libcrypto
16 from ctypescrypto.bio import Membio
17 from ctypescrypto.oid import Oid
18
19 class CMSError(LibCryptoError):
20     """
21     Exception which is raised when error occurs
22     """
23     pass
24
25 class Flags:
26     """
27     Constants for flags passed to the CMS methods. 
28     Can be OR-ed together
29     """
30     TEXT=1
31     NOCERTS=2
32     NO_CONTENT_VERIFY=4
33     NO_ATTR_VERIFY=8
34     NO_SIGS=NO_CONTENT_VERIFY|NO_ATTR_VERIFY
35     NOINTERN=0x10
36     NO_SIGNER_CERT_VERIFY=0x20
37     NO_VERIFY=0x20
38     DETACHED=0x40
39     BINARY=0x80
40     NOATTR=0x100
41     NOSMIMECAP =0x200
42     NOOLDMIMETYPE=0x400
43     CRLFEOL=0x800
44     STREAM=0x1000
45     NOCRL=0x2000
46     PARTIAL=0x4000
47     REUSE_DIGEST=0x8000
48     USE_KEYID=0x10000
49     DEBUG_DECRYPT=0x20000
50
51 def CMS(data,format="PEM"):
52     """
53     Parses CMS data and returns either SignedData or EnvelopedData
54     object
55     """
56     b=Membio(data)
57     if format == "PEM":
58         ptr=libcrypto.PEM_read_bio_CMS(b.bio,None,None,None)
59     else:
60         ptr=libcrypto.d2i_CMS_bio(b.bio,None)
61     typeoid = Oid(libcrypto.OBJ_obj2nid(libcrypto.CMS_get0_type(ptr)))
62     if typeoid.shortname()=="pkcs7-signedData":
63         return SignedData(ptr)
64     elif typeoid.shortname()=="pkcs7-envelopedData":
65         return EnvelopedData(ptr)
66     elif typeoid.shortname()=="pkcs7-encryptedData":
67         return EncryptedData(ptr)
68     else:
69         raise NotImplementedError("cannot handle "+typeoid.shortname())
70
71 class CMSBase(object): 
72     """
73     Common ancessor for all CMS types.
74     Implements serializatio/deserialization
75     """
76     def __init__(self,ptr=None):
77         self.ptr=ptr
78     def __str__(self):
79         """
80         Serialize in DER format
81         """
82         b=Membio()
83         if not libcrypto.i2d_CMS_bio(b.bio,self.ptr):
84             raise CMSError("writing CMS to PEM")
85         return str(b)
86
87     def pem(self):
88         """
89         Serialize in PEM format
90         """
91         b=Membio()
92         if not libcrypto.PEM_write_bio_CMS(b.bio,self.ptr):
93             raise CMSError("writing CMS to PEM")
94         return str(b)
95         
96     
97         
98 class SignedData(CMSBase):
99     @staticmethod
100     def create(data,cert,pkey,flags=Flags.BINARY,certs=[]):
101         """
102             Creates SignedData message by signing data with pkey and
103             certificate.
104
105             @param data - data to sign
106             @param pkey - pkey object with private key to sign
107             @param flags - OReed combination of Flags constants
108             @param certs - list of X509 objects to include into CMS
109         """
110         if not pkey.cansign:
111             raise ValueError("Specified keypair has no private part")
112         if cert.pubkey!=pkey:
113             raise ValueError("Certificate doesn't match public key")
114         b=Membio(data)
115         if certs is not None and len(certs)>0:
116             certstack=StackOfX509(certs)
117         else:
118             certstack=None
119         ptr=libcrypto.CMS_sign(cert.cert,pkey.ptr,certstack,b.bio,flags)
120         if ptr is None:
121             raise CMSError("signing message")
122         return SignedData(ptr)
123     def sign(self,cert,pkey,md=None,data=None,flags=Flags.BINARY):
124         """
125             Adds another signer to already signed message
126             @param cert - signer's certificate
127             @param pkey - signer's private key
128             @param md - message digest to use as DigestType object 
129                 (if None - default for key would be used)
130             @param data - data to sign (if detached and
131                     Flags.REUSE_DIGEST is not specified)
132             @param flags - ORed combination of Flags consants
133         """
134         if not pkey.cansign:
135             raise ValueError("Specified keypair has no private part")
136         if cert.pubkey!=pkey:
137             raise ValueError("Certificate doesn't match public key")
138         p1=libcrypto.CMS_sign_add1_Signer(self.ptr,cert.cert,pkey.ptr,
139             md.digest,flags)
140         if p1 is None:
141             raise CMSError("adding signer")
142         if flags & Flags.REUSE_DIGEST==0:
143             if data is not None:
144                 b=Membio(data)
145                 biodata=b.bio
146             else:
147                 biodata=None
148             res= libcrypto.CMS_final(self.ptr,biodata,None,flags)
149             if res<=0:
150                 raise CMSError
151     def verify(self,store,flags,data=None,certs=[]):
152         """
153         Verifies signature under CMS message using trusted cert store
154
155         @param store -  X509Store object with trusted certs
156         @param flags - OR-ed combination of flag consants
157         @param data - message data, if messge has detached signature
158         param certs - list of certificates to use during verification
159                 If Flags.NOINTERN is specified, these are only
160                 sertificates to search for signing certificates
161         @returns True if signature valid, False otherwise
162         """
163         bio=None
164         if data!=None:
165             b=Membio(data)
166             bio=b.bio
167         if certs is not None and len(certs)>0:
168             certstack=StackOfX509(certs)
169         else:
170             certstack=None
171         res=libcrypto.CMS_verify(self.ptr,certstack,store.store,bio,None,flags)
172         return res>0
173     @property   
174     def signers(self):
175         """
176         Return list of signer's certificates
177         """
178         p=libcrypto.CMS_get0_signers(self.ptr)
179         if p is None:
180             raise CMSError
181         return StackOfX509(ptr=p,disposable=False)
182     @property
183     def data(self):
184         """
185         Returns signed data if present in the message
186         """
187         b=Membio()
188         if not libcrypto.CMS_verify(self.ptr,None,None,None,b.bio,Flags.NO_VERIFY):
189             raise CMSError("extract data")
190         return str(b)
191     def addcert(self,cert):
192         """
193         Adds a certificate (probably intermediate CA) to the SignedData
194         structure
195         """
196         if libcrypto.CMS_add1_cert(self.ptr,cert.cert)<=0:
197             raise CMSError("adding cert")
198     def addcrl(self,crl):
199         """
200         Adds a CRL to the signed data structure
201         """
202         raise NotImplementedError
203     @property
204     def certs(self):
205         """
206         List of the certificates contained in the structure
207         """
208         p=CMS_get1_certs(self.ptr)
209         if p is None:
210             raise CMSError("getting certs")
211         return StackOfX509(ptr=p,disposable=True)
212     @property
213     def crls(self):
214         """
215         List of the CRLs contained in the structure
216         """
217         raise NotImplementedError
218
219 class EnvelopedData(CMSBase):
220     @staticmethod
221     def create(recipients,data,cipher,flags=0):
222         """
223         Creates and encrypts message
224         @param recipients - list of X509 objects
225         @param data - contents of the message
226         @param cipher - CipherType object
227         @param flags - flag
228         """
229         recp=StackOfX509(recipients)
230         b=Membio(data)
231         p=libcrypto.CMS_encrypt(recp.ptr,b.bio,cipher.cipher_type,flags)
232         if p is None:
233             raise CMSError("encrypt EnvelopedData")
234         return EnvelopedData(p)
235     def decrypt(self,pkey,cert,flags=0):
236         """
237         Decrypts message
238         @param pkey - private key to decrypt
239         @param cert - certificate of this private key (to find
240             neccessary RecipientInfo
241         @param flags - flags
242         @returns - decrypted data
243         """
244         if not pkey.cansign:
245             raise ValueError("Specified keypair has no private part")
246         if pkey != cert.pubkey:
247             raise ValueError("Certificate doesn't match private key")
248         b=Membio()
249         res=libcrypto.CMS_decrypt(self.ptr,pkey.ptr,cert.ccert,None,b.bio,flags)
250         if res<=0:
251             raise CMSError("decrypting CMS")
252         return str(b)
253
254 class EncryptedData(CMSBase):
255     @staticmethod
256     def create(data,cipher,key,flags=0):
257         """
258         Creates an EncryptedData message.
259         @param data data to encrypt
260         @param cipher cipher.CipherType object represening required
261                 cipher type
262         @param key - byte array used as simmetic key
263         @param flags - OR-ed combination of Flags constant
264         """
265         b=Membio(data)
266         ptr=libcrypto.CMS_EncryptedData_encrypt(b.bio,cipher.cipher_type,key,len(key),flags)
267         if ptr is None:
268             raise CMSError("encrypt data")
269         return EncryptedData(ptr)
270     def decrypt(self,key,flags=0):
271         """
272         Decrypts encrypted data message
273         @param key - symmetic key to decrypt
274         @param flags - OR-ed combination of Flags constant
275         """
276         b=Membio()
277         if libcrypto.CMS_EncryptedData_decrypt(self.ptr,key,len(key),None,
278             b.bio,flags)<=0:
279                 raise CMSError("decrypt data")
280         return str(b)
281
282 __all__=['CMS','CMSError','Flags','SignedData','EnvelopedData','EncryptedData']
283
284 libcrypto.CMS_verify.restype=c_int
285 libcrypto.CMS_verify.argtypes=(c_void_p,c_void_p,c_void_p,c_void_p,c_void_p,c_int)