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                                       &params, &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