|
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
3 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
5 |
|
6 #include "nsCMS.h" |
|
7 |
|
8 #include "CertVerifier.h" |
|
9 #include "pkix/pkixtypes.h" |
|
10 #include "nsISupports.h" |
|
11 #include "nsNSSHelper.h" |
|
12 #include "nsNSSCertificate.h" |
|
13 #include "smime.h" |
|
14 #include "cms.h" |
|
15 #include "nsICMSMessageErrors.h" |
|
16 #include "nsIArray.h" |
|
17 #include "nsArrayUtils.h" |
|
18 #include "nsCertVerificationThread.h" |
|
19 |
|
20 #include "prlog.h" |
|
21 |
|
22 using namespace mozilla; |
|
23 using namespace mozilla::psm; |
|
24 |
|
25 #ifdef PR_LOGGING |
|
26 extern PRLogModuleInfo* gPIPNSSLog; |
|
27 #endif |
|
28 |
|
29 using namespace mozilla; |
|
30 |
|
31 NS_IMPL_ISUPPORTS(nsCMSMessage, nsICMSMessage, nsICMSMessage2) |
|
32 |
|
33 nsCMSMessage::nsCMSMessage() |
|
34 { |
|
35 m_cmsMsg = nullptr; |
|
36 } |
|
37 nsCMSMessage::nsCMSMessage(NSSCMSMessage *aCMSMsg) |
|
38 { |
|
39 m_cmsMsg = aCMSMsg; |
|
40 } |
|
41 |
|
42 nsCMSMessage::~nsCMSMessage() |
|
43 { |
|
44 nsNSSShutDownPreventionLock locker; |
|
45 if (isAlreadyShutDown()) { |
|
46 return; |
|
47 } |
|
48 destructorSafeDestroyNSSReference(); |
|
49 shutdown(calledFromObject); |
|
50 } |
|
51 |
|
52 void nsCMSMessage::virtualDestroyNSSReference() |
|
53 { |
|
54 destructorSafeDestroyNSSReference(); |
|
55 } |
|
56 |
|
57 void nsCMSMessage::destructorSafeDestroyNSSReference() |
|
58 { |
|
59 if (m_cmsMsg) { |
|
60 NSS_CMSMessage_Destroy(m_cmsMsg); |
|
61 } |
|
62 } |
|
63 |
|
64 NS_IMETHODIMP nsCMSMessage::VerifySignature() |
|
65 { |
|
66 return CommonVerifySignature(nullptr, 0); |
|
67 } |
|
68 |
|
69 NSSCMSSignerInfo* nsCMSMessage::GetTopLevelSignerInfo() |
|
70 { |
|
71 nsNSSShutDownPreventionLock locker; |
|
72 if (isAlreadyShutDown()) |
|
73 return nullptr; |
|
74 |
|
75 if (!m_cmsMsg) |
|
76 return nullptr; |
|
77 |
|
78 if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) |
|
79 return nullptr; |
|
80 |
|
81 NSSCMSContentInfo *cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); |
|
82 if (!cinfo) |
|
83 return nullptr; |
|
84 |
|
85 NSSCMSSignedData *sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); |
|
86 if (!sigd) |
|
87 return nullptr; |
|
88 |
|
89 PR_ASSERT(NSS_CMSSignedData_SignerInfoCount(sigd) > 0); |
|
90 return NSS_CMSSignedData_GetSignerInfo(sigd, 0); |
|
91 } |
|
92 |
|
93 NS_IMETHODIMP nsCMSMessage::GetSignerEmailAddress(char * * aEmail) |
|
94 { |
|
95 nsNSSShutDownPreventionLock locker; |
|
96 if (isAlreadyShutDown()) |
|
97 return NS_ERROR_NOT_AVAILABLE; |
|
98 |
|
99 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerEmailAddress\n")); |
|
100 NS_ENSURE_ARG(aEmail); |
|
101 |
|
102 NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); |
|
103 if (!si) |
|
104 return NS_ERROR_FAILURE; |
|
105 |
|
106 *aEmail = NSS_CMSSignerInfo_GetSignerEmailAddress(si); |
|
107 return NS_OK; |
|
108 } |
|
109 |
|
110 NS_IMETHODIMP nsCMSMessage::GetSignerCommonName(char ** aName) |
|
111 { |
|
112 nsNSSShutDownPreventionLock locker; |
|
113 if (isAlreadyShutDown()) |
|
114 return NS_ERROR_NOT_AVAILABLE; |
|
115 |
|
116 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCommonName\n")); |
|
117 NS_ENSURE_ARG(aName); |
|
118 |
|
119 NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); |
|
120 if (!si) |
|
121 return NS_ERROR_FAILURE; |
|
122 |
|
123 *aName = NSS_CMSSignerInfo_GetSignerCommonName(si); |
|
124 return NS_OK; |
|
125 } |
|
126 |
|
127 NS_IMETHODIMP nsCMSMessage::ContentIsEncrypted(bool *isEncrypted) |
|
128 { |
|
129 nsNSSShutDownPreventionLock locker; |
|
130 if (isAlreadyShutDown()) |
|
131 return NS_ERROR_NOT_AVAILABLE; |
|
132 |
|
133 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::ContentIsEncrypted\n")); |
|
134 NS_ENSURE_ARG(isEncrypted); |
|
135 |
|
136 if (!m_cmsMsg) |
|
137 return NS_ERROR_FAILURE; |
|
138 |
|
139 *isEncrypted = NSS_CMSMessage_IsEncrypted(m_cmsMsg); |
|
140 |
|
141 return NS_OK; |
|
142 } |
|
143 |
|
144 NS_IMETHODIMP nsCMSMessage::ContentIsSigned(bool *isSigned) |
|
145 { |
|
146 nsNSSShutDownPreventionLock locker; |
|
147 if (isAlreadyShutDown()) |
|
148 return NS_ERROR_NOT_AVAILABLE; |
|
149 |
|
150 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::ContentIsSigned\n")); |
|
151 NS_ENSURE_ARG(isSigned); |
|
152 |
|
153 if (!m_cmsMsg) |
|
154 return NS_ERROR_FAILURE; |
|
155 |
|
156 *isSigned = NSS_CMSMessage_IsSigned(m_cmsMsg); |
|
157 |
|
158 return NS_OK; |
|
159 } |
|
160 |
|
161 NS_IMETHODIMP nsCMSMessage::GetSignerCert(nsIX509Cert **scert) |
|
162 { |
|
163 nsNSSShutDownPreventionLock locker; |
|
164 if (isAlreadyShutDown()) |
|
165 return NS_ERROR_NOT_AVAILABLE; |
|
166 |
|
167 NSSCMSSignerInfo *si = GetTopLevelSignerInfo(); |
|
168 if (!si) |
|
169 return NS_ERROR_FAILURE; |
|
170 |
|
171 if (si->cert) { |
|
172 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCert got signer cert\n")); |
|
173 |
|
174 *scert = nsNSSCertificate::Create(si->cert); |
|
175 if (*scert) { |
|
176 (*scert)->AddRef(); |
|
177 } |
|
178 } |
|
179 else { |
|
180 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::GetSignerCert no signer cert, do we have a cert list? %s\n", |
|
181 (si->certList ? "yes" : "no") )); |
|
182 |
|
183 *scert = nullptr; |
|
184 } |
|
185 |
|
186 return NS_OK; |
|
187 } |
|
188 |
|
189 NS_IMETHODIMP nsCMSMessage::GetEncryptionCert(nsIX509Cert **ecert) |
|
190 { |
|
191 nsNSSShutDownPreventionLock locker; |
|
192 if (isAlreadyShutDown()) |
|
193 return NS_ERROR_NOT_AVAILABLE; |
|
194 |
|
195 return NS_ERROR_NOT_IMPLEMENTED; |
|
196 } |
|
197 |
|
198 NS_IMETHODIMP nsCMSMessage::VerifyDetachedSignature(unsigned char* aDigestData, uint32_t aDigestDataLen) |
|
199 { |
|
200 if (!aDigestData || !aDigestDataLen) |
|
201 return NS_ERROR_FAILURE; |
|
202 |
|
203 return CommonVerifySignature(aDigestData, aDigestDataLen); |
|
204 } |
|
205 |
|
206 nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, uint32_t aDigestDataLen) |
|
207 { |
|
208 nsNSSShutDownPreventionLock locker; |
|
209 if (isAlreadyShutDown()) |
|
210 return NS_ERROR_NOT_AVAILABLE; |
|
211 |
|
212 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature, content level count %d\n", NSS_CMSMessage_ContentLevelCount(m_cmsMsg))); |
|
213 NSSCMSContentInfo *cinfo = nullptr; |
|
214 NSSCMSSignedData *sigd = nullptr; |
|
215 NSSCMSSignerInfo *si; |
|
216 int32_t nsigners; |
|
217 RefPtr<SharedCertVerifier> certVerifier; |
|
218 nsresult rv = NS_ERROR_FAILURE; |
|
219 |
|
220 if (!NSS_CMSMessage_IsSigned(m_cmsMsg)) { |
|
221 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - not signed\n")); |
|
222 return NS_ERROR_CMS_VERIFY_NOT_SIGNED; |
|
223 } |
|
224 |
|
225 cinfo = NSS_CMSMessage_ContentLevel(m_cmsMsg, 0); |
|
226 if (cinfo) { |
|
227 // I don't like this hard cast. We should check in some way, that we really have this type. |
|
228 sigd = (NSSCMSSignedData*)NSS_CMSContentInfo_GetContent(cinfo); |
|
229 } |
|
230 |
|
231 if (!sigd) { |
|
232 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - no content info\n")); |
|
233 rv = NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO; |
|
234 goto loser; |
|
235 } |
|
236 |
|
237 if (aDigestData && aDigestDataLen) |
|
238 { |
|
239 SECItem digest; |
|
240 digest.data = aDigestData; |
|
241 digest.len = aDigestDataLen; |
|
242 |
|
243 if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { |
|
244 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad digest\n")); |
|
245 rv = NS_ERROR_CMS_VERIFY_BAD_DIGEST; |
|
246 goto loser; |
|
247 } |
|
248 } |
|
249 |
|
250 // Import certs. Note that import failure is not a signature verification failure. // |
|
251 if (NSS_CMSSignedData_ImportCerts(sigd, CERT_GetDefaultCertDB(), certUsageEmailRecipient, true) != SECSuccess) { |
|
252 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not import certs\n")); |
|
253 } |
|
254 |
|
255 nsigners = NSS_CMSSignedData_SignerInfoCount(sigd); |
|
256 PR_ASSERT(nsigners > 0); |
|
257 NS_ENSURE_TRUE(nsigners > 0, NS_ERROR_UNEXPECTED); |
|
258 si = NSS_CMSSignedData_GetSignerInfo(sigd, 0); |
|
259 |
|
260 // See bug 324474. We want to make sure the signing cert is |
|
261 // still valid at the current time. |
|
262 |
|
263 certVerifier = GetDefaultCertVerifier(); |
|
264 NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED); |
|
265 |
|
266 { |
|
267 SECStatus srv = certVerifier->VerifyCert(si->cert, |
|
268 certificateUsageEmailSigner, |
|
269 PR_Now(), nullptr /*XXX pinarg*/, |
|
270 nullptr /*hostname*/); |
|
271 if (srv != SECSuccess) { |
|
272 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, |
|
273 ("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n")); |
|
274 rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; |
|
275 goto loser; |
|
276 } |
|
277 } |
|
278 |
|
279 // We verify the first signer info, only // |
|
280 if (NSS_CMSSignedData_VerifySignerInfo(sigd, 0, CERT_GetDefaultCertDB(), certUsageEmailSigner) != SECSuccess) { |
|
281 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to verify signature\n")); |
|
282 |
|
283 if (NSSCMSVS_SigningCertNotFound == si->verificationStatus) { |
|
284 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not found\n")); |
|
285 rv = NS_ERROR_CMS_VERIFY_NOCERT; |
|
286 } |
|
287 else if(NSSCMSVS_SigningCertNotTrusted == si->verificationStatus) { |
|
288 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - signing cert not trusted at signing time\n")); |
|
289 rv = NS_ERROR_CMS_VERIFY_UNTRUSTED; |
|
290 } |
|
291 else if(NSSCMSVS_Unverified == si->verificationStatus) { |
|
292 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - can not verify\n")); |
|
293 rv = NS_ERROR_CMS_VERIFY_ERROR_UNVERIFIED; |
|
294 } |
|
295 else if(NSSCMSVS_ProcessingError == si->verificationStatus) { |
|
296 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - processing error\n")); |
|
297 rv = NS_ERROR_CMS_VERIFY_ERROR_PROCESSING; |
|
298 } |
|
299 else if(NSSCMSVS_BadSignature == si->verificationStatus) { |
|
300 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - bad signature\n")); |
|
301 rv = NS_ERROR_CMS_VERIFY_BAD_SIGNATURE; |
|
302 } |
|
303 else if(NSSCMSVS_DigestMismatch == si->verificationStatus) { |
|
304 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - digest mismatch\n")); |
|
305 rv = NS_ERROR_CMS_VERIFY_DIGEST_MISMATCH; |
|
306 } |
|
307 else if(NSSCMSVS_SignatureAlgorithmUnknown == si->verificationStatus) { |
|
308 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo unknown\n")); |
|
309 rv = NS_ERROR_CMS_VERIFY_UNKNOWN_ALGO; |
|
310 } |
|
311 else if(NSSCMSVS_SignatureAlgorithmUnsupported == si->verificationStatus) { |
|
312 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - algo not supported\n")); |
|
313 rv = NS_ERROR_CMS_VERIFY_UNSUPPORTED_ALGO; |
|
314 } |
|
315 else if(NSSCMSVS_MalformedSignature == si->verificationStatus) { |
|
316 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - malformed signature\n")); |
|
317 rv = NS_ERROR_CMS_VERIFY_MALFORMED_SIGNATURE; |
|
318 } |
|
319 |
|
320 goto loser; |
|
321 } |
|
322 |
|
323 // Save the profile. Note that save import failure is not a signature verification failure. // |
|
324 if (NSS_SMIMESignerInfo_SaveSMIMEProfile(si) != SECSuccess) { |
|
325 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CommonVerifySignature - unable to save smime profile\n")); |
|
326 } |
|
327 |
|
328 rv = NS_OK; |
|
329 loser: |
|
330 return rv; |
|
331 } |
|
332 |
|
333 NS_IMETHODIMP nsCMSMessage::AsyncVerifySignature( |
|
334 nsISMimeVerificationListener *aListener) |
|
335 { |
|
336 return CommonAsyncVerifySignature(aListener, nullptr, 0); |
|
337 } |
|
338 |
|
339 NS_IMETHODIMP nsCMSMessage::AsyncVerifyDetachedSignature( |
|
340 nsISMimeVerificationListener *aListener, |
|
341 unsigned char* aDigestData, uint32_t aDigestDataLen) |
|
342 { |
|
343 if (!aDigestData || !aDigestDataLen) |
|
344 return NS_ERROR_FAILURE; |
|
345 |
|
346 return CommonAsyncVerifySignature(aListener, aDigestData, aDigestDataLen); |
|
347 } |
|
348 |
|
349 nsresult nsCMSMessage::CommonAsyncVerifySignature(nsISMimeVerificationListener *aListener, |
|
350 unsigned char* aDigestData, uint32_t aDigestDataLen) |
|
351 { |
|
352 nsSMimeVerificationJob *job = new nsSMimeVerificationJob; |
|
353 |
|
354 if (aDigestData) |
|
355 { |
|
356 job->digest_data = new unsigned char[aDigestDataLen]; |
|
357 memcpy(job->digest_data, aDigestData, aDigestDataLen); |
|
358 } |
|
359 else |
|
360 { |
|
361 job->digest_data = nullptr; |
|
362 } |
|
363 |
|
364 job->digest_len = aDigestDataLen; |
|
365 job->mMessage = this; |
|
366 job->mListener = aListener; |
|
367 |
|
368 nsresult rv = nsCertVerificationThread::addJob(job); |
|
369 if (NS_FAILED(rv)) |
|
370 delete job; |
|
371 |
|
372 return rv; |
|
373 } |
|
374 |
|
375 class nsZeroTerminatedCertArray : public nsNSSShutDownObject |
|
376 { |
|
377 public: |
|
378 nsZeroTerminatedCertArray() |
|
379 :mCerts(nullptr), mPoolp(nullptr), mSize(0) |
|
380 { |
|
381 } |
|
382 |
|
383 ~nsZeroTerminatedCertArray() |
|
384 { |
|
385 nsNSSShutDownPreventionLock locker; |
|
386 if (isAlreadyShutDown()) { |
|
387 return; |
|
388 } |
|
389 destructorSafeDestroyNSSReference(); |
|
390 shutdown(calledFromObject); |
|
391 } |
|
392 |
|
393 void virtualDestroyNSSReference() |
|
394 { |
|
395 destructorSafeDestroyNSSReference(); |
|
396 } |
|
397 |
|
398 void destructorSafeDestroyNSSReference() |
|
399 { |
|
400 if (mCerts) |
|
401 { |
|
402 for (uint32_t i=0; i < mSize; i++) { |
|
403 if (mCerts[i]) { |
|
404 CERT_DestroyCertificate(mCerts[i]); |
|
405 } |
|
406 } |
|
407 } |
|
408 |
|
409 if (mPoolp) |
|
410 PORT_FreeArena(mPoolp, false); |
|
411 } |
|
412 |
|
413 bool allocate(uint32_t count) |
|
414 { |
|
415 // only allow allocation once |
|
416 if (mPoolp) |
|
417 return false; |
|
418 |
|
419 mSize = count; |
|
420 |
|
421 if (!mSize) |
|
422 return false; |
|
423 |
|
424 mPoolp = PORT_NewArena(1024); |
|
425 if (!mPoolp) |
|
426 return false; |
|
427 |
|
428 mCerts = (CERTCertificate**)PORT_ArenaZAlloc( |
|
429 mPoolp, (count+1)*sizeof(CERTCertificate*)); |
|
430 |
|
431 if (!mCerts) |
|
432 return false; |
|
433 |
|
434 // null array, including zero termination |
|
435 for (uint32_t i = 0; i < count+1; i++) { |
|
436 mCerts[i] = nullptr; |
|
437 } |
|
438 |
|
439 return true; |
|
440 } |
|
441 |
|
442 void set(uint32_t i, CERTCertificate *c) |
|
443 { |
|
444 nsNSSShutDownPreventionLock locker; |
|
445 if (isAlreadyShutDown()) |
|
446 return; |
|
447 |
|
448 if (i >= mSize) |
|
449 return; |
|
450 |
|
451 if (mCerts[i]) { |
|
452 CERT_DestroyCertificate(mCerts[i]); |
|
453 } |
|
454 |
|
455 mCerts[i] = CERT_DupCertificate(c); |
|
456 } |
|
457 |
|
458 CERTCertificate *get(uint32_t i) |
|
459 { |
|
460 nsNSSShutDownPreventionLock locker; |
|
461 if (isAlreadyShutDown()) |
|
462 return nullptr; |
|
463 |
|
464 if (i >= mSize) |
|
465 return nullptr; |
|
466 |
|
467 return CERT_DupCertificate(mCerts[i]); |
|
468 } |
|
469 |
|
470 CERTCertificate **getRawArray() |
|
471 { |
|
472 nsNSSShutDownPreventionLock locker; |
|
473 if (isAlreadyShutDown()) |
|
474 return nullptr; |
|
475 |
|
476 return mCerts; |
|
477 } |
|
478 |
|
479 private: |
|
480 CERTCertificate **mCerts; |
|
481 PLArenaPool *mPoolp; |
|
482 uint32_t mSize; |
|
483 }; |
|
484 |
|
485 NS_IMETHODIMP nsCMSMessage::CreateEncrypted(nsIArray * aRecipientCerts) |
|
486 { |
|
487 nsNSSShutDownPreventionLock locker; |
|
488 if (isAlreadyShutDown()) |
|
489 return NS_ERROR_NOT_AVAILABLE; |
|
490 |
|
491 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted\n")); |
|
492 NSSCMSContentInfo *cinfo; |
|
493 NSSCMSEnvelopedData *envd; |
|
494 NSSCMSRecipientInfo *recipientInfo; |
|
495 nsZeroTerminatedCertArray recipientCerts; |
|
496 SECOidTag bulkAlgTag; |
|
497 int keySize; |
|
498 uint32_t i; |
|
499 nsCOMPtr<nsIX509Cert2> nssRecipientCert; |
|
500 nsresult rv = NS_ERROR_FAILURE; |
|
501 |
|
502 // Check the recipient certificates // |
|
503 uint32_t recipientCertCount; |
|
504 aRecipientCerts->GetLength(&recipientCertCount); |
|
505 PR_ASSERT(recipientCertCount > 0); |
|
506 |
|
507 if (!recipientCerts.allocate(recipientCertCount)) { |
|
508 goto loser; |
|
509 } |
|
510 |
|
511 for (i=0; i<recipientCertCount; i++) { |
|
512 nsCOMPtr<nsIX509Cert> x509cert = do_QueryElementAt(aRecipientCerts, i); |
|
513 |
|
514 nssRecipientCert = do_QueryInterface(x509cert); |
|
515 |
|
516 if (!nssRecipientCert) |
|
517 return NS_ERROR_FAILURE; |
|
518 |
|
519 mozilla::pkix::ScopedCERTCertificate c(nssRecipientCert->GetCert()); |
|
520 recipientCerts.set(i, c.get()); |
|
521 } |
|
522 |
|
523 // Find a bulk key algorithm // |
|
524 if (NSS_SMIMEUtil_FindBulkAlgForRecipients(recipientCerts.getRawArray(), &bulkAlgTag, |
|
525 &keySize) != SECSuccess) { |
|
526 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't find bulk alg for recipients\n")); |
|
527 rv = NS_ERROR_CMS_ENCRYPT_NO_BULK_ALG; |
|
528 goto loser; |
|
529 } |
|
530 |
|
531 m_cmsMsg = NSS_CMSMessage_Create(nullptr); |
|
532 if (!m_cmsMsg) { |
|
533 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create new cms message\n")); |
|
534 rv = NS_ERROR_OUT_OF_MEMORY; |
|
535 goto loser; |
|
536 } |
|
537 |
|
538 if ((envd = NSS_CMSEnvelopedData_Create(m_cmsMsg, bulkAlgTag, keySize)) == nullptr) { |
|
539 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create enveloped data\n")); |
|
540 goto loser; |
|
541 } |
|
542 |
|
543 cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); |
|
544 if (NSS_CMSContentInfo_SetContent_EnvelopedData(m_cmsMsg, cinfo, envd) != SECSuccess) { |
|
545 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create content enveloped data\n")); |
|
546 goto loser; |
|
547 } |
|
548 |
|
549 cinfo = NSS_CMSEnvelopedData_GetContentInfo(envd); |
|
550 if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, false) != SECSuccess) { |
|
551 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't set content data\n")); |
|
552 goto loser; |
|
553 } |
|
554 |
|
555 // Create and attach recipient information // |
|
556 for (i=0; i < recipientCertCount; i++) { |
|
557 mozilla::pkix::ScopedCERTCertificate rc(recipientCerts.get(i)); |
|
558 if ((recipientInfo = NSS_CMSRecipientInfo_Create(m_cmsMsg, rc.get())) == nullptr) { |
|
559 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't create recipient info\n")); |
|
560 goto loser; |
|
561 } |
|
562 if (NSS_CMSEnvelopedData_AddRecipient(envd, recipientInfo) != SECSuccess) { |
|
563 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateEncrypted - can't add recipient info\n")); |
|
564 goto loser; |
|
565 } |
|
566 } |
|
567 |
|
568 return NS_OK; |
|
569 loser: |
|
570 if (m_cmsMsg) { |
|
571 NSS_CMSMessage_Destroy(m_cmsMsg); |
|
572 m_cmsMsg = nullptr; |
|
573 } |
|
574 |
|
575 return rv; |
|
576 } |
|
577 |
|
578 NS_IMETHODIMP nsCMSMessage::CreateSigned(nsIX509Cert* aSigningCert, nsIX509Cert* aEncryptCert, unsigned char* aDigestData, uint32_t aDigestDataLen) |
|
579 { |
|
580 nsNSSShutDownPreventionLock locker; |
|
581 if (isAlreadyShutDown()) |
|
582 return NS_ERROR_NOT_AVAILABLE; |
|
583 |
|
584 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned\n")); |
|
585 NSSCMSContentInfo *cinfo; |
|
586 NSSCMSSignedData *sigd; |
|
587 NSSCMSSignerInfo *signerinfo; |
|
588 mozilla::pkix::ScopedCERTCertificate scert; |
|
589 mozilla::pkix::ScopedCERTCertificate ecert; |
|
590 nsCOMPtr<nsIX509Cert2> aSigningCert2 = do_QueryInterface(aSigningCert); |
|
591 nsresult rv = NS_ERROR_FAILURE; |
|
592 |
|
593 /* Get the certs */ |
|
594 if (aSigningCert2) { |
|
595 scert = aSigningCert2->GetCert(); |
|
596 } |
|
597 if (!scert) { |
|
598 return NS_ERROR_FAILURE; |
|
599 } |
|
600 |
|
601 if (aEncryptCert) { |
|
602 nsCOMPtr<nsIX509Cert2> aEncryptCert2 = do_QueryInterface(aEncryptCert); |
|
603 if (aEncryptCert2) { |
|
604 ecert = aEncryptCert2->GetCert(); |
|
605 } |
|
606 } |
|
607 |
|
608 /* |
|
609 * create the message object |
|
610 */ |
|
611 m_cmsMsg = NSS_CMSMessage_Create(nullptr); /* create a message on its own pool */ |
|
612 if (!m_cmsMsg) { |
|
613 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create new message\n")); |
|
614 rv = NS_ERROR_OUT_OF_MEMORY; |
|
615 goto loser; |
|
616 } |
|
617 |
|
618 /* |
|
619 * build chain of objects: message->signedData->data |
|
620 */ |
|
621 if ((sigd = NSS_CMSSignedData_Create(m_cmsMsg)) == nullptr) { |
|
622 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signed data\n")); |
|
623 goto loser; |
|
624 } |
|
625 cinfo = NSS_CMSMessage_GetContentInfo(m_cmsMsg); |
|
626 if (NSS_CMSContentInfo_SetContent_SignedData(m_cmsMsg, cinfo, sigd) |
|
627 != SECSuccess) { |
|
628 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content signed data\n")); |
|
629 goto loser; |
|
630 } |
|
631 |
|
632 cinfo = NSS_CMSSignedData_GetContentInfo(sigd); |
|
633 |
|
634 /* we're always passing data in and detaching optionally */ |
|
635 if (NSS_CMSContentInfo_SetContent_Data(m_cmsMsg, cinfo, nullptr, true) |
|
636 != SECSuccess) { |
|
637 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set content data\n")); |
|
638 goto loser; |
|
639 } |
|
640 |
|
641 /* |
|
642 * create & attach signer information |
|
643 */ |
|
644 if ((signerinfo = NSS_CMSSignerInfo_Create(m_cmsMsg, scert.get(), SEC_OID_SHA1)) |
|
645 == nullptr) { |
|
646 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't create signer info\n")); |
|
647 goto loser; |
|
648 } |
|
649 |
|
650 /* we want the cert chain included for this one */ |
|
651 if (NSS_CMSSignerInfo_IncludeCerts(signerinfo, NSSCMSCM_CertChain, |
|
652 certUsageEmailSigner) |
|
653 != SECSuccess) { |
|
654 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't include signer cert chain\n")); |
|
655 goto loser; |
|
656 } |
|
657 |
|
658 if (NSS_CMSSignerInfo_AddSigningTime(signerinfo, PR_Now()) |
|
659 != SECSuccess) { |
|
660 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signing time\n")); |
|
661 goto loser; |
|
662 } |
|
663 |
|
664 if (NSS_CMSSignerInfo_AddSMIMECaps(signerinfo) != SECSuccess) { |
|
665 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime caps\n")); |
|
666 goto loser; |
|
667 } |
|
668 |
|
669 if (ecert) { |
|
670 if (NSS_CMSSignerInfo_AddSMIMEEncKeyPrefs(signerinfo, ecert.get(), |
|
671 CERT_GetDefaultCertDB()) |
|
672 != SECSuccess) { |
|
673 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add smime enc key prefs\n")); |
|
674 goto loser; |
|
675 } |
|
676 |
|
677 if (NSS_CMSSignerInfo_AddMSSMIMEEncKeyPrefs(signerinfo, ecert.get(), |
|
678 CERT_GetDefaultCertDB()) |
|
679 != SECSuccess) { |
|
680 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add MS smime enc key prefs\n")); |
|
681 goto loser; |
|
682 } |
|
683 |
|
684 // If signing and encryption cert are identical, don't add it twice. |
|
685 bool addEncryptionCert = |
|
686 (ecert && (!scert || !CERT_CompareCerts(ecert.get(), scert.get()))); |
|
687 |
|
688 if (addEncryptionCert && |
|
689 NSS_CMSSignedData_AddCertificate(sigd, ecert.get()) != SECSuccess) { |
|
690 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add own encryption certificate\n")); |
|
691 goto loser; |
|
692 } |
|
693 } |
|
694 |
|
695 if (NSS_CMSSignedData_AddSignerInfo(sigd, signerinfo) != SECSuccess) { |
|
696 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't add signer info\n")); |
|
697 goto loser; |
|
698 } |
|
699 |
|
700 // Finally, add the pre-computed digest if passed in |
|
701 if (aDigestData) { |
|
702 SECItem digest; |
|
703 |
|
704 digest.data = aDigestData; |
|
705 digest.len = aDigestDataLen; |
|
706 |
|
707 if (NSS_CMSSignedData_SetDigestValue(sigd, SEC_OID_SHA1, &digest)) { |
|
708 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSMessage::CreateSigned - can't set digest value\n")); |
|
709 goto loser; |
|
710 } |
|
711 } |
|
712 |
|
713 return NS_OK; |
|
714 loser: |
|
715 if (m_cmsMsg) { |
|
716 NSS_CMSMessage_Destroy(m_cmsMsg); |
|
717 m_cmsMsg = nullptr; |
|
718 } |
|
719 return rv; |
|
720 } |
|
721 |
|
722 NS_IMPL_ISUPPORTS(nsCMSDecoder, nsICMSDecoder) |
|
723 |
|
724 nsCMSDecoder::nsCMSDecoder() |
|
725 : m_dcx(nullptr) |
|
726 { |
|
727 } |
|
728 |
|
729 nsCMSDecoder::~nsCMSDecoder() |
|
730 { |
|
731 nsNSSShutDownPreventionLock locker; |
|
732 if (isAlreadyShutDown()) { |
|
733 return; |
|
734 } |
|
735 destructorSafeDestroyNSSReference(); |
|
736 shutdown(calledFromObject); |
|
737 } |
|
738 |
|
739 void nsCMSDecoder::virtualDestroyNSSReference() |
|
740 { |
|
741 destructorSafeDestroyNSSReference(); |
|
742 } |
|
743 |
|
744 void nsCMSDecoder::destructorSafeDestroyNSSReference() |
|
745 { |
|
746 if (m_dcx) { |
|
747 NSS_CMSDecoder_Cancel(m_dcx); |
|
748 m_dcx = nullptr; |
|
749 } |
|
750 } |
|
751 |
|
752 /* void start (in NSSCMSContentCallback cb, in voidPtr arg); */ |
|
753 NS_IMETHODIMP nsCMSDecoder::Start(NSSCMSContentCallback cb, void * arg) |
|
754 { |
|
755 nsNSSShutDownPreventionLock locker; |
|
756 if (isAlreadyShutDown()) |
|
757 return NS_ERROR_NOT_AVAILABLE; |
|
758 |
|
759 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Start\n")); |
|
760 m_ctx = new PipUIContext(); |
|
761 |
|
762 m_dcx = NSS_CMSDecoder_Start(0, cb, arg, 0, m_ctx, 0, 0); |
|
763 if (!m_dcx) { |
|
764 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Start - can't start decoder\n")); |
|
765 return NS_ERROR_FAILURE; |
|
766 } |
|
767 return NS_OK; |
|
768 } |
|
769 |
|
770 /* void update (in string bug, in long len); */ |
|
771 NS_IMETHODIMP nsCMSDecoder::Update(const char *buf, int32_t len) |
|
772 { |
|
773 nsNSSShutDownPreventionLock locker; |
|
774 if (isAlreadyShutDown()) |
|
775 return NS_ERROR_NOT_AVAILABLE; |
|
776 |
|
777 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Update\n")); |
|
778 NSS_CMSDecoder_Update(m_dcx, (char *)buf, len); |
|
779 return NS_OK; |
|
780 } |
|
781 |
|
782 /* void finish (); */ |
|
783 NS_IMETHODIMP nsCMSDecoder::Finish(nsICMSMessage ** aCMSMsg) |
|
784 { |
|
785 nsNSSShutDownPreventionLock locker; |
|
786 if (isAlreadyShutDown()) |
|
787 return NS_ERROR_NOT_AVAILABLE; |
|
788 |
|
789 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSDecoder::Finish\n")); |
|
790 NSSCMSMessage *cmsMsg; |
|
791 cmsMsg = NSS_CMSDecoder_Finish(m_dcx); |
|
792 m_dcx = nullptr; |
|
793 if (cmsMsg) { |
|
794 nsCMSMessage *obj = new nsCMSMessage(cmsMsg); |
|
795 // The NSS object cmsMsg still carries a reference to the context |
|
796 // we gave it on construction. |
|
797 // Make sure the context will live long enough. |
|
798 obj->referenceContext(m_ctx); |
|
799 *aCMSMsg = obj; |
|
800 NS_ADDREF(*aCMSMsg); |
|
801 } |
|
802 return NS_OK; |
|
803 } |
|
804 |
|
805 NS_IMPL_ISUPPORTS(nsCMSEncoder, nsICMSEncoder) |
|
806 |
|
807 nsCMSEncoder::nsCMSEncoder() |
|
808 : m_ecx(nullptr) |
|
809 { |
|
810 } |
|
811 |
|
812 nsCMSEncoder::~nsCMSEncoder() |
|
813 { |
|
814 nsNSSShutDownPreventionLock locker; |
|
815 if (isAlreadyShutDown()) { |
|
816 return; |
|
817 } |
|
818 destructorSafeDestroyNSSReference(); |
|
819 shutdown(calledFromObject); |
|
820 } |
|
821 |
|
822 void nsCMSEncoder::virtualDestroyNSSReference() |
|
823 { |
|
824 destructorSafeDestroyNSSReference(); |
|
825 } |
|
826 |
|
827 void nsCMSEncoder::destructorSafeDestroyNSSReference() |
|
828 { |
|
829 if (m_ecx) |
|
830 NSS_CMSEncoder_Cancel(m_ecx); |
|
831 } |
|
832 |
|
833 /* void start (); */ |
|
834 NS_IMETHODIMP nsCMSEncoder::Start(nsICMSMessage *aMsg, NSSCMSContentCallback cb, void * arg) |
|
835 { |
|
836 nsNSSShutDownPreventionLock locker; |
|
837 if (isAlreadyShutDown()) |
|
838 return NS_ERROR_NOT_AVAILABLE; |
|
839 |
|
840 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Start\n")); |
|
841 nsCMSMessage *cmsMsg = static_cast<nsCMSMessage*>(aMsg); |
|
842 m_ctx = new PipUIContext(); |
|
843 |
|
844 m_ecx = NSS_CMSEncoder_Start(cmsMsg->getCMS(), cb, arg, 0, 0, 0, m_ctx, 0, 0, 0, 0); |
|
845 if (!m_ecx) { |
|
846 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Start - can't start encoder\n")); |
|
847 return NS_ERROR_FAILURE; |
|
848 } |
|
849 return NS_OK; |
|
850 } |
|
851 |
|
852 /* void update (in string aBuf, in long aLen); */ |
|
853 NS_IMETHODIMP nsCMSEncoder::Update(const char *aBuf, int32_t aLen) |
|
854 { |
|
855 nsNSSShutDownPreventionLock locker; |
|
856 if (isAlreadyShutDown()) |
|
857 return NS_ERROR_NOT_AVAILABLE; |
|
858 |
|
859 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Update\n")); |
|
860 if (!m_ecx || NSS_CMSEncoder_Update(m_ecx, aBuf, aLen) != SECSuccess) { |
|
861 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Update - can't update encoder\n")); |
|
862 return NS_ERROR_FAILURE; |
|
863 } |
|
864 return NS_OK; |
|
865 } |
|
866 |
|
867 /* void finish (); */ |
|
868 NS_IMETHODIMP nsCMSEncoder::Finish() |
|
869 { |
|
870 nsNSSShutDownPreventionLock locker; |
|
871 if (isAlreadyShutDown()) |
|
872 return NS_ERROR_NOT_AVAILABLE; |
|
873 |
|
874 nsresult rv = NS_OK; |
|
875 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Finish\n")); |
|
876 if (!m_ecx || NSS_CMSEncoder_Finish(m_ecx) != SECSuccess) { |
|
877 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Finish - can't finish encoder\n")); |
|
878 rv = NS_ERROR_FAILURE; |
|
879 } |
|
880 m_ecx = nullptr; |
|
881 return rv; |
|
882 } |
|
883 |
|
884 /* void encode (in nsICMSMessage aMsg); */ |
|
885 NS_IMETHODIMP nsCMSEncoder::Encode(nsICMSMessage *aMsg) |
|
886 { |
|
887 nsNSSShutDownPreventionLock locker; |
|
888 if (isAlreadyShutDown()) |
|
889 return NS_ERROR_NOT_AVAILABLE; |
|
890 |
|
891 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("nsCMSEncoder::Encode\n")); |
|
892 return NS_ERROR_NOT_IMPLEMENTED; |
|
893 } |