tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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