RTCCertificate.cpp (15667B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=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 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "mozilla/dom/RTCCertificate.h" 8 9 #include <cstdio> 10 #include <cstring> 11 #include <new> 12 #include <utility> 13 14 #include "ErrorList.h" 15 #include "MainThreadUtils.h" 16 #include "cert.h" 17 #include "cryptohi.h" 18 #include "js/StructuredClone.h" 19 #include "js/TypeDecls.h" 20 #include "js/Value.h" 21 #include "keyhi.h" 22 #include "mozilla/ErrorResult.h" 23 #include "mozilla/OwningNonNull.h" 24 #include "mozilla/dom/BindingDeclarations.h" 25 #include "mozilla/dom/CryptoBuffer.h" 26 #include "mozilla/dom/CryptoKey.h" 27 #include "mozilla/dom/KeyAlgorithmBinding.h" 28 #include "mozilla/dom/KeyAlgorithmProxy.h" 29 #include "mozilla/dom/Promise.h" 30 #include "mozilla/dom/RTCCertificateBinding.h" 31 #include "mozilla/dom/StructuredCloneHolder.h" 32 #include "mozilla/dom/SubtleCryptoBinding.h" 33 #include "mozilla/dom/UnionTypes.h" 34 #include "mozilla/dom/WebCryptoCommon.h" 35 #include "mozilla/dom/WebCryptoTask.h" 36 #include "mozilla/fallible.h" 37 #include "nsDebug.h" 38 #include "nsError.h" 39 #include "nsLiteralString.h" 40 #include "nsStringFlags.h" 41 #include "nsStringFwd.h" 42 #include "nsTLiteralString.h" 43 #include "pk11pub.h" 44 #include "plarena.h" 45 #include "sdp/SdpAttribute.h" 46 #include "secasn1.h" 47 #include "secasn1t.h" 48 #include "seccomon.h" 49 #include "secmodt.h" 50 #include "secoid.h" 51 #include "secoidt.h" 52 #include "transport/dtlsidentity.h" 53 #include "xpcpublic.h" 54 55 namespace mozilla::dom { 56 57 #define RTCCERTIFICATE_SC_VERSION 0x00000001 58 59 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(RTCCertificate, mGlobal) 60 NS_IMPL_CYCLE_COLLECTING_ADDREF(RTCCertificate) 61 NS_IMPL_CYCLE_COLLECTING_RELEASE(RTCCertificate) 62 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(RTCCertificate) 63 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 64 NS_INTERFACE_MAP_ENTRY(nsISupports) 65 NS_INTERFACE_MAP_END 66 67 // Note: explicit casts necessary to avoid 68 // warning C4307: '*' : integral constant overflow 69 #define ONE_DAY \ 70 PRTime(PR_USEC_PER_SEC) * PRTime(60) /*sec*/ \ 71 * PRTime(60) /*min*/ * PRTime(24) /*hours*/ 72 #define EXPIRATION_DEFAULT ONE_DAY* PRTime(30) 73 #define EXPIRATION_SLACK ONE_DAY 74 #define EXPIRATION_MAX ONE_DAY* PRTime(365) /*year*/ 75 76 const size_t RTCCertificateCommonNameLength = 16; 77 const size_t RTCCertificateMinRsaSize = 1024; 78 79 class GenerateRTCCertificateTask : public GenerateAsymmetricKeyTask { 80 public: 81 GenerateRTCCertificateTask(nsIGlobalObject* aGlobal, JSContext* aCx, 82 const ObjectOrString& aAlgorithm, 83 const Sequence<nsString>& aKeyUsages, 84 PRTime aExpires) 85 : GenerateAsymmetricKeyTask(aGlobal, aCx, aAlgorithm, true, aKeyUsages), 86 mExpires(aExpires), 87 mAuthType(ssl_kea_null), 88 mCertificate(nullptr), 89 mSignatureAlg(SEC_OID_UNKNOWN) { 90 if (NS_FAILED(mEarlyRv)) { 91 // webrtc-pc says to throw NotSupportedError if we have passed "an 92 // algorithm that the user agent cannot or will not use to generate a 93 // certificate". This catches these cases. 94 mEarlyRv = NS_ERROR_DOM_NOT_SUPPORTED_ERR; 95 } 96 } 97 98 private: 99 PRTime mExpires; 100 SSLKEAType mAuthType; 101 UniqueCERTCertificate mCertificate; 102 SECOidTag mSignatureAlg; 103 104 static CERTName* GenerateRandomName(PK11SlotInfo* aSlot) { 105 uint8_t randomName[RTCCertificateCommonNameLength]; 106 SECStatus rv = 107 PK11_GenerateRandomOnSlot(aSlot, randomName, sizeof(randomName)); 108 if (rv != SECSuccess) { 109 return nullptr; 110 } 111 112 char buf[sizeof(randomName) * 2 + 4]; 113 strncpy(buf, "CN=", 4); 114 for (size_t i = 0; i < sizeof(randomName); ++i) { 115 snprintf(&buf[i * 2 + 3], 3, "%.2x", randomName[i]); 116 } 117 buf[sizeof(buf) - 1] = '\0'; 118 119 return CERT_AsciiToName(buf); 120 } 121 122 nsresult GenerateCertificate() { 123 UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 124 MOZ_ASSERT(slot.get()); 125 126 UniqueCERTName subjectName(GenerateRandomName(slot.get())); 127 if (!subjectName) { 128 return NS_ERROR_DOM_UNKNOWN_ERR; 129 } 130 131 UniqueSECKEYPublicKey publicKey(mKeyPair->mPublicKey->GetPublicKey()); 132 UniqueCERTSubjectPublicKeyInfo spki( 133 SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); 134 if (!spki) { 135 return NS_ERROR_DOM_UNKNOWN_ERR; 136 } 137 138 UniqueCERTCertificateRequest certreq( 139 CERT_CreateCertificateRequest(subjectName.get(), spki.get(), nullptr)); 140 if (!certreq) { 141 return NS_ERROR_DOM_UNKNOWN_ERR; 142 } 143 144 PRTime now = PR_Now(); 145 PRTime notBefore = now - EXPIRATION_SLACK; 146 mExpires += now; 147 148 UniqueCERTValidity validity(CERT_CreateValidity(notBefore, mExpires)); 149 if (!validity) { 150 return NS_ERROR_DOM_UNKNOWN_ERR; 151 } 152 153 unsigned long serial; 154 // Note: This serial in principle could collide, but it's unlikely, and we 155 // don't expect anyone to be validating certificates anyway. 156 SECStatus rv = PK11_GenerateRandomOnSlot( 157 slot.get(), reinterpret_cast<unsigned char*>(&serial), sizeof(serial)); 158 if (rv != SECSuccess) { 159 return NS_ERROR_DOM_UNKNOWN_ERR; 160 } 161 162 // NB: CERTCertificates created with CERT_CreateCertificate are not safe to 163 // use with other NSS functions like CERT_DupCertificate. The strategy 164 // here is to create a tbsCertificate ("to-be-signed certificate"), encode 165 // it, and sign it, resulting in a signed DER certificate that can be 166 // decoded into a CERTCertificate. 167 UniqueCERTCertificate tbsCertificate(CERT_CreateCertificate( 168 serial, subjectName.get(), validity.get(), certreq.get())); 169 if (!tbsCertificate) { 170 return NS_ERROR_DOM_UNKNOWN_ERR; 171 } 172 173 MOZ_ASSERT(mSignatureAlg != SEC_OID_UNKNOWN); 174 PLArenaPool* arena = tbsCertificate->arena; 175 176 rv = SECOID_SetAlgorithmID(arena, &tbsCertificate->signature, mSignatureAlg, 177 nullptr); 178 if (rv != SECSuccess) { 179 return NS_ERROR_DOM_UNKNOWN_ERR; 180 } 181 182 // Set version to X509v3. 183 *(tbsCertificate->version.data) = SEC_CERTIFICATE_VERSION_3; 184 tbsCertificate->version.len = 1; 185 186 SECItem innerDER = {siBuffer, nullptr, 0}; 187 if (!SEC_ASN1EncodeItem(arena, &innerDER, tbsCertificate.get(), 188 SEC_ASN1_GET(CERT_CertificateTemplate))) { 189 return NS_ERROR_DOM_UNKNOWN_ERR; 190 } 191 192 SECItem* certDer = PORT_ArenaZNew(arena, SECItem); 193 if (!certDer) { 194 return NS_ERROR_DOM_UNKNOWN_ERR; 195 } 196 197 UniqueSECKEYPrivateKey privateKey(mKeyPair->mPrivateKey->GetPrivateKey()); 198 rv = SEC_DerSignData(arena, certDer, innerDER.data, innerDER.len, 199 privateKey.get(), mSignatureAlg); 200 if (rv != SECSuccess) { 201 return NS_ERROR_DOM_UNKNOWN_ERR; 202 } 203 204 mCertificate.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), certDer, 205 nullptr, false, true)); 206 if (!mCertificate) { 207 return NS_ERROR_DOM_UNKNOWN_ERR; 208 } 209 return NS_OK; 210 } 211 212 nsresult BeforeCrypto() override { 213 if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_RSASSA_PKCS1)) { 214 // Double check that size is OK. 215 auto sz = static_cast<size_t>(mRsaParams.keySizeInBits); 216 if (sz < RTCCertificateMinRsaSize) { 217 return NS_ERROR_DOM_NOT_SUPPORTED_ERR; 218 } 219 220 KeyAlgorithmProxy& alg = mKeyPair->mPublicKey->Algorithm(); 221 if (alg.mType != KeyAlgorithmProxy::RSA || 222 !alg.mRsa.mHash.mName.EqualsLiteral(WEBCRYPTO_ALG_SHA256)) { 223 return NS_ERROR_DOM_NOT_SUPPORTED_ERR; 224 } 225 226 mSignatureAlg = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; 227 mAuthType = ssl_kea_rsa; 228 229 } else if (mAlgName.EqualsLiteral(WEBCRYPTO_ALG_ECDSA)) { 230 // We only support good curves in WebCrypto. 231 // If that ever changes, check that a good one was chosen. 232 233 mSignatureAlg = SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE; 234 mAuthType = ssl_kea_ecdh; 235 } else { 236 return NS_ERROR_DOM_NOT_SUPPORTED_ERR; 237 } 238 return NS_OK; 239 } 240 241 nsresult DoCrypto() override { 242 nsresult rv = GenerateAsymmetricKeyTask::DoCrypto(); 243 NS_ENSURE_SUCCESS(rv, rv); 244 245 rv = GenerateCertificate(); 246 NS_ENSURE_SUCCESS(rv, rv); 247 248 return NS_OK; 249 } 250 251 virtual void Resolve() override { 252 // Make copies of the private key and certificate, otherwise, when this 253 // object is deleted, the structures they reference will be deleted too. 254 UniqueSECKEYPrivateKey key = mKeyPair->mPrivateKey->GetPrivateKey(); 255 CERTCertificate* cert = CERT_DupCertificate(mCertificate.get()); 256 RefPtr<RTCCertificate> result = 257 new RTCCertificate(mResultPromise->GetParentObject(), key.release(), 258 cert, mAuthType, mExpires); 259 mResultPromise->MaybeResolve(result); 260 } 261 }; 262 263 static PRTime ReadExpires(JSContext* aCx, const ObjectOrString& aOptions, 264 ErrorResult& aRv) { 265 // This conversion might fail, but we don't really care; use the default. 266 // If this isn't an object, or it doesn't coerce into the right type, 267 // then we won't get the |expires| value. Either will be caught later. 268 RTCCertificateExpiration expiration; 269 if (!aOptions.IsObject()) { 270 return EXPIRATION_DEFAULT; 271 } 272 JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*aOptions.GetAsObject())); 273 if (!expiration.Init(aCx, value)) { 274 aRv.NoteJSContextException(aCx); 275 return 0; 276 } 277 278 if (!expiration.mExpires.WasPassed()) { 279 return EXPIRATION_DEFAULT; 280 } 281 static const uint64_t max = 282 static_cast<uint64_t>(EXPIRATION_MAX / PR_USEC_PER_MSEC); 283 if (expiration.mExpires.Value() > max) { 284 return EXPIRATION_MAX; 285 } 286 return static_cast<PRTime>(expiration.mExpires.Value() * PR_USEC_PER_MSEC); 287 } 288 289 already_AddRefed<Promise> RTCCertificate::GenerateCertificate( 290 const GlobalObject& aGlobal, const ObjectOrString& aOptions, 291 ErrorResult& aRv, JS::Compartment* aCompartment) { 292 nsIGlobalObject* global = xpc::NativeGlobal(aGlobal.Get()); 293 RefPtr<Promise> p = Promise::Create(global, aRv); 294 if (aRv.Failed()) { 295 return nullptr; 296 } 297 Sequence<nsString> usages; 298 if (!usages.AppendElement(u"sign"_ns, fallible)) { 299 aRv.Throw(NS_ERROR_OUT_OF_MEMORY); 300 return nullptr; 301 } 302 303 PRTime expires = ReadExpires(aGlobal.Context(), aOptions, aRv); 304 if (aRv.Failed()) { 305 return nullptr; 306 } 307 RefPtr<WebCryptoTask> task = new GenerateRTCCertificateTask( 308 global, aGlobal.Context(), aOptions, usages, expires); 309 task->DispatchWithPromise(p); 310 return p.forget(); 311 } 312 313 RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal) 314 : mGlobal(aGlobal), 315 mPrivateKey(nullptr), 316 mCertificate(nullptr), 317 mAuthType(ssl_kea_null), 318 mExpires(0) {} 319 320 RTCCertificate::RTCCertificate(nsIGlobalObject* aGlobal, 321 SECKEYPrivateKey* aPrivateKey, 322 CERTCertificate* aCertificate, 323 SSLKEAType aAuthType, PRTime aExpires) 324 : mGlobal(aGlobal), 325 mPrivateKey(aPrivateKey), 326 mCertificate(aCertificate), 327 mAuthType(aAuthType), 328 mExpires(aExpires) {} 329 330 void RTCCertificate::GetFingerprints( 331 nsTArray<dom::RTCDtlsFingerprint>& aFingerprintsOut) { 332 // if we have a cert and haven't already built the fingerprints 333 if (mCertificate && mFingerprints.Length() == 0) { 334 DtlsDigest digest(DtlsIdentity::DEFAULT_HASH_ALGORITHM); 335 nsresult rv = DtlsIdentity::ComputeFingerprint(mCertificate, &digest); 336 if (NS_FAILED(rv)) { 337 // Safe to return early here since we didn't already have fingerprints 338 // and the call to DtlsIdentity::ComputeFingerprint failed. 339 return; 340 } 341 RTCDtlsFingerprint fingerprint; 342 fingerprint.mAlgorithm.Construct(NS_ConvertASCIItoUTF16(digest.algorithm_)); 343 344 std::string value = 345 SdpFingerprintAttributeList::FormatFingerprint(digest.value_); 346 // Sadly, the SDP fingerprint is expected to be all uppercase hex, 347 // while the RTC fingerprint is expected to be all lowercase hex. 348 std::transform(value.begin(), value.end(), value.begin(), ::tolower); 349 fingerprint.mValue.Construct(NS_ConvertASCIItoUTF16(value)); 350 351 mFingerprints.AppendElement(fingerprint); 352 } 353 354 aFingerprintsOut = mFingerprints.Clone(); 355 } 356 357 RefPtr<DtlsIdentity> RTCCertificate::CreateDtlsIdentity() const { 358 if (!mPrivateKey || !mCertificate) { 359 return nullptr; 360 } 361 UniqueSECKEYPrivateKey key(SECKEY_CopyPrivateKey(mPrivateKey.get())); 362 UniqueCERTCertificate cert(CERT_DupCertificate(mCertificate.get())); 363 RefPtr<DtlsIdentity> id = 364 new DtlsIdentity(std::move(key), std::move(cert), mAuthType); 365 return id; 366 } 367 368 JSObject* RTCCertificate::WrapObject(JSContext* aCx, 369 JS::Handle<JSObject*> aGivenProto) { 370 return RTCCertificate_Binding::Wrap(aCx, this, aGivenProto); 371 } 372 373 bool RTCCertificate::WritePrivateKey(JSStructuredCloneWriter* aWriter) const { 374 JsonWebKey jwk; 375 nsresult rv = CryptoKey::PrivateKeyToJwk(mPrivateKey.get(), jwk); 376 if (NS_FAILED(rv)) { 377 return false; 378 } 379 nsString json; 380 if (!jwk.ToJSON(json)) { 381 return false; 382 } 383 return StructuredCloneHolder::WriteString(aWriter, json); 384 } 385 386 bool RTCCertificate::WriteCertificate(JSStructuredCloneWriter* aWriter) const { 387 UniqueCERTCertificateList certs(CERT_CertListFromCert(mCertificate.get())); 388 if (!certs || certs->len <= 0) { 389 return false; 390 } 391 if (!JS_WriteUint32Pair(aWriter, certs->certs[0].len, 0)) { 392 return false; 393 } 394 return JS_WriteBytes(aWriter, certs->certs[0].data, certs->certs[0].len); 395 } 396 397 bool RTCCertificate::WriteStructuredClone( 398 JSContext* aCx, JSStructuredCloneWriter* aWriter) const { 399 if (!mPrivateKey || !mCertificate) { 400 return false; 401 } 402 403 return JS_WriteUint32Pair(aWriter, RTCCERTIFICATE_SC_VERSION, mAuthType) && 404 JS_WriteUint32Pair(aWriter, (mExpires >> 32) & 0xffffffff, 405 mExpires & 0xffffffff) && 406 WritePrivateKey(aWriter) && WriteCertificate(aWriter); 407 } 408 409 bool RTCCertificate::ReadPrivateKey(JSStructuredCloneReader* aReader) { 410 nsString json; 411 if (!StructuredCloneHolder::ReadString(aReader, json)) { 412 return false; 413 } 414 JsonWebKey jwk; 415 if (!jwk.Init(json)) { 416 return false; 417 } 418 mPrivateKey = CryptoKey::PrivateKeyFromJwk(jwk); 419 return !!mPrivateKey; 420 } 421 422 bool RTCCertificate::ReadCertificate(JSStructuredCloneReader* aReader) { 423 CryptoBuffer cert; 424 if (!ReadBuffer(aReader, cert) || cert.Length() == 0) { 425 return false; 426 } 427 428 SECItem der = {siBuffer, cert.Elements(), 429 static_cast<unsigned int>(cert.Length())}; 430 mCertificate.reset(CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &der, 431 nullptr, true, true)); 432 return !!mCertificate; 433 } 434 435 // static 436 already_AddRefed<RTCCertificate> RTCCertificate::ReadStructuredClone( 437 JSContext* aCx, nsIGlobalObject* aGlobal, 438 JSStructuredCloneReader* aReader) { 439 if (!NS_IsMainThread()) { 440 // These objects are mainthread-only. 441 return nullptr; 442 } 443 uint32_t version, authType; 444 if (!JS_ReadUint32Pair(aReader, &version, &authType) || 445 version != RTCCERTIFICATE_SC_VERSION) { 446 return nullptr; 447 } 448 RefPtr<RTCCertificate> cert = new RTCCertificate(aGlobal); 449 cert->mAuthType = static_cast<SSLKEAType>(authType); 450 451 uint32_t high, low; 452 if (!JS_ReadUint32Pair(aReader, &high, &low)) { 453 return nullptr; 454 } 455 cert->mExpires = static_cast<PRTime>(high) << 32 | low; 456 457 if (!cert->ReadPrivateKey(aReader) || !cert->ReadCertificate(aReader)) { 458 return nullptr; 459 } 460 461 return cert.forget(); 462 } 463 464 } // namespace mozilla::dom