• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * libjingle
3  * Copyright 2012, Google Inc.
4  * Copyright 2012, RTFM, Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *  1. Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *  2. Redistributions in binary form must reproduce the above copyright notice,
12  *     this list of conditions and the following disclaimer in the documentation
13  *     and/or other materials provided with the distribution.
14  *  3. The name of the author may not be used to endorse or promote products
15  *     derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
18  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
20  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
23  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
24  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
25  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
26  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <algorithm>
30 #include <string>
31 #include <vector>
32 
33 #if HAVE_CONFIG_H
34 #include "config.h"
35 #endif  // HAVE_CONFIG_H
36 
37 #if HAVE_NSS_SSL_H
38 
39 #include "talk/base/nssidentity.h"
40 
41 #include "cert.h"
42 #include "cryptohi.h"
43 #include "keyhi.h"
44 #include "nss.h"
45 #include "pk11pub.h"
46 #include "sechash.h"
47 
48 #include "talk/base/logging.h"
49 #include "talk/base/helpers.h"
50 #include "talk/base/nssstreamadapter.h"
51 
52 namespace talk_base {
53 
~NSSKeyPair()54 NSSKeyPair::~NSSKeyPair() {
55   if (privkey_)
56     SECKEY_DestroyPrivateKey(privkey_);
57   if (pubkey_)
58     SECKEY_DestroyPublicKey(pubkey_);
59 }
60 
Generate()61 NSSKeyPair *NSSKeyPair::Generate() {
62   SECKEYPrivateKey *privkey = NULL;
63   SECKEYPublicKey *pubkey = NULL;
64   PK11RSAGenParams rsaparams;
65   rsaparams.keySizeInBits = 1024;
66   rsaparams.pe = 0x010001;  // 65537 -- a common RSA public exponent.
67 
68   privkey = PK11_GenerateKeyPair(NSSContext::GetSlot(),
69                                  CKM_RSA_PKCS_KEY_PAIR_GEN,
70                                  &rsaparams, &pubkey, PR_FALSE /*permanent*/,
71                                  PR_FALSE /*sensitive*/, NULL);
72   if (!privkey) {
73     LOG(LS_ERROR) << "Couldn't generate key pair";
74     return NULL;
75   }
76 
77   return new NSSKeyPair(privkey, pubkey);
78 }
79 
80 // Just make a copy.
GetReference()81 NSSKeyPair *NSSKeyPair::GetReference() {
82   SECKEYPrivateKey *privkey = SECKEY_CopyPrivateKey(privkey_);
83   if (!privkey)
84     return NULL;
85 
86   SECKEYPublicKey *pubkey = SECKEY_CopyPublicKey(pubkey_);
87   if (!pubkey) {
88     SECKEY_DestroyPrivateKey(privkey);
89     return NULL;
90   }
91 
92   return new NSSKeyPair(privkey, pubkey);
93 }
94 
NSSCertificate(CERTCertificate * cert)95 NSSCertificate::NSSCertificate(CERTCertificate* cert)
96     : certificate_(CERT_DupCertificate(cert)) {
97   ASSERT(certificate_ != NULL);
98 }
99 
DeleteCert(SSLCertificate * cert)100 static void DeleteCert(SSLCertificate* cert) {
101   delete cert;
102 }
103 
NSSCertificate(CERTCertList * cert_list)104 NSSCertificate::NSSCertificate(CERTCertList* cert_list) {
105   // Copy the first cert into certificate_.
106   CERTCertListNode* node = CERT_LIST_HEAD(cert_list);
107   certificate_ = CERT_DupCertificate(node->cert);
108 
109   // Put any remaining certificates into the chain.
110   node = CERT_LIST_NEXT(node);
111   std::vector<SSLCertificate*> certs;
112   for (; !CERT_LIST_END(node, cert_list); node = CERT_LIST_NEXT(node)) {
113     certs.push_back(new NSSCertificate(node->cert));
114   }
115 
116   if (!certs.empty())
117     chain_.reset(new SSLCertChain(certs));
118 
119   // The SSLCertChain constructor copies its input, so now we have to delete
120   // the originals.
121   std::for_each(certs.begin(), certs.end(), DeleteCert);
122 }
123 
NSSCertificate(CERTCertificate * cert,SSLCertChain * chain)124 NSSCertificate::NSSCertificate(CERTCertificate* cert, SSLCertChain* chain)
125     : certificate_(CERT_DupCertificate(cert)) {
126   ASSERT(certificate_ != NULL);
127   if (chain)
128     chain_.reset(chain->Copy());
129 }
130 
131 
FromPEMString(const std::string & pem_string)132 NSSCertificate *NSSCertificate::FromPEMString(const std::string &pem_string) {
133   std::string der;
134   if (!SSLIdentity::PemToDer(kPemTypeCertificate, pem_string, &der))
135     return NULL;
136 
137   SECItem der_cert;
138   der_cert.data = reinterpret_cast<unsigned char *>(const_cast<char *>(
139       der.data()));
140   der_cert.len = der.size();
141   CERTCertificate *cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
142       &der_cert, NULL, PR_FALSE, PR_TRUE);
143 
144   if (!cert)
145     return NULL;
146 
147   NSSCertificate* ret = new NSSCertificate(cert);
148   CERT_DestroyCertificate(cert);
149   return ret;
150 }
151 
GetReference() const152 NSSCertificate *NSSCertificate::GetReference() const {
153   return new NSSCertificate(certificate_, chain_.get());
154 }
155 
ToPEMString() const156 std::string NSSCertificate::ToPEMString() const {
157   return SSLIdentity::DerToPem(kPemTypeCertificate,
158                                certificate_->derCert.data,
159                                certificate_->derCert.len);
160 }
161 
ToDER(Buffer * der_buffer) const162 void NSSCertificate::ToDER(Buffer* der_buffer) const {
163   der_buffer->SetData(certificate_->derCert.data, certificate_->derCert.len);
164 }
165 
GetDigestLength(const std::string & algorithm,std::size_t * length)166 bool NSSCertificate::GetDigestLength(const std::string &algorithm,
167                                      std::size_t *length) {
168   const SECHashObject *ho;
169 
170   if (!GetDigestObject(algorithm, &ho))
171     return false;
172 
173   *length = ho->length;
174 
175   return true;
176 }
177 
GetSignatureDigestAlgorithm(std::string * algorithm) const178 bool NSSCertificate::GetSignatureDigestAlgorithm(std::string* algorithm) const {
179   // The function sec_DecodeSigAlg in NSS provides this mapping functionality.
180   // Unfortunately it is private, so the functionality must be duplicated here.
181   // See https://bugzilla.mozilla.org/show_bug.cgi?id=925165 .
182   SECOidTag sig_alg = SECOID_GetAlgorithmTag(&certificate_->signature);
183   switch (sig_alg) {
184     case SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION:
185       *algorithm = DIGEST_MD5;
186       break;
187     case SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION:
188     case SEC_OID_ISO_SHA_WITH_RSA_SIGNATURE:
189     case SEC_OID_ISO_SHA1_WITH_RSA_SIGNATURE:
190     case SEC_OID_ANSIX9_DSA_SIGNATURE_WITH_SHA1_DIGEST:
191     case SEC_OID_BOGUS_DSA_SIGNATURE_WITH_SHA1_DIGEST:
192     case SEC_OID_ANSIX962_ECDSA_SHA1_SIGNATURE:
193     case SEC_OID_MISSI_DSS:
194     case SEC_OID_MISSI_KEA_DSS:
195     case SEC_OID_MISSI_KEA_DSS_OLD:
196     case SEC_OID_MISSI_DSS_OLD:
197       *algorithm = DIGEST_SHA_1;
198       break;
199     case SEC_OID_ANSIX962_ECDSA_SHA224_SIGNATURE:
200     case SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION:
201     case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA224_DIGEST:
202       *algorithm = DIGEST_SHA_224;
203       break;
204     case SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE:
205     case SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION:
206     case SEC_OID_NIST_DSA_SIGNATURE_WITH_SHA256_DIGEST:
207       *algorithm = DIGEST_SHA_256;
208       break;
209     case SEC_OID_ANSIX962_ECDSA_SHA384_SIGNATURE:
210     case SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION:
211       *algorithm = DIGEST_SHA_384;
212       break;
213     case SEC_OID_ANSIX962_ECDSA_SHA512_SIGNATURE:
214     case SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION:
215       *algorithm = DIGEST_SHA_512;
216       break;
217     default:
218       // Unknown algorithm.  There are several unhandled options that are less
219       // common and more complex.
220       algorithm->clear();
221       return false;
222   }
223   return true;
224 }
225 
ComputeDigest(const std::string & algorithm,unsigned char * digest,std::size_t size,std::size_t * length) const226 bool NSSCertificate::ComputeDigest(const std::string &algorithm,
227                                    unsigned char *digest, std::size_t size,
228                                    std::size_t *length) const {
229   const SECHashObject *ho;
230 
231   if (!GetDigestObject(algorithm, &ho))
232     return false;
233 
234   if (size < ho->length)  // Sanity check for fit
235     return false;
236 
237   SECStatus rv = HASH_HashBuf(ho->type, digest,
238                               certificate_->derCert.data,
239                               certificate_->derCert.len);
240   if (rv != SECSuccess)
241     return false;
242 
243   *length = ho->length;
244 
245   return true;
246 }
247 
GetChain(SSLCertChain ** chain) const248 bool NSSCertificate::GetChain(SSLCertChain** chain) const {
249   if (!chain_)
250     return false;
251 
252   *chain = chain_->Copy();
253   return true;
254 }
255 
Equals(const NSSCertificate * tocompare) const256 bool NSSCertificate::Equals(const NSSCertificate *tocompare) const {
257   if (!certificate_->derCert.len)
258     return false;
259   if (!tocompare->certificate_->derCert.len)
260     return false;
261 
262   if (certificate_->derCert.len != tocompare->certificate_->derCert.len)
263     return false;
264 
265   return memcmp(certificate_->derCert.data,
266                 tocompare->certificate_->derCert.data,
267                 certificate_->derCert.len) == 0;
268 }
269 
270 
GetDigestObject(const std::string & algorithm,const SECHashObject ** hop)271 bool NSSCertificate::GetDigestObject(const std::string &algorithm,
272                                      const SECHashObject **hop) {
273   const SECHashObject *ho;
274   HASH_HashType hash_type;
275 
276   if (algorithm == DIGEST_SHA_1) {
277     hash_type = HASH_AlgSHA1;
278   // HASH_AlgSHA224 is not supported in the chromium linux build system.
279 #if 0
280   } else if (algorithm == DIGEST_SHA_224) {
281     hash_type = HASH_AlgSHA224;
282 #endif
283   } else if (algorithm == DIGEST_SHA_256) {
284     hash_type = HASH_AlgSHA256;
285   } else if (algorithm == DIGEST_SHA_384) {
286     hash_type = HASH_AlgSHA384;
287   } else if (algorithm == DIGEST_SHA_512) {
288     hash_type = HASH_AlgSHA512;
289   } else {
290     return false;
291   }
292 
293   ho = HASH_GetHashObject(hash_type);
294 
295   ASSERT(ho->length >= 20);  // Can't happen
296   *hop = ho;
297 
298   return true;
299 }
300 
301 
Generate(const std::string & common_name)302 NSSIdentity *NSSIdentity::Generate(const std::string &common_name) {
303   std::string subject_name_string = "CN=" + common_name;
304   CERTName *subject_name = CERT_AsciiToName(
305       const_cast<char *>(subject_name_string.c_str()));
306   NSSIdentity *identity = NULL;
307   CERTSubjectPublicKeyInfo *spki = NULL;
308   CERTCertificateRequest *certreq = NULL;
309   CERTValidity *validity;
310   CERTCertificate *certificate = NULL;
311   NSSKeyPair *keypair = NSSKeyPair::Generate();
312   SECItem inner_der;
313   SECStatus rv;
314   PLArenaPool* arena;
315   SECItem signed_cert;
316   PRTime not_before, not_after;
317   PRTime now = PR_Now();
318   PRTime one_day;
319 
320   inner_der.len = 0;
321   inner_der.data = NULL;
322 
323   if (!keypair) {
324     LOG(LS_ERROR) << "Couldn't generate key pair";
325     goto fail;
326   }
327 
328   if (!subject_name) {
329     LOG(LS_ERROR) << "Couldn't convert subject name " << subject_name;
330     goto fail;
331   }
332 
333   spki = SECKEY_CreateSubjectPublicKeyInfo(keypair->pubkey());
334   if (!spki) {
335     LOG(LS_ERROR) << "Couldn't create SPKI";
336     goto fail;
337   }
338 
339   certreq = CERT_CreateCertificateRequest(subject_name, spki, NULL);
340   if (!certreq) {
341     LOG(LS_ERROR) << "Couldn't create certificate signing request";
342     goto fail;
343   }
344 
345   one_day = 86400;
346   one_day *= PR_USEC_PER_SEC;
347   not_before = now - one_day;
348   not_after = now + 30 * one_day;
349 
350   validity = CERT_CreateValidity(not_before, not_after);
351   if (!validity) {
352     LOG(LS_ERROR) << "Couldn't create validity";
353     goto fail;
354   }
355 
356   unsigned long serial;
357   // Note: This serial in principle could collide, but it's unlikely
358   rv = PK11_GenerateRandom(reinterpret_cast<unsigned char *>(&serial),
359                            sizeof(serial));
360   if (rv != SECSuccess) {
361     LOG(LS_ERROR) << "Couldn't generate random serial";
362     goto fail;
363   }
364 
365   certificate = CERT_CreateCertificate(serial, subject_name, validity, certreq);
366   if (!certificate) {
367     LOG(LS_ERROR) << "Couldn't create certificate";
368     goto fail;
369   }
370 
371   arena = certificate->arena;
372 
373   rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
374                              SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, NULL);
375   if (rv != SECSuccess)
376     goto fail;
377 
378   // Set version to X509v3.
379   *(certificate->version.data) = 2;
380   certificate->version.len = 1;
381 
382   if (!SEC_ASN1EncodeItem(arena, &inner_der, certificate,
383                           SEC_ASN1_GET(CERT_CertificateTemplate)))
384     goto fail;
385 
386   rv = SEC_DerSignData(arena, &signed_cert, inner_der.data, inner_der.len,
387                        keypair->privkey(),
388                        SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION);
389   if (rv != SECSuccess) {
390     LOG(LS_ERROR) << "Couldn't sign certificate";
391     goto fail;
392   }
393   certificate->derCert = signed_cert;
394 
395   identity = new NSSIdentity(keypair, new NSSCertificate(certificate));
396 
397   goto done;
398 
399  fail:
400   delete keypair;
401 
402  done:
403   if (certificate) CERT_DestroyCertificate(certificate);
404   if (subject_name) CERT_DestroyName(subject_name);
405   if (spki) SECKEY_DestroySubjectPublicKeyInfo(spki);
406   if (certreq) CERT_DestroyCertificateRequest(certreq);
407   if (validity) CERT_DestroyValidity(validity);
408   return identity;
409 }
410 
FromPEMStrings(const std::string & private_key,const std::string & certificate)411 SSLIdentity* NSSIdentity::FromPEMStrings(const std::string& private_key,
412                                          const std::string& certificate) {
413   std::string private_key_der;
414   if (!SSLIdentity::PemToDer(
415       kPemTypeRsaPrivateKey, private_key, &private_key_der))
416     return NULL;
417 
418   SECItem private_key_item;
419   private_key_item.data =
420       reinterpret_cast<unsigned char *>(
421           const_cast<char *>(private_key_der.c_str()));
422   private_key_item.len = private_key_der.size();
423 
424   const unsigned int key_usage = KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT |
425       KU_DIGITAL_SIGNATURE;
426 
427   SECKEYPrivateKey* privkey = NULL;
428   SECStatus rv =
429       PK11_ImportDERPrivateKeyInfoAndReturnKey(NSSContext::GetSlot(),
430                                                &private_key_item,
431                                                NULL, NULL, PR_FALSE, PR_FALSE,
432                                                key_usage, &privkey, NULL);
433   if (rv != SECSuccess) {
434     LOG(LS_ERROR) << "Couldn't import private key";
435     return NULL;
436   }
437 
438   SECKEYPublicKey *pubkey = SECKEY_ConvertToPublicKey(privkey);
439   if (rv != SECSuccess) {
440     SECKEY_DestroyPrivateKey(privkey);
441     LOG(LS_ERROR) << "Couldn't convert private key to public key";
442     return NULL;
443   }
444 
445   // Assign to a scoped_ptr so we don't leak on error.
446   scoped_ptr<NSSKeyPair> keypair(new NSSKeyPair(privkey, pubkey));
447 
448   scoped_ptr<NSSCertificate> cert(NSSCertificate::FromPEMString(certificate));
449   if (!cert) {
450     LOG(LS_ERROR) << "Couldn't parse certificate";
451     return NULL;
452   }
453 
454   // TODO(ekr@rtfm.com): Check the public key against the certificate.
455 
456   return new NSSIdentity(keypair.release(), cert.release());
457 }
458 
GetReference() const459 NSSIdentity *NSSIdentity::GetReference() const {
460   NSSKeyPair *keypair = keypair_->GetReference();
461   if (!keypair)
462     return NULL;
463 
464   NSSCertificate *certificate = certificate_->GetReference();
465   if (!certificate) {
466     delete keypair;
467     return NULL;
468   }
469 
470   return new NSSIdentity(keypair, certificate);
471 }
472 
473 
certificate() const474 NSSCertificate &NSSIdentity::certificate() const {
475   return *certificate_;
476 }
477 
478 
479 }  // talk_base namespace
480 
481 #endif  // HAVE_NSS_SSL_H
482 
483