Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
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