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.

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

mercurial