Home | History | Annotate | Download | only in base
      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 
     54 NSSKeyPair::~NSSKeyPair() {
     55   if (privkey_)
     56     SECKEY_DestroyPrivateKey(privkey_);
     57   if (pubkey_)
     58     SECKEY_DestroyPublicKey(pubkey_);
     59 }
     60 
     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.
     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 
     95 NSSCertificate::NSSCertificate(CERTCertificate* cert)
     96     : certificate_(CERT_DupCertificate(cert)) {
     97   ASSERT(certificate_ != NULL);
     98 }
     99 
    100 static void DeleteCert(SSLCertificate* cert) {
    101   delete cert;
    102 }
    103 
    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 
    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 
    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 
    152 NSSCertificate *NSSCertificate::GetReference() const {
    153   return new NSSCertificate(certificate_, chain_.get());
    154 }
    155 
    156 std::string NSSCertificate::ToPEMString() const {
    157   return SSLIdentity::DerToPem(kPemTypeCertificate,
    158                                certificate_->derCert.data,
    159                                certificate_->derCert.len);
    160 }
    161 
    162 void NSSCertificate::ToDER(Buffer* der_buffer) const {
    163   der_buffer->SetData(certificate_->derCert.data, certificate_->derCert.len);
    164 }
    165 
    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 
    178 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 
    226 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 
    248 bool NSSCertificate::GetChain(SSLCertChain** chain) const {
    249   if (!chain_)
    250     return false;
    251 
    252   *chain = chain_->Copy();
    253   return true;
    254 }
    255 
    256 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 
    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 
    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 
    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 (at) rtfm.com): Check the public key against the certificate.
    455 
    456   return new NSSIdentity(keypair.release(), cert.release());
    457 }
    458 
    459 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 
    474 NSSCertificate &NSSIdentity::certificate() const {
    475   return *certificate_;
    476 }
    477 
    478 
    479 }  // talk_base namespace
    480 
    481 #endif  // HAVE_NSS_SSL_H
    482 
    483