pk11_import_unittest.cc (14858B)
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_import_vectors.h" 19 #include "pk11_keygen.h" 20 21 namespace nss_test { 22 23 // This deleter deletes a set of objects, unlike the deleter on 24 // ScopedPK11GenericObject, which only deletes one. 25 struct PK11GenericObjectsDeleter { 26 void operator()(PK11GenericObject* objs) { 27 if (objs) { 28 PK11_DestroyGenericObjects(objs); 29 } 30 } 31 }; 32 33 class Pk11KeyImportTestBase : public ::testing::Test { 34 public: 35 Pk11KeyImportTestBase() = default; 36 virtual ~Pk11KeyImportTestBase() = default; 37 38 void SetUp() override { 39 slot_.reset(PK11_GetInternalKeySlot()); 40 ASSERT_TRUE(slot_); 41 42 static const uint8_t pw[] = "pw"; 43 SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)}; 44 password_.reset(SECITEM_DupItem(&pwItem)); 45 } 46 47 void Test(const Pkcs11KeyPairGenerator& generator) { 48 // Generate a key and export it. 49 KeyType key_type = nullKey; 50 ScopedSECKEYEncryptedPrivateKeyInfo key_info; 51 ScopedSECItem public_value; 52 GenerateAndExport(generator, &key_type, &key_info, &public_value); 53 54 // Note: NSS is currently unable export wrapped DH keys, so this doesn't 55 // test those beyond generate and verify. 56 if (key_type == dhKey) { 57 return; 58 } 59 #ifdef NSS_DISABLE_DSA 60 // if DSA is disabled, we can't generate a key, Generated will 61 // have expected a failure, and checked the error codeso we are now. 62 if (generator.mechanism() == CKM_DSA_KEY_PAIR_GEN) { 63 return; 64 } 65 #endif 66 ASSERT_NE(nullptr, public_value); 67 ASSERT_NE(nullptr, key_info); 68 69 // Now import the encrypted key. 70 static const uint8_t nick[] = "nick"; 71 SECItem nickname = {siBuffer, toUcharPtr(nick), sizeof(nick)}; 72 SECKEYPrivateKey* priv_tmp; 73 SECStatus rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey( 74 slot_.get(), key_info.get(), password_.get(), &nickname, 75 public_value.get(), PR_TRUE, PR_TRUE, key_type, 0, &priv_tmp, NULL); 76 ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed " 77 << PORT_ErrorToName(PORT_GetError()); 78 ScopedSECKEYPrivateKey priv_key(priv_tmp); 79 ASSERT_NE(nullptr, priv_key); 80 81 CheckForPublicKey(priv_key, public_value.get()); 82 } 83 84 private: 85 SECItem GetPublicComponent(ScopedSECKEYPublicKey& pub_key) { 86 SECItem null = {siBuffer, NULL, 0}; 87 switch (SECKEY_GetPublicKeyType(pub_key.get())) { 88 case rsaKey: 89 case rsaPssKey: 90 case rsaOaepKey: 91 return pub_key->u.rsa.modulus; 92 case keaKey: 93 return pub_key->u.kea.publicValue; 94 case dsaKey: 95 return pub_key->u.dsa.publicValue; 96 case dhKey: 97 return pub_key->u.dh.publicValue; 98 case ecKey: 99 case edKey: 100 case ecMontKey: 101 return pub_key->u.ec.publicValue; 102 case kyberKey: 103 return pub_key->u.kyber.publicValue; 104 case mldsaKey: /* add import tests when pkcs #8 support is added */ 105 case fortezzaKey: /* depricated */ 106 case nullKey: 107 /* didn't use default here so we can catch new key types at compile time 108 */ 109 break; 110 } 111 return null; 112 } 113 void CheckForPublicKey(const ScopedSECKEYPrivateKey& priv_key, 114 const SECItem* expected_public) { 115 // Verify the public key exists. 116 StackSECItem priv_id; 117 KeyType type = SECKEY_GetPrivateKeyType(priv_key.get()); 118 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, priv_key.get(), 119 CKA_ID, &priv_id); 120 ASSERT_EQ(SECSuccess, rv) << "Couldn't read CKA_ID from private key: " 121 << PORT_ErrorToName(PORT_GetError()); 122 123 CK_ATTRIBUTE_TYPE value_type = CKA_VALUE; 124 switch (type) { 125 case rsaKey: 126 value_type = CKA_MODULUS; 127 break; 128 129 case dhKey: 130 case dsaKey: 131 value_type = CKA_VALUE; 132 break; 133 134 case ecKey: 135 value_type = CKA_EC_POINT; 136 break; 137 138 default: 139 FAIL() << "unknown key type"; 140 } 141 142 // Scan public key objects until we find one with the same CKA_ID as 143 // priv_key 144 std::unique_ptr<PK11GenericObject, PK11GenericObjectsDeleter> objs( 145 PK11_FindGenericObjects(slot_.get(), CKO_PUBLIC_KEY)); 146 ASSERT_NE(nullptr, objs); 147 for (PK11GenericObject* obj = objs.get(); obj != nullptr; 148 obj = PK11_GetNextGenericObject(obj)) { 149 StackSECItem pub_id; 150 rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_ID, &pub_id); 151 if (rv != SECSuccess) { 152 // Can't read CKA_ID from object. 153 continue; 154 } 155 if (!SECITEM_ItemsAreEqual(&priv_id, &pub_id)) { 156 // This isn't the object we're looking for. 157 continue; 158 } 159 160 StackSECItem token; 161 rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, CKA_TOKEN, &token); 162 ASSERT_EQ(SECSuccess, rv); 163 ASSERT_EQ(1U, token.len); 164 ASSERT_NE(0, token.data[0]); 165 166 StackSECItem raw_value; 167 SECItem decoded_value; 168 rv = PK11_ReadRawAttribute(PK11_TypeGeneric, obj, value_type, &raw_value); 169 ASSERT_EQ(SECSuccess, rv); 170 SECItem value = raw_value; 171 172 // Decode the EC_POINT and check the output against expected. 173 // CKA_EC_POINT isn't stable, see Bug 1520649. 174 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); 175 ASSERT_TRUE(arena); 176 if (value_type == CKA_EC_POINT) { 177 // If this fails due to the noted inconsistency, we may need to 178 // check the whole raw_value, or remove a leading UNCOMPRESSED_POINT tag 179 rv = SEC_QuickDERDecodeItem(arena.get(), &decoded_value, 180 SEC_ASN1_GET(SEC_OctetStringTemplate), 181 &raw_value); 182 ASSERT_EQ(SECSuccess, rv); 183 value = decoded_value; 184 } 185 ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &value)) 186 << "expected: " 187 << DataBuffer(expected_public->data, expected_public->len) 188 << std::endl 189 << "actual: " << DataBuffer(value.data, value.len) << std::endl; 190 191 // Finally, convert the private to public and ensure it matches. 192 ScopedSECKEYPublicKey pub_key(SECKEY_ConvertToPublicKey(priv_key.get())); 193 ASSERT_TRUE(pub_key); 194 SECItem converted_public = GetPublicComponent(pub_key); 195 ASSERT_TRUE(converted_public.len != 0); 196 197 ASSERT_TRUE(SECITEM_ItemsAreEqual(expected_public, &converted_public)) 198 << "expected: " 199 << DataBuffer(expected_public->data, expected_public->len) 200 << std::endl 201 << "actual: " 202 << DataBuffer(converted_public.data, converted_public.len) 203 << std::endl; 204 } 205 } 206 207 void GenerateAndExport(const Pkcs11KeyPairGenerator& generator, 208 KeyType* key_type, 209 ScopedSECKEYEncryptedPrivateKeyInfo* key_info, 210 ScopedSECItem* public_value) { 211 ScopedSECKEYPrivateKey priv_key; 212 ScopedSECKEYPublicKey pub_key; 213 generator.GenerateKey(&priv_key, &pub_key); 214 #ifdef NSS_DISABLE_DSA 215 if (generator.mechanism() == CKM_DSA_KEY_PAIR_GEN) { 216 ASSERT_FALSE(priv_key); 217 return; 218 } 219 #endif 220 ASSERT_TRUE(priv_key); 221 222 // Save the public value, which we will need on import 223 SECItem* pub_val; 224 KeyType t = SECKEY_GetPublicKeyType(pub_key.get()); 225 switch (t) { 226 case rsaKey: 227 pub_val = &pub_key->u.rsa.modulus; 228 break; 229 case dhKey: 230 pub_val = &pub_key->u.dh.publicValue; 231 break; 232 case dsaKey: 233 pub_val = &pub_key->u.dsa.publicValue; 234 break; 235 case ecKey: 236 pub_val = &pub_key->u.ec.publicValue; 237 break; 238 default: 239 FAIL() << "Unknown key type"; 240 } 241 242 CheckForPublicKey(priv_key, pub_val); 243 244 *key_type = t; 245 // Note: NSS is currently unable export wrapped DH keys, so this doesn't 246 // test those beyond generate and verify. 247 if (t == dhKey) { 248 return; 249 } 250 public_value->reset(SECITEM_DupItem(pub_val)); 251 252 // Wrap and export the key. 253 ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo( 254 slot_.get(), SEC_OID_AES_256_CBC, password_.get(), priv_key.get(), 1, 255 nullptr)); 256 ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: " 257 << PORT_ErrorToName(PORT_GetError()); 258 259 key_info->swap(epki); 260 } 261 262 ScopedPK11SlotInfo slot_; 263 ScopedSECItem password_; 264 }; 265 266 class Pk11KeyImportTest 267 : public Pk11KeyImportTestBase, 268 public ::testing::WithParamInterface<CK_MECHANISM_TYPE> { 269 public: 270 Pk11KeyImportTest() = default; 271 virtual ~Pk11KeyImportTest() = default; 272 }; 273 274 TEST_P(Pk11KeyImportTest, GenerateExportImport) { 275 Test(Pkcs11KeyPairGenerator(GetParam())); 276 } 277 278 // we go ahead and test for DSA, this only trigger key gen, which will 279 // check that we fail with a proper error code 280 INSTANTIATE_TEST_SUITE_P(Pk11KeyImportTest, Pk11KeyImportTest, 281 ::testing::Values(CKM_RSA_PKCS_KEY_PAIR_GEN, 282 CKM_DSA_KEY_PAIR_GEN, 283 CKM_DH_PKCS_KEY_PAIR_GEN)); 284 285 class Pk11KeyImportTestEC : public Pk11KeyImportTestBase, 286 public ::testing::WithParamInterface<SECOidTag> { 287 public: 288 Pk11KeyImportTestEC() = default; 289 virtual ~Pk11KeyImportTestEC() = default; 290 }; 291 292 TEST_P(Pk11KeyImportTestEC, GenerateExportImport) { 293 Test(Pkcs11KeyPairGenerator(CKM_EC_KEY_PAIR_GEN, GetParam())); 294 } 295 296 INSTANTIATE_TEST_SUITE_P(Pk11KeyImportTestEC, Pk11KeyImportTestEC, 297 ::testing::Values(SEC_OID_SECG_EC_SECP256R1, 298 SEC_OID_SECG_EC_SECP384R1, 299 SEC_OID_SECG_EC_SECP521R1, 300 SEC_OID_CURVE25519)); 301 302 struct Pkcs11CompressedECKeyTestParams { 303 DataBuffer compressedKey; 304 DataBuffer uncompressedKey; 305 }; 306 307 class Pk11KeyImportTestECCompressed 308 : public Pk11KeyImportTestBase, 309 public ::testing::WithParamInterface<Pkcs11CompressedECKeyTestParams> { 310 public: 311 Pk11KeyImportTestECCompressed() = default; 312 virtual ~Pk11KeyImportTestECCompressed() = default; 313 }; 314 315 // Importing a private key in PKCS#8 format with a point not on the curve will 316 // succeed. Using the contained public key however will fail when trying to 317 // import it before using it for any operation. 318 TEST_P(Pk11KeyImportTestECCompressed, CompressedPointTest) { 319 DataBuffer spki(GetParam().compressedKey); 320 SECItem spki_item = {siBuffer, toUcharPtr(spki.data()), 321 static_cast<unsigned int>(spki.len())}; 322 323 ScopedCERTSubjectPublicKeyInfo cert_spki( 324 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); 325 ASSERT_TRUE(cert_spki); 326 ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); 327 ASSERT_TRUE(pub_key); 328 329 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 330 ASSERT_TRUE(slot); 331 332 CK_OBJECT_HANDLE id = 333 PK11_ImportPublicKey(slot.get(), pub_key.get(), PR_FALSE); 334 ASSERT_NE(id, (unsigned int)CK_INVALID_HANDLE); 335 336 StackSECItem publicDecoded; 337 SECStatus rv = PK11_ReadRawAttribute(PK11_TypePubKey, pub_key.get(), 338 CKA_EC_POINT, &publicDecoded); 339 ASSERT_EQ(rv, SECSuccess); 340 341 ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); 342 ASSERT_TRUE(arena); 343 344 SECItem decodedItem; 345 rv = SEC_QuickDERDecodeItem(arena.get(), &decodedItem, 346 SEC_ASN1_GET(SEC_OctetStringTemplate), 347 &publicDecoded); 348 349 ASSERT_EQ(rv, SECSuccess); 350 ASSERT_EQ(decodedItem.len, GetParam().uncompressedKey.len()); 351 ASSERT_EQ(0, PORT_Memcmp(decodedItem.data, GetParam().uncompressedKey.data(), 352 decodedItem.len)); 353 }; 354 355 static const Pkcs11CompressedECKeyTestParams kCompressedVectors[] = { 356 {DataBuffer(kP256CompressedSpki, sizeof(kP256CompressedSpki)), 357 DataBuffer(kP256Uncompressed, sizeof(kP256Uncompressed))}, 358 {DataBuffer(kP384CompressedSpki, sizeof(kP384CompressedSpki)), 359 DataBuffer(kP384Uncompressed, sizeof(kP384Uncompressed))}, 360 {DataBuffer(kP521CompressedSpki, sizeof(kP521CompressedSpki)), 361 DataBuffer(kP521Uncompressed, sizeof(kP521Uncompressed))}}; 362 363 INSTANTIATE_TEST_SUITE_P(Pk11KeyImportTestECCompressed, 364 Pk11KeyImportTestECCompressed, 365 ::testing::ValuesIn(kCompressedVectors)); 366 367 // Importing a key with 0x7 sign value instead of 0x02/0x03 368 TEST_F(Pk11KeyImportTestEC, CompressedPointWrongSign) { 369 DataBuffer spki(kP256CompressedSpkiWrongSign, 370 sizeof(kP256CompressedSpkiWrongSign)); 371 SECItem spki_item = {siBuffer, toUcharPtr(spki.data()), 372 static_cast<unsigned int>(spki.len())}; 373 374 ScopedCERTSubjectPublicKeyInfo cert_spki( 375 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); 376 ASSERT_TRUE(cert_spki); 377 ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); 378 ASSERT_TRUE(pub_key); 379 380 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 381 ASSERT_TRUE(slot); 382 383 CK_OBJECT_HANDLE id = 384 PK11_ImportPublicKey(slot.get(), pub_key.get(), PR_FALSE); 385 ASSERT_EQ(id, (unsigned int)CK_INVALID_HANDLE); 386 } 387 388 TEST_F(Pk11KeyImportTestEC, CompressedPointWrongLen) { 389 DataBuffer spki(kP256CompressedSpkiWrongPointLength, 390 sizeof(kP256CompressedSpkiWrongPointLength)); 391 SECItem spki_item = {siBuffer, toUcharPtr(spki.data()), 392 static_cast<unsigned int>(spki.len())}; 393 394 ScopedCERTSubjectPublicKeyInfo cert_spki( 395 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); 396 ASSERT_FALSE(cert_spki); 397 } 398 399 TEST_F(Pk11KeyImportTestEC, CompressedPointNotOnCurve) { 400 DataBuffer spki(kP256CompressedSpkiNotOnCurve, 401 sizeof(kP256CompressedSpkiNotOnCurve)); 402 SECItem spki_item = {siBuffer, toUcharPtr(spki.data()), 403 static_cast<unsigned int>(spki.len())}; 404 405 ScopedCERTSubjectPublicKeyInfo cert_spki( 406 SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item)); 407 ASSERT_TRUE(cert_spki); 408 ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get())); 409 ASSERT_TRUE(pub_key); 410 411 ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); 412 ASSERT_TRUE(slot); 413 414 CK_OBJECT_HANDLE id = 415 PK11_ImportPublicKey(slot.get(), pub_key.get(), PR_FALSE); 416 ASSERT_NE(id, (unsigned int)CK_INVALID_HANDLE); 417 } 418 419 } // namespace nss_test