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
InitNSSIfNeeded()50 InitNSSIfNeeded()
51 {
52 if (NSS_NoDB_Init(nullptr) != SECSuccess) {
53 abort();
54 }
55 }
56
57 static ScopedTestKeyPair reusedKeyPair;
58
59 PRStatus
InitReusedKeyPair()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:
NSSTestKeyPair(const TestPublicKeyAlgorithm & aPublicKeyAlg,const ByteString & spk,const ByteString & aEncryptedPrivateKey,const ByteString & aEncryptionAlgorithm,const ByteString & aEncryptionParams)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
SignData(const ByteString & tbs,const TestSignatureAlgorithm & signatureAlgorithm,ByteString & signature) const82 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
Clone() const167 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).
CreateTestKeyPair(const TestPublicKeyAlgorithm publicKeyAlg,const ScopedSECKEYPublicKey & publicKey,const ScopedSECKEYPrivateKey & privateKey)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*
GenerateKeyPairInner()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*
GenerateKeyPair()278 GenerateKeyPair()
279 {
280 InitNSSIfNeeded();
281 return GenerateKeyPairInner();
282 }
283
284 TestKeyPair*
CloneReusedKeyPair()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*
GenerateDSSKeyPair()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
TestVerifyECDSASignedDigest(const SignedDigest & signedDigest,Input subjectPublicKeyInfo)338 TestVerifyECDSASignedDigest(const SignedDigest& signedDigest,
339 Input subjectPublicKeyInfo)
340 {
341 InitNSSIfNeeded();
342 return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo,
343 nullptr);
344 }
345
346 Result
TestVerifyRSAPKCS1SignedDigest(const SignedDigest & signedDigest,Input subjectPublicKeyInfo)347 TestVerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
348 Input subjectPublicKeyInfo)
349 {
350 InitNSSIfNeeded();
351 return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo,
352 nullptr);
353 }
354
355 Result
TestDigestBuf(Input item,DigestAlgorithm digestAlg,uint8_t * digestBuf,size_t digestBufLen)356 TestDigestBuf(Input item,
357 DigestAlgorithm digestAlg,
358 /*out*/ uint8_t* digestBuf,
359 size_t digestBufLen)
360 {
361 InitNSSIfNeeded();
362 return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
363 }
364
365 } } } // namespace mozilla::pkix::test
366