pkixtestnss.cpp (11724B)
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 code is made available to you under your choice of the following sets 4 * of licensing terms: 5 */ 6 /* This Source Code Form is subject to the terms of the Mozilla Public 7 * License, v. 2.0. If a copy of the MPL was not distributed with this 8 * file, You can obtain one at http://mozilla.org/MPL/2.0/. 9 */ 10 /* Copyright 2013 Mozilla Contributors 11 * 12 * Licensed under the Apache License, Version 2.0 (the "License"); 13 * you may not use this file except in compliance with the License. 14 * You may obtain a copy of the License at 15 * 16 * http://www.apache.org/licenses/LICENSE-2.0 17 * 18 * Unless required by applicable law or agreed to in writing, software 19 * distributed under the License is distributed on an "AS IS" BASIS, 20 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 21 * See the License for the specific language governing permissions and 22 * limitations under the License. 23 */ 24 25 #include "mozpkix/test/pkixtestutil.h" 26 #include "mozpkix/test/pkixtestnss.h" 27 28 #include <limits> 29 30 #include "cryptohi.h" 31 #include "keyhi.h" 32 #include "nss.h" 33 #include "pk11pqg.h" 34 #include "pk11pub.h" 35 #include "mozpkix/nss_scoped_ptrs.h" 36 #include "mozpkix/pkixnss.h" 37 #include "mozpkix/pkixder.h" 38 #include "mozpkix/pkixutil.h" 39 #include "prinit.h" 40 #include "secerr.h" 41 #include "secitem.h" 42 43 namespace mozilla { namespace pkix { namespace test { 44 45 namespace { 46 47 TestKeyPair* GenerateKeyPairInner(); 48 49 void 50 InitNSSIfNeeded() 51 { 52 if (NSS_NoDB_Init(nullptr) != SECSuccess) { 53 abort(); 54 } 55 } 56 57 static ScopedTestKeyPair reusedKeyPair; 58 59 PRStatus 60 InitReusedKeyPair() 61 { 62 InitNSSIfNeeded(); 63 reusedKeyPair.reset(GenerateKeyPairInner()); 64 return reusedKeyPair ? PR_SUCCESS : PR_FAILURE; 65 } 66 67 class NSSTestKeyPair final : public TestKeyPair 68 { 69 public: 70 NSSTestKeyPair(const TestPublicKeyAlgorithm& aPublicKeyAlg, 71 const ByteString& spk, 72 const ByteString& aEncryptedPrivateKey, 73 const ByteString& aEncryptionAlgorithm, 74 const ByteString& aEncryptionParams) 75 : TestKeyPair(aPublicKeyAlg, spk) 76 , encryptedPrivateKey(aEncryptedPrivateKey) 77 , encryptionAlgorithm(aEncryptionAlgorithm) 78 , encryptionParams(aEncryptionParams) 79 { 80 } 81 82 Result SignData(const ByteString& tbs, 83 const TestSignatureAlgorithm& signatureAlgorithm, 84 /*out*/ ByteString& signature) const override 85 { 86 SECOidTag oidTag; 87 if (signatureAlgorithm.publicKeyAlg == RSA_PKCS1()) { 88 switch (signatureAlgorithm.digestAlg) { 89 case TestDigestAlgorithmID::MD2: 90 oidTag = SEC_OID_PKCS1_MD2_WITH_RSA_ENCRYPTION; 91 break; 92 case TestDigestAlgorithmID::MD5: 93 oidTag = SEC_OID_PKCS1_MD5_WITH_RSA_ENCRYPTION; 94 break; 95 case TestDigestAlgorithmID::SHA1: 96 oidTag = SEC_OID_PKCS1_SHA1_WITH_RSA_ENCRYPTION; 97 break; 98 case TestDigestAlgorithmID::SHA224: 99 oidTag = SEC_OID_PKCS1_SHA224_WITH_RSA_ENCRYPTION; 100 break; 101 case TestDigestAlgorithmID::SHA256: 102 oidTag = SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION; 103 break; 104 case TestDigestAlgorithmID::SHA384: 105 oidTag = SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION; 106 break; 107 case TestDigestAlgorithmID::SHA512: 108 oidTag = SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION; 109 break; 110 MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM 111 } 112 } else { 113 abort(); 114 } 115 116 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 117 if (!slot) { 118 return MapPRErrorCodeToResult(PR_GetError()); 119 } 120 SECItem encryptedPrivateKeyInfoItem = { 121 siBuffer, 122 const_cast<uint8_t*>(encryptedPrivateKey.data()), 123 static_cast<unsigned int>(encryptedPrivateKey.length()) 124 }; 125 SECItem encryptionAlgorithmItem = { 126 siBuffer, 127 const_cast<uint8_t*>(encryptionAlgorithm.data()), 128 static_cast<unsigned int>(encryptionAlgorithm.length()) 129 }; 130 SECItem encryptionParamsItem = { 131 siBuffer, 132 const_cast<uint8_t*>(encryptionParams.data()), 133 static_cast<unsigned int>(encryptionParams.length()) 134 }; 135 SECKEYEncryptedPrivateKeyInfo encryptedPrivateKeyInfo = { 136 nullptr, 137 { encryptionAlgorithmItem, encryptionParamsItem }, 138 encryptedPrivateKeyInfoItem 139 }; 140 SECItem passwordItem = { siBuffer, nullptr, 0 }; 141 SECItem publicValueItem = { 142 siBuffer, 143 const_cast<uint8_t*>(subjectPublicKey.data()), 144 static_cast<unsigned int>(subjectPublicKey.length()) 145 }; 146 SECKEYPrivateKey* privateKey; 147 // This should always be an RSA key (we'll have aborted above if we're not 148 // doing an RSA signature). 149 if (PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( 150 slot.get(), &encryptedPrivateKeyInfo, &passwordItem, nullptr, 151 &publicValueItem, false, false, rsaKey, KU_ALL, &privateKey, 152 nullptr) != SECSuccess) { 153 return MapPRErrorCodeToResult(PR_GetError()); 154 } 155 ScopedSECKEYPrivateKey scopedPrivateKey(privateKey); 156 SECItem signatureItem; 157 if (SEC_SignData(&signatureItem, tbs.data(), 158 static_cast<int>(tbs.length()), 159 scopedPrivateKey.get(), oidTag) != SECSuccess) { 160 return MapPRErrorCodeToResult(PR_GetError()); 161 } 162 signature.assign(signatureItem.data, signatureItem.len); 163 SECITEM_FreeItem(&signatureItem, false); 164 return Success; 165 } 166 167 TestKeyPair* Clone() const override 168 { 169 return new (std::nothrow) NSSTestKeyPair(publicKeyAlg, 170 subjectPublicKey, 171 encryptedPrivateKey, 172 encryptionAlgorithm, 173 encryptionParams); 174 } 175 176 private: 177 const ByteString encryptedPrivateKey; 178 const ByteString encryptionAlgorithm; 179 const ByteString encryptionParams; 180 }; 181 182 } // namespace 183 184 // This private function is also used by Gecko's PSM test framework 185 // (OCSPCommon.cpp). 186 TestKeyPair* CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg, 187 const ScopedSECKEYPublicKey& publicKey, 188 const ScopedSECKEYPrivateKey& privateKey) 189 { 190 ScopedCERTSubjectPublicKeyInfo 191 spki(SECKEY_CreateSubjectPublicKeyInfo(publicKey.get())); 192 if (!spki) { 193 return nullptr; 194 } 195 SECItem spkDER = spki->subjectPublicKey; 196 DER_ConvertBitString(&spkDER); // bits to bytes 197 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 198 if (!slot) { 199 return nullptr; 200 } 201 // Because NSSTestKeyPair isn't tracked by XPCOM and won't otherwise be aware 202 // of shutdown, we don't have a way to release NSS resources at the 203 // appropriate time. To work around this, NSSTestKeyPair doesn't hold on to 204 // NSS resources. Instead, we export the generated private key part as an 205 // encrypted blob (with an empty password and fairly lame encryption). When we 206 // need to use it (e.g. to sign something), we decrypt it and create a 207 // temporary key object. 208 SECItem passwordItem = { siBuffer, nullptr, 0 }; 209 ScopedSECKEYEncryptedPrivateKeyInfo encryptedPrivateKey( 210 PK11_ExportEncryptedPrivKeyInfo( 211 slot.get(), SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC, 212 &passwordItem, privateKey.get(), 1, nullptr)); 213 if (!encryptedPrivateKey) { 214 return nullptr; 215 } 216 217 return new (std::nothrow) NSSTestKeyPair( 218 publicKeyAlg, 219 ByteString(spkDER.data, spkDER.len), 220 ByteString(encryptedPrivateKey->encryptedData.data, 221 encryptedPrivateKey->encryptedData.len), 222 ByteString(encryptedPrivateKey->algorithm.algorithm.data, 223 encryptedPrivateKey->algorithm.algorithm.len), 224 ByteString(encryptedPrivateKey->algorithm.parameters.data, 225 encryptedPrivateKey->algorithm.parameters.len)); 226 } 227 228 namespace { 229 230 TestKeyPair* 231 GenerateKeyPairInner() 232 { 233 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 234 if (!slot) { 235 abort(); 236 } 237 PK11RSAGenParams params; 238 params.keySizeInBits = 2048; 239 params.pe = 65537; 240 241 // Bug 1012786: PK11_GenerateKeyPair can fail if there is insufficient 242 // entropy to generate a random key. Attempting to add some entropy and 243 // retrying appears to solve this issue. 244 for (uint32_t retries = 0; retries < 10; retries++) { 245 SECKEYPublicKey* publicKeyTemp = nullptr; 246 ScopedSECKEYPrivateKey 247 privateKey(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN, 248 ¶ms, &publicKeyTemp, false, true, 249 nullptr)); 250 ScopedSECKEYPublicKey publicKey(publicKeyTemp); 251 if (privateKey) { 252 return CreateTestKeyPair(RSA_PKCS1(), publicKey, privateKey); 253 } 254 255 assert(!publicKeyTemp); 256 257 if (PR_GetError() != SEC_ERROR_PKCS11_FUNCTION_FAILED) { 258 break; 259 } 260 261 // Since these keys are only for testing, we don't need them to be good, 262 // random keys. 263 // https://xkcd.com/221/ 264 static const uint8_t RANDOM_NUMBER[] = { 4, 4, 4, 4, 4, 4, 4, 4 }; 265 if (PK11_RandomUpdate( 266 const_cast<void*>(reinterpret_cast<const void*>(RANDOM_NUMBER)), 267 sizeof(RANDOM_NUMBER)) != SECSuccess) { 268 break; 269 } 270 } 271 272 abort(); 273 } 274 275 } // namespace 276 277 TestKeyPair* 278 GenerateKeyPair() 279 { 280 InitNSSIfNeeded(); 281 return GenerateKeyPairInner(); 282 } 283 284 TestKeyPair* 285 CloneReusedKeyPair() 286 { 287 static PRCallOnceType initCallOnce; 288 if (PR_CallOnce(&initCallOnce, InitReusedKeyPair) != PR_SUCCESS) { 289 abort(); 290 } 291 assert(reusedKeyPair); 292 return reusedKeyPair->Clone(); 293 } 294 295 TestKeyPair* 296 GenerateDSSKeyPair() 297 { 298 InitNSSIfNeeded(); 299 300 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 301 if (!slot) { 302 return nullptr; 303 } 304 305 ByteString p(DSS_P()); 306 ByteString q(DSS_Q()); 307 ByteString g(DSS_G()); 308 309 static const PQGParams PARAMS = { 310 nullptr, 311 { siBuffer, 312 const_cast<uint8_t*>(p.data()), 313 static_cast<unsigned int>(p.length()) 314 }, 315 { siBuffer, 316 const_cast<uint8_t*>(q.data()), 317 static_cast<unsigned int>(q.length()) 318 }, 319 { siBuffer, 320 const_cast<uint8_t*>(g.data()), 321 static_cast<unsigned int>(g.length()) 322 } 323 }; 324 325 SECKEYPublicKey* publicKeyTemp = nullptr; 326 ScopedSECKEYPrivateKey 327 privateKey(PK11_GenerateKeyPair(slot.get(), CKM_DSA_KEY_PAIR_GEN, 328 const_cast<PQGParams*>(&PARAMS), 329 &publicKeyTemp, false, true, nullptr)); 330 if (!privateKey) { 331 return nullptr; 332 } 333 ScopedSECKEYPublicKey publicKey(publicKeyTemp); 334 return CreateTestKeyPair(DSS(), publicKey, privateKey); 335 } 336 337 Result 338 TestVerifyECDSASignedData(Input data, DigestAlgorithm digestAlgorithm, 339 Input signature, Input subjectPublicKeyInfo) 340 { 341 InitNSSIfNeeded(); 342 return VerifyECDSASignedDataNSS(data, digestAlgorithm, signature, 343 subjectPublicKeyInfo, nullptr); 344 } 345 346 Result 347 TestVerifyRSAPKCS1SignedData(Input data, DigestAlgorithm digestAlgorithm, 348 Input signature, Input subjectPublicKeyInfo) 349 { 350 InitNSSIfNeeded(); 351 return VerifyRSAPKCS1SignedDataNSS(data, digestAlgorithm, signature, 352 subjectPublicKeyInfo, nullptr); 353 } 354 355 Result 356 TestVerifyRSAPSSSignedData(Input data, DigestAlgorithm digestAlgorithm, 357 Input signature, Input subjectPublicKeyInfo) 358 { 359 InitNSSIfNeeded(); 360 return VerifyRSAPSSSignedDataNSS(data, digestAlgorithm, signature, 361 subjectPublicKeyInfo, nullptr); 362 } 363 364 Result 365 TestDigestBuf(Input item, 366 DigestAlgorithm digestAlg, 367 /*out*/ uint8_t* digestBuf, 368 size_t digestBufLen) 369 { 370 InitNSSIfNeeded(); 371 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); 372 } 373 374 } } } // namespace mozilla::pkix::test