media/mtransport/dtlsidentity.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 et sw=2 tw=80: */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
     5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include <iomanip>
     8 #include "logging.h"
     9 #include "nspr.h"
    10 #include "cryptohi.h"
    11 #include "ssl.h"
    12 #include "keyhi.h"
    13 #include "pk11pub.h"
    14 #include "sechash.h"
    15 #include "nsError.h"
    16 #include "dtlsidentity.h"
    18 namespace mozilla {
    20 MOZ_MTLOG_MODULE("mtransport")
    22 DtlsIdentity::~DtlsIdentity() {
    23   // XXX: make cert_ a smart pointer to avoid this, after we figure
    24   // out the linking problem.
    25   if (cert_)
    26     CERT_DestroyCertificate(cert_);
    27 }
    29 const std::string DtlsIdentity::DEFAULT_HASH_ALGORITHM = "sha-256";
    30 const size_t DtlsIdentity::HASH_ALGORITHM_MAX_LENGTH = 64;
    32 TemporaryRef<DtlsIdentity> DtlsIdentity::Generate() {
    34   ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
    35   if (!slot) {
    36     return nullptr;
    37   }
    39   uint8_t random_name[16];
    41   SECStatus rv = PK11_GenerateRandomOnSlot(slot, random_name,
    42                                            sizeof(random_name));
    43   if (rv != SECSuccess)
    44     return nullptr;
    46   std::string name;
    47   char chunk[3];
    48   for (size_t i = 0; i < sizeof(random_name); ++i) {
    49     PR_snprintf(chunk, sizeof(chunk), "%.2x", random_name[i]);
    50     name += chunk;
    51   }
    53   std::string subject_name_string = "CN=" + name;
    54   ScopedCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str()));
    55   if (!subject_name) {
    56     return nullptr;
    57   }
    59   PK11RSAGenParams rsaparams;
    60   rsaparams.keySizeInBits = 1024; // TODO: make this stronger when we
    61                                   // pre-generate.
    62   rsaparams.pe = 65537; // We are too paranoid to use 3 as the exponent.
    64   ScopedSECKEYPrivateKey private_key;
    65   ScopedSECKEYPublicKey public_key;
    66   SECKEYPublicKey *pubkey;
    68   private_key =
    69       PK11_GenerateKeyPair(slot,
    70                            CKM_RSA_PKCS_KEY_PAIR_GEN, &rsaparams, &pubkey,
    71                            PR_FALSE, PR_TRUE, nullptr);
    72   if (private_key == nullptr)
    73     return nullptr;
    74   public_key = pubkey;
    76   ScopedCERTSubjectPublicKeyInfo spki(
    77       SECKEY_CreateSubjectPublicKeyInfo(pubkey));
    78   if (!spki) {
    79     return nullptr;
    80   }
    82   ScopedCERTCertificateRequest certreq(
    83       CERT_CreateCertificateRequest(subject_name, spki, nullptr));
    84   if (!certreq) {
    85     return nullptr;
    86   }
    88   // From 1 day before todayto 30 days after.
    89   // This is a sort of arbitrary range designed to be valid
    90   // now with some slack in case the other side expects
    91   // some before expiry.
    92   //
    93   // Note: explicit casts necessary to avoid
    94   //       warning C4307: '*' : integral constant overflow
    95   static const PRTime oneDay = PRTime(PR_USEC_PER_SEC)
    96                              * PRTime(60)  // sec
    97                              * PRTime(60)  // min
    98                              * PRTime(24); // hours
    99   PRTime now = PR_Now();
   100   PRTime notBefore = now - oneDay;
   101   PRTime notAfter = now + (PRTime(30) * oneDay);
   103   ScopedCERTValidity validity(CERT_CreateValidity(notBefore, notAfter));
   104   if (!validity) {
   105     return nullptr;
   106   }
   108   unsigned long serial;
   109   // Note: This serial in principle could collide, but it's unlikely
   110   rv = PK11_GenerateRandomOnSlot(slot,
   111                                  reinterpret_cast<unsigned char *>(&serial),
   112                                  sizeof(serial));
   113   if (rv != SECSuccess) {
   114     return nullptr;
   115   }
   117   ScopedCERTCertificate certificate(
   118       CERT_CreateCertificate(serial, subject_name, validity, certreq));
   119   if (!certificate) {
   120     return nullptr;
   121   }
   123   PLArenaPool *arena = certificate->arena;
   125   rv = SECOID_SetAlgorithmID(arena, &certificate->signature,
   126                              SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION, 0);
   127   if (rv != SECSuccess)
   128     return nullptr;
   130   // Set version to X509v3.
   131   *(certificate->version.data) = SEC_CERTIFICATE_VERSION_3;
   132   certificate->version.len = 1;
   134   SECItem innerDER;
   135   innerDER.len = 0;
   136   innerDER.data = nullptr;
   138   if (!SEC_ASN1EncodeItem(arena, &innerDER, certificate,
   139                           SEC_ASN1_GET(CERT_CertificateTemplate))) {
   140     return nullptr;
   141   }
   143   SECItem *signedCert = PORT_ArenaZNew(arena, SECItem);
   144   if (!signedCert) {
   145     return nullptr;
   146   }
   148   rv = SEC_DerSignData(arena, signedCert, innerDER.data, innerDER.len,
   149                        private_key,
   150                        SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION);
   151   if (rv != SECSuccess) {
   152     return nullptr;
   153   }
   154   certificate->derCert = *signedCert;
   156   return new DtlsIdentity(private_key.forget(), certificate.forget());
   157 }
   160 nsresult DtlsIdentity::ComputeFingerprint(const std::string algorithm,
   161                                           unsigned char *digest,
   162                                           std::size_t size,
   163                                           std::size_t *digest_length) {
   164   MOZ_ASSERT(cert_);
   166   return ComputeFingerprint(cert_, algorithm, digest, size, digest_length);
   167 }
   169 nsresult DtlsIdentity::ComputeFingerprint(const CERTCertificate *cert,
   170                                           const std::string algorithm,
   171                                           unsigned char *digest,
   172                                           std::size_t size,
   173                                           std::size_t *digest_length) {
   174   MOZ_ASSERT(cert);
   176   HASH_HashType ht;
   178   if (algorithm == "sha-1") {
   179     ht = HASH_AlgSHA1;
   180   } else if (algorithm == "sha-224") {
   181     ht = HASH_AlgSHA224;
   182   } else if (algorithm == "sha-256") {
   183     ht = HASH_AlgSHA256;
   184   } else if (algorithm == "sha-384") {
   185     ht = HASH_AlgSHA384;
   186   }  else if (algorithm == "sha-512") {
   187     ht = HASH_AlgSHA512;
   188   } else {
   189     return NS_ERROR_INVALID_ARG;
   190   }
   192   const SECHashObject *ho = HASH_GetHashObject(ht);
   193   MOZ_ASSERT(ho);
   194   if (!ho)
   195     return NS_ERROR_INVALID_ARG;
   197   MOZ_ASSERT(ho->length >= 20);  // Double check
   199   if (size < ho->length)
   200     return NS_ERROR_INVALID_ARG;
   202   SECStatus rv = HASH_HashBuf(ho->type, digest,
   203                               cert->derCert.data,
   204                               cert->derCert.len);
   205   if (rv != SECSuccess)
   206     return NS_ERROR_FAILURE;
   208   *digest_length = ho->length;
   210   return NS_OK;
   211 }
   213 // Format the fingerprint in RFC 4572 Section 5 attribute format, including both
   214 // the hash name and the fingerprint, colons and all.
   215 // returns an empty string if there is a problem
   216 std::string DtlsIdentity::GetFormattedFingerprint(const std::string &algorithm) {
   217   unsigned char digest[HASH_ALGORITHM_MAX_LENGTH];
   218   size_t digest_length;
   220   nsresult res = this->ComputeFingerprint(algorithm,
   221                                           digest,
   222                                           sizeof(digest),
   223                                           &digest_length);
   224   if (NS_FAILED(res)) {
   225     MOZ_MTLOG(ML_ERROR, "Unable to compute " << algorithm
   226               << " hash for identity: nsresult = 0x"
   227               << std::hex << std::uppercase
   228               << static_cast<uint32_t>(res)
   229               << std::nouppercase << std::dec);
   230     return "";
   231   }
   233   return algorithm + " " + this->FormatFingerprint(digest, digest_length);
   234 }
   236 std::string DtlsIdentity::FormatFingerprint(const unsigned char *digest,
   237                                             std::size_t size) {
   238   std::string str("");
   239   char group[3];
   241   for (std::size_t i=0; i < size; i++) {
   242     PR_snprintf(group, sizeof(group), "%.2X", digest[i]);
   243     if (i != 0) {
   244       str += ":";
   245     }
   246     str += group;
   247   }
   249   MOZ_ASSERT(str.size() == (size * 3 - 1));  // Check result length
   250   return str;
   251 }
   253 // Parse a fingerprint in RFC 4572 format.
   254 // Note that this tolerates some badly formatted data, in particular:
   255 // (a) arbitrary runs of colons
   256 // (b) colons at the beginning or end.
   257 nsresult DtlsIdentity::ParseFingerprint(const std::string fp,
   258                                         unsigned char *digest,
   259                                         size_t size,
   260                                         size_t *length) {
   261   size_t offset = 0;
   262   bool top_half = true;
   263   uint8_t val = 0;
   265   for (size_t i=0; i<fp.length(); i++) {
   266     if (offset >= size) {
   267       // Note: no known way for offset to get > size
   268       MOZ_MTLOG(ML_ERROR, "Fingerprint too long for buffer");
   269       return NS_ERROR_INVALID_ARG;
   270     }
   272     if (top_half && (fp[i] == ':')) {
   273       continue;
   274     } else if ((fp[i] >= '0') && (fp[i] <= '9')) {
   275       val |= fp[i] - '0';
   276     } else if ((fp[i] >= 'A') && (fp[i] <= 'F')) {
   277       val |= fp[i] - 'A' + 10;
   278     } else {
   279       MOZ_MTLOG(ML_ERROR, "Invalid fingerprint value " << fp[i]);
   280       return NS_ERROR_ILLEGAL_VALUE;
   281     }
   283     if (top_half) {
   284       val <<= 4;
   285       top_half = false;
   286     } else {
   287       digest[offset++] = val;
   288       top_half = true;
   289       val = 0;
   290     }
   291   }
   293   *length = offset;
   295   return NS_OK;
   296 }
   298 }  // close namespace

mercurial