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 <memory>
8 #include "nss.h"
9 #include "pk11pub.h"
10 #include "pk11pqg.h"
11 #include "prerror.h"
12 #include "secoid.h"
13 
14 #include "cpputil.h"
15 #include "nss_scoped_ptrs.h"
16 #include "gtest/gtest.h"
17 #include "databuffer.h"
18 #include "pk11_keygen.h"
19 
20 namespace nss_test {
21 
22 // This deleter deletes a set of objects, unlike the deleter on
23 // ScopedPK11GenericObject, which only deletes one.
24 struct PK11GenericObjectsDeleter {
operator ()nss_test::PK11GenericObjectsDeleter25   void operator()(PK11GenericObject* objs) {
26     if (objs) {
27       PK11_DestroyGenericObjects(objs);
28     }
29   }
30 };
31 
32 class Pk11KeyImportTestBase : public ::testing::Test {
33  public:
34   Pk11KeyImportTestBase() = default;
35   virtual ~Pk11KeyImportTestBase() = default;
36 
SetUp()37   void SetUp() override {
38     slot_.reset(PK11_GetInternalKeySlot());
39     ASSERT_TRUE(slot_);
40 
41     static const uint8_t pw[] = "pw";
42     SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)};
43     password_.reset(SECITEM_DupItem(&pwItem));
44   }
45 
Test(const Pkcs11KeyPairGenerator & generator)46   void Test(const Pkcs11KeyPairGenerator& generator) {
47     // Generate a key and export it.
48     KeyType key_type = nullKey;
49     ScopedSECKEYEncryptedPrivateKeyInfo key_info;
50     ScopedSECItem public_value;
51     GenerateAndExport(generator, &key_type, &key_info, &public_value);
52 
53     // Note: NSS is currently unable export wrapped DH keys, so this doesn't
54     // test those beyond generate and verify.
55     if (key_type == dhKey) {
56       return;
57     }
58     ASSERT_NE(nullptr, public_value);
59     ASSERT_NE(nullptr, key_info);
60 
61     // Now import the encrypted key.
62     static const uint8_t nick[] = "nick";
63     SECItem nickname = {siBuffer, toUcharPtr(nick), sizeof(nick)};
64     SECKEYPrivateKey* priv_tmp;
65     SECStatus rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
66         slot_.get(), key_info.get(), password_.get(), &nickname,
67         public_value.get(), PR_TRUE, PR_TRUE, key_type, 0, &priv_tmp, NULL);
68     ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed "
69                               << PORT_ErrorToName(PORT_GetError());
70     ScopedSECKEYPrivateKey priv_key(priv_tmp);
71     ASSERT_NE(nullptr, priv_key);
72 
73     CheckForPublicKey(priv_key, public_value.get());
74   }
75 
76  private:
GetPublicComponent(ScopedSECKEYPublicKey & pub_key)77   SECItem GetPublicComponent(ScopedSECKEYPublicKey& pub_key) {
78     SECItem null = {siBuffer, NULL, 0};
79     switch (SECKEY_GetPublicKeyType(pub_key.get())) {
80       case rsaKey:
81       case rsaPssKey:
82       case rsaOaepKey:
83         return pub_key->u.rsa.modulus;
84       case keaKey:
85         return pub_key->u.kea.publicValue;
86       case dsaKey:
87         return pub_key->u.dsa.publicValue;
88       case dhKey:
89         return pub_key->u.dh.publicValue;
90       case ecKey:
91         return pub_key->u.ec.publicValue;
92       case fortezzaKey: /* depricated */
93       case nullKey:
94         /* didn't use default here so we can catch new key types at compile time
95          */
96         break;
97     }
98     return null;
99   }
CheckForPublicKey(const ScopedSECKEYPrivateKey & priv_key,const SECItem * expected_public)100   void CheckForPublicKey(const ScopedSECKEYPrivateKey& priv_key,
101                          const SECItem* expected_public) {
102     // Verify the public key exists.
103     StackSECItem priv_id;
104     KeyType type = SECKEY_GetPrivateKeyType(priv_key.get());
105     SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, priv_key.get(),
106                                          CKA_ID, &priv_id);
107     ASSERT_EQ(SECSuccess, rv) << "Couldn't read CKA_ID from private key: "
108                               << PORT_ErrorToName(PORT_GetError());
109 
110     CK_ATTRIBUTE_TYPE value_type = CKA_VALUE;
111     switch (type) {
112       case rsaKey:
113         value_type = CKA_MODULUS;
114         break;
115 
116       case dhKey:
117       case dsaKey:
118         value_type = CKA_VALUE;
119         break;
120 
121       case ecKey:
122         value_type = CKA_EC_POINT;
123         break;
124 
125       default:
126         FAIL() << "unknown key type";
127     }
128 
129     // Scan public key objects until we find one with the same CKA_ID as
130     // priv_key
131     std::unique_ptr<PK11GenericObject, PK11GenericObjectsDeleter> objs(
132         PK11_FindGenericObjects(slot_.get(), CKO_PUBLIC_KEY));
133     ASSERT_NE(nullptr, objs);
134     for (PK11GenericObject* obj = objs.get(); obj != nullptr;
135          obj = PK11_GetNextGenericObject(obj)) {
136       StackSECItem pub_id;
137       rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pub_id);
138       if (rv != SECSuccess) {
139         // Can't read CKA_ID from object.
140         continue;
141       }
142       if (!SECITEM_ItemsAreEqual(&priv_id, &pub_id)) {
143         // This isn't the object we're looking for.
144         continue;
145       }
146 
147       StackSECItem token;
148       rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token);
149       ASSERT_EQ(SECSuccess, rv);
150       ASSERT_EQ(1U, token.len);
151       ASSERT_NE(0, token.data[0]);
152 
153       StackSECItem raw_value;
154       SECItem decoded_value;
155       rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &raw_value);
156       ASSERT_EQ(SECSuccess, rv);
157       SECItem value = raw_value;
158 
159       // Decode the EC_POINT and check the output against expected.
160       // CKA_EC_POINT isn't stable, see Bug 1520649.
161       ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
162       ASSERT_TRUE(arena);
163       if (value_type == CKA_EC_POINT) {
164         // If this fails due to the noted inconsistency, we may need to
165         // check the whole raw_value, or remove a leading UNCOMPRESSED_POINT tag
166         rv = SEC_QuickDERDecodeItem(arena.get(), &decoded_value,
167                                     SEC_ASN1_GET(SEC_OctetStringTemplate),
168                                     &raw_value);
169         ASSERT_EQ(SECSuccess, rv);
170         value = decoded_value;
171       }
172       ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &value))
173           << "expected: "
174           << DataBuffer(expected_public->data, expected_public->len)
175           << std::endl
176           << "actual: " << DataBuffer(value.data, value.len) << std::endl;
177 
178       // Finally, convert the private to public and ensure it matches.
179       ScopedSECKEYPublicKey pub_key(SECKEY_ConvertToPublicKey(priv_key.get()));
180       ASSERT_TRUE(pub_key);
181       SECItem converted_public = GetPublicComponent(pub_key);
182       ASSERT_TRUE(converted_public.len != 0);
183 
184       ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &converted_public))
185           << "expected: "
186           << DataBuffer(expected_public->data, expected_public->len)
187           << std::endl
188           << "actual: "
189           << DataBuffer(converted_public.data, converted_public.len)
190           << std::endl;
191     }
192   }
193 
GenerateAndExport(const Pkcs11KeyPairGenerator & generator,KeyType * key_type,ScopedSECKEYEncryptedPrivateKeyInfo * key_info,ScopedSECItem * public_value)194   void GenerateAndExport(const Pkcs11KeyPairGenerator& generator,
195                          KeyType* key_type,
196                          ScopedSECKEYEncryptedPrivateKeyInfo* key_info,
197                          ScopedSECItem* public_value) {
198     ScopedSECKEYPrivateKey priv_key;
199     ScopedSECKEYPublicKey pub_key;
200     generator.GenerateKey(&priv_key, &pub_key);
201     ASSERT_TRUE(priv_key);
202 
203     // Save the public value, which we will need on import */
204     SECItem* pub_val;
205     KeyType t = SECKEY_GetPublicKeyType(pub_key.get());
206     switch (t) {
207       case rsaKey:
208         pub_val = &pub_key->u.rsa.modulus;
209         break;
210       case dhKey:
211         pub_val = &pub_key->u.dh.publicValue;
212         break;
213       case dsaKey:
214         pub_val = &pub_key->u.dsa.publicValue;
215         break;
216       case ecKey:
217         pub_val = &pub_key->u.ec.publicValue;
218         break;
219       default:
220         FAIL() << "Unknown key type";
221     }
222 
223     CheckForPublicKey(priv_key, pub_val);
224 
225     *key_type = t;
226     // Note: NSS is currently unable export wrapped DH keys, so this doesn't
227     // test those beyond generate and verify.
228     if (t == dhKey) {
229       return;
230     }
231     public_value->reset(SECITEM_DupItem(pub_val));
232 
233     // Wrap and export the key.
234     ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
235         slot_.get(), SEC_OID_AES_256_CBC, password_.get(), priv_key.get(), 1,
236         nullptr));
237     ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: "
238                              << PORT_ErrorToName(PORT_GetError());
239 
240     key_info->swap(epki);
241   }
242 
243   ScopedPK11SlotInfo slot_;
244   ScopedSECItem password_;
245 };
246 
247 class Pk11KeyImportTest
248     : public Pk11KeyImportTestBase,
249       public ::testing::WithParamInterface<CK_MECHANISM_TYPE> {
250  public:
251   Pk11KeyImportTest() = default;
252   virtual ~Pk11KeyImportTest() = default;
253 };
254 
TEST_P(Pk11KeyImportTest,GenerateExportImport)255 TEST_P(Pk11KeyImportTest, GenerateExportImport) {
256   Test(Pkcs11KeyPairGenerator(GetParam()));
257 }
258 
259 INSTANTIATE_TEST_CASE_P(Pk11KeyImportTest, Pk11KeyImportTest,
260                         ::testing::Values(CKM_RSA_PKCS_KEY_PAIR_GEN,
261                                           CKM_DSA_KEY_PAIR_GEN,
262                                           CKM_DH_PKCS_KEY_PAIR_GEN));
263 
264 class Pk11KeyImportTestEC : public Pk11KeyImportTestBase,
265                             public ::testing::WithParamInterface<SECOidTag> {
266  public:
267   Pk11KeyImportTestEC() = default;
268   virtual ~Pk11KeyImportTestEC() = default;
269 };
270 
TEST_P(Pk11KeyImportTestEC,GenerateExportImport)271 TEST_P(Pk11KeyImportTestEC, GenerateExportImport) {
272   Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam()));
273 }
274 
275 INSTANTIATE_TEST_CASE_P(Pk11KeyImportTestEC, Pk11KeyImportTestEC,
276                         ::testing::Values(SEC_OID_SECG_EC_SECP256R1,
277                                           SEC_OID_SECG_EC_SECP384R1,
278                                           SEC_OID_SECG_EC_SECP521R1,
279                                           SEC_OID_CURVE25519));
280 
281 }  // namespace nss_test
282