dtlsidentity.cpp (10397B)
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/. */ 6 7 #include "dtlsidentity.h" 8 9 #include "cert.h" 10 #include "cryptohi.h" 11 #include "keyhi.h" 12 #include "mozilla/Sprintf.h" 13 #include "mozpkix/nss_scoped_ptrs.h" 14 #include "nsError.h" 15 #include "pk11pub.h" 16 #include "secerr.h" 17 #include "sechash.h" 18 #include "sslerr.h" 19 20 namespace mozilla { 21 22 SECItem* WrapPrivateKeyInfoWithEmptyPassword( 23 SECKEYPrivateKey* pk) /* encrypt this private key */ 24 { 25 if (!pk) { 26 PR_SetError(SEC_ERROR_INVALID_ARGS, 0); 27 return nullptr; 28 } 29 30 UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 31 if (!slot) { 32 return nullptr; 33 } 34 35 // For private keys, NSS cannot export anything other than RSA, but we need EC 36 // also. So, we use the private key encryption function to serialize instead, 37 // using a hard-coded dummy password; this is not intended to provide any 38 // additional security, it just works around a limitation in NSS. 39 SECItem dummyPassword = {siBuffer, nullptr, 0}; 40 UniqueSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( 41 slot.get(), SEC_OID_AES_128_CBC, &dummyPassword, pk, 1, nullptr)); 42 43 if (!epki) { 44 return nullptr; 45 } 46 47 return SEC_ASN1EncodeItem( 48 nullptr, nullptr, epki.get(), 49 NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false)); 50 } 51 52 SECStatus UnwrapPrivateKeyInfoWithEmptyPassword( 53 SECItem* derPKI, const UniqueCERTCertificate& aCert, 54 SECKEYPrivateKey** privk) { 55 if (!derPKI || !aCert || !privk) { 56 PR_SetError(SEC_ERROR_INVALID_ARGS, 0); 57 return SECFailure; 58 } 59 60 UniqueSECKEYPublicKey publicKey(CERT_ExtractPublicKey(aCert.get())); 61 // This is a pointer to data inside publicKey 62 SECItem* publicValue = nullptr; 63 switch (publicKey->keyType) { 64 case dsaKey: 65 publicValue = &publicKey->u.dsa.publicValue; 66 break; 67 case dhKey: 68 publicValue = &publicKey->u.dh.publicValue; 69 break; 70 case rsaKey: 71 publicValue = &publicKey->u.rsa.modulus; 72 break; 73 case ecKey: 74 publicValue = &publicKey->u.ec.publicValue; 75 break; 76 default: 77 MOZ_ASSERT(false); 78 PR_SetError(SSL_ERROR_BAD_CERTIFICATE, 0); 79 return SECFailure; 80 } 81 82 UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 83 if (!slot) { 84 return SECFailure; 85 } 86 87 UniquePLArenaPool temparena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); 88 if (!temparena) { 89 return SECFailure; 90 } 91 92 SECKEYEncryptedPrivateKeyInfo* epki = 93 PORT_ArenaZNew(temparena.get(), SECKEYEncryptedPrivateKeyInfo); 94 if (!epki) { 95 return SECFailure; 96 } 97 98 SECStatus rv = SEC_ASN1DecodeItem( 99 temparena.get(), epki, 100 NSS_Get_SECKEY_EncryptedPrivateKeyInfoTemplate(nullptr, false), derPKI); 101 if (rv != SECSuccess) { 102 // If SEC_ASN1DecodeItem fails, we cannot assume anything about the 103 // validity of the data in epki. The best we can do is free the arena 104 // and return. 105 return rv; 106 } 107 108 // See comment in WrapPrivateKeyInfoWithEmptyPassword about this 109 // dummy password stuff. 110 SECItem dummyPassword = {siBuffer, nullptr, 0}; 111 return PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( 112 slot.get(), epki, &dummyPassword, nullptr, publicValue, false, false, 113 publicKey->keyType, KU_ALL, privk, nullptr); 114 } 115 116 nsresult DtlsIdentity::Serialize(nsTArray<uint8_t>* aKeyDer, 117 nsTArray<uint8_t>* aCertDer) { 118 ScopedSECItem derPki(WrapPrivateKeyInfoWithEmptyPassword(private_key_.get())); 119 if (!derPki) { 120 return NS_ERROR_FAILURE; 121 } 122 123 aKeyDer->AppendElements(derPki->data, derPki->len); 124 aCertDer->AppendElements(cert_->derCert.data, cert_->derCert.len); 125 return NS_OK; 126 } 127 128 /* static */ 129 RefPtr<DtlsIdentity> DtlsIdentity::Deserialize( 130 const nsTArray<uint8_t>& aKeyDer, const nsTArray<uint8_t>& aCertDer, 131 SSLKEAType authType) { 132 SECItem certDer = {siBuffer, const_cast<uint8_t*>(aCertDer.Elements()), 133 static_cast<unsigned int>(aCertDer.Length())}; 134 UniqueCERTCertificate cert(CERT_NewTempCertificate( 135 CERT_GetDefaultCertDB(), &certDer, nullptr, true, true)); 136 137 SECItem derPKI = {siBuffer, const_cast<uint8_t*>(aKeyDer.Elements()), 138 static_cast<unsigned int>(aKeyDer.Length())}; 139 140 SECKEYPrivateKey* privateKey; 141 if (UnwrapPrivateKeyInfoWithEmptyPassword(&derPKI, cert, &privateKey) != 142 SECSuccess) { 143 MOZ_ASSERT(false); 144 return nullptr; 145 } 146 147 return new DtlsIdentity(UniqueSECKEYPrivateKey(privateKey), std::move(cert), 148 authType); 149 } 150 151 RefPtr<DtlsIdentity> DtlsIdentity::Generate() { 152 UniquePK11SlotInfo slot(PK11_GetInternalSlot()); 153 if (!slot) { 154 return nullptr; 155 } 156 157 uint8_t random_name[16]; 158 159 SECStatus rv = 160 PK11_GenerateRandomOnSlot(slot.get(), random_name, sizeof(random_name)); 161 if (rv != SECSuccess) return nullptr; 162 163 std::string name; 164 char chunk[3]; 165 for (unsigned char r_name : random_name) { 166 SprintfLiteral(chunk, "%.2x", r_name); 167 name += chunk; 168 } 169 170 std::string subject_name_string = "CN=" + name; 171 UniqueCERTName subject_name(CERT_AsciiToName(subject_name_string.c_str())); 172 if (!subject_name) { 173 return nullptr; 174 } 175 176 unsigned char paramBuf[12]; // OIDs are small 177 SECItem ecdsaParams = {siBuffer, paramBuf, sizeof(paramBuf)}; 178 SECOidData* oidData = SECOID_FindOIDByTag(SEC_OID_SECG_EC_SECP256R1); 179 if (!oidData || (oidData->oid.len > (sizeof(paramBuf) - 2))) { 180 return nullptr; 181 } 182 ecdsaParams.data[0] = SEC_ASN1_OBJECT_ID; 183 ecdsaParams.data[1] = oidData->oid.len; 184 memcpy(ecdsaParams.data + 2, oidData->oid.data, oidData->oid.len); 185 ecdsaParams.len = oidData->oid.len + 2; 186 187 SECKEYPublicKey* pubkey; 188 UniqueSECKEYPrivateKey private_key( 189 PK11_GenerateKeyPair(slot.get(), CKM_EC_KEY_PAIR_GEN, &ecdsaParams, 190 &pubkey, PR_FALSE, PR_TRUE, nullptr)); 191 if (private_key == nullptr) return nullptr; 192 UniqueSECKEYPublicKey public_key(pubkey); 193 pubkey = nullptr; 194 195 UniqueCERTSubjectPublicKeyInfo spki( 196 SECKEY_CreateSubjectPublicKeyInfo(public_key.get())); 197 if (!spki) { 198 return nullptr; 199 } 200 201 UniqueCERTCertificateRequest certreq( 202 CERT_CreateCertificateRequest(subject_name.get(), spki.get(), nullptr)); 203 if (!certreq) { 204 return nullptr; 205 } 206 207 // From 1 day before todayto 30 days after. 208 // This is a sort of arbitrary range designed to be valid 209 // now with some slack in case the other side expects 210 // some before expiry. 211 // 212 // Note: explicit casts necessary to avoid 213 // warning C4307: '*' : integral constant overflow 214 static const PRTime oneDay = PRTime(PR_USEC_PER_SEC) * PRTime(60) // sec 215 * PRTime(60) // min 216 * PRTime(24); // hours 217 PRTime now = PR_Now(); 218 PRTime notBefore = now - oneDay; 219 PRTime notAfter = now + (PRTime(30) * oneDay); 220 221 UniqueCERTValidity validity(CERT_CreateValidity(notBefore, notAfter)); 222 if (!validity) { 223 return nullptr; 224 } 225 226 unsigned long serial; 227 // Note: This serial in principle could collide, but it's unlikely 228 rv = PK11_GenerateRandomOnSlot( 229 slot.get(), reinterpret_cast<unsigned char*>(&serial), sizeof(serial)); 230 if (rv != SECSuccess) { 231 return nullptr; 232 } 233 234 // NB: CERTCertificates created with CERT_CreateCertificate are not safe to 235 // use with other NSS functions like CERT_DupCertificate. 236 // The strategy here is to create a tbsCertificate ("to-be-signed 237 // certificate"), encode it, and sign it, resulting in a signed DER 238 // certificate that can be decoded into a CERTCertificate. 239 UniqueCERTCertificate tbsCertificate(CERT_CreateCertificate( 240 serial, subject_name.get(), validity.get(), certreq.get())); 241 if (!tbsCertificate) { 242 return nullptr; 243 } 244 245 PLArenaPool* arena = tbsCertificate->arena; 246 247 rv = SECOID_SetAlgorithmID(arena, &tbsCertificate->signature, 248 SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE, nullptr); 249 if (rv != SECSuccess) return nullptr; 250 251 // Set version to X509v3. 252 *(tbsCertificate->version.data) = SEC_CERTIFICATE_VERSION_3; 253 tbsCertificate->version.len = 1; 254 255 SECItem innerDER; 256 innerDER.len = 0; 257 innerDER.data = nullptr; 258 259 if (!SEC_ASN1EncodeItem(arena, &innerDER, tbsCertificate.get(), 260 SEC_ASN1_GET(CERT_CertificateTemplate))) { 261 return nullptr; 262 } 263 264 SECItem* certDer = PORT_ArenaZNew(arena, SECItem); 265 if (!certDer) { 266 return nullptr; 267 } 268 269 rv = SEC_DerSignData(arena, certDer, innerDER.data, innerDER.len, 270 private_key.get(), 271 SEC_OID_ANSIX962_ECDSA_SHA256_SIGNATURE); 272 if (rv != SECSuccess) { 273 return nullptr; 274 } 275 276 UniqueCERTCertificate certificate(CERT_NewTempCertificate( 277 CERT_GetDefaultCertDB(), certDer, nullptr, false, true)); 278 279 return new DtlsIdentity(std::move(private_key), std::move(certificate), 280 ssl_kea_ecdh); 281 } 282 283 constexpr nsLiteralCString DtlsIdentity::DEFAULT_HASH_ALGORITHM; 284 285 nsresult DtlsIdentity::ComputeFingerprint(DtlsDigest* digest) const { 286 const UniqueCERTCertificate& c = cert(); 287 MOZ_ASSERT(c); 288 289 return ComputeFingerprint(c, digest); 290 } 291 292 nsresult DtlsIdentity::ComputeFingerprint(const UniqueCERTCertificate& cert, 293 DtlsDigest* digest) { 294 MOZ_ASSERT(cert); 295 296 HASH_HashType ht; 297 298 if (digest->algorithm_ == "sha-1") { 299 ht = HASH_AlgSHA1; 300 } else if (digest->algorithm_ == "sha-224") { 301 ht = HASH_AlgSHA224; 302 } else if (digest->algorithm_ == "sha-256") { 303 ht = HASH_AlgSHA256; 304 } else if (digest->algorithm_ == "sha-384") { 305 ht = HASH_AlgSHA384; 306 } else if (digest->algorithm_ == "sha-512") { 307 ht = HASH_AlgSHA512; 308 } else { 309 return NS_ERROR_INVALID_ARG; 310 } 311 312 const SECHashObject* ho = HASH_GetHashObject(ht); 313 MOZ_ASSERT(ho); 314 if (!ho) { 315 return NS_ERROR_INVALID_ARG; 316 } 317 318 MOZ_ASSERT(ho->length >= 20); // Double check 319 digest->value_.resize(ho->length); 320 321 SECStatus rv = HASH_HashBuf(ho->type, digest->value_.data(), 322 cert->derCert.data, cert->derCert.len); 323 if (rv != SECSuccess) { 324 return NS_ERROR_FAILURE; 325 } 326 327 return NS_OK; 328 } 329 330 } // namespace mozilla