tor-browser

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

pk11_rsaoaep_unittest.cc (9473B)


      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 <stdint.h>
      8 
      9 #include "cpputil.h"
     10 #include "cryptohi.h"
     11 #include "json_reader.h"
     12 #include "gtest/gtest.h"
     13 #include "limits.h"
     14 #include "nss.h"
     15 #include "nss_scoped_ptrs.h"
     16 #include "pk11pub.h"
     17 #include "testvectors_base/test-structs.h"
     18 
     19 namespace nss_test {
     20 
     21 struct RsaOaepTestVector {
     22  uint32_t id;
     23  std::vector<uint8_t> msg;
     24  std::vector<uint8_t> ct;
     25  std::vector<uint8_t> label;
     26  bool valid;
     27 };
     28 
     29 class RsaOaepWycheproofTest : public ::testing::Test {
     30 protected:
     31  void Run(const std::string& file) {
     32    WycheproofHeader(file, "RSAES-OAEP", "rsaes_oaep_decrypt_schema.json",
     33                     [this](JsonReader& r) { RunGroup(r); });
     34  }
     35 
     36  void TestDecrypt(ScopedSECKEYPrivateKey& priv_key, SECOidTag hash_oid,
     37                   CK_RSA_PKCS_MGF_TYPE mgf_hash,
     38                   const RsaOaepTestVector& vec) {
     39    // Set up the OAEP parameters.
     40    CK_RSA_PKCS_OAEP_PARAMS oaepParams;
     41    oaepParams.source = CKZ_DATA_SPECIFIED;
     42    oaepParams.pSourceData = const_cast<unsigned char*>(vec.label.data());
     43    oaepParams.ulSourceDataLen = vec.label.size();
     44    oaepParams.mgf = mgf_hash;
     45    oaepParams.hashAlg = HashOidToHashMech(hash_oid);
     46    SECItem params_item = {siBuffer,
     47                           toUcharPtr(reinterpret_cast<uint8_t*>(&oaepParams)),
     48                           static_cast<unsigned int>(sizeof(oaepParams))};
     49    // Decrypt.
     50    std::vector<uint8_t> decrypted(PR_MAX(1, vec.ct.size()));
     51    unsigned int decrypted_len = 0;
     52    SECStatus rv = PK11_PrivDecrypt(
     53        priv_key.get(), CKM_RSA_PKCS_OAEP, &params_item, decrypted.data(),
     54        &decrypted_len, decrypted.size(), vec.ct.data(), vec.ct.size());
     55 
     56    if (vec.valid) {
     57      EXPECT_EQ(SECSuccess, rv);
     58      decrypted.resize(decrypted_len);
     59      EXPECT_EQ(vec.msg, decrypted);
     60    } else {
     61      EXPECT_EQ(SECFailure, rv);
     62    }
     63  };
     64 
     65 private:
     66  void RunGroup(JsonReader& r) {
     67    std::vector<RsaOaepTestVector> tests;
     68    ScopedSECKEYPrivateKey private_key;
     69    CK_MECHANISM_TYPE mgf_hash = CKM_INVALID_MECHANISM;
     70    SECOidTag hash_oid = SEC_OID_UNKNOWN;
     71 
     72    while (r.NextItem()) {
     73      std::string n = r.ReadLabel();
     74      if (n == "") {
     75        break;
     76      }
     77 
     78      if (n == "d" || n == "e" || n == "keysize" || n == "n" ||
     79          n == "privateKeyJwk" || n == "privateKeyPem") {
     80        r.SkipValue();
     81      } else if (n == "privateKeyPkcs8") {
     82        std::vector<uint8_t> priv_key = r.ReadHex();
     83        private_key = LoadPrivateKey(priv_key);
     84      } else if (n == "mgf") {
     85        ASSERT_EQ("MGF1", r.ReadString());
     86      } else if (n == "mgfSha") {
     87        mgf_hash = HashOidToHashMech(r.ReadHash());
     88      } else if (n == "sha") {
     89        hash_oid = r.ReadHash();
     90      } else if (n == "type") {
     91        ASSERT_EQ("RsaesOaepDecrypt", r.ReadString());
     92      } else if (n == "tests") {
     93        WycheproofReadTests(r, &tests, ReadTestAttr);
     94      } else {
     95        FAIL() << "unknown label in group: " << n;
     96      }
     97    }
     98 
     99    for (auto& t : tests) {
    100      TestDecrypt(private_key, hash_oid, mgf_hash, t);
    101    }
    102  }
    103 
    104  ScopedSECKEYPrivateKey LoadPrivateKey(const std::vector<uint8_t>& priv_key) {
    105    SECItem pkcs8_item = {siBuffer, toUcharPtr(priv_key.data()),
    106                          static_cast<unsigned int>(priv_key.size())};
    107 
    108    ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
    109    EXPECT_NE(nullptr, slot);
    110 
    111    SECKEYPrivateKey* key = nullptr;
    112    SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
    113        slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key,
    114        nullptr);
    115    EXPECT_EQ(SECSuccess, rv);
    116    EXPECT_NE(nullptr, key);
    117 
    118    return ScopedSECKEYPrivateKey(key);
    119  }
    120 
    121  static void ReadTestAttr(RsaOaepTestVector& t, const std::string& n,
    122                           JsonReader& r) {
    123    if (n == "msg") {
    124      t.msg = r.ReadHex();
    125    } else if (n == "ct") {
    126      t.ct = r.ReadHex();
    127    } else if (n == "label") {
    128      t.label = r.ReadHex();
    129    } else {
    130      FAIL() << "unsupported test case field: " << n;
    131    }
    132  }
    133 
    134  inline CK_MECHANISM_TYPE HashOidToHashMech(SECOidTag hash_oid) {
    135    switch (hash_oid) {
    136      case SEC_OID_SHA1:
    137        return CKM_SHA_1;
    138      case SEC_OID_SHA224:
    139        return CKM_SHA224;
    140      case SEC_OID_SHA256:
    141        return CKM_SHA256;
    142      case SEC_OID_SHA384:
    143        return CKM_SHA384;
    144      case SEC_OID_SHA512:
    145        return CKM_SHA512;
    146      default:
    147        ADD_FAILURE();
    148    }
    149    return CKM_INVALID_MECHANISM;
    150  }
    151 };
    152 
    153 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha1) {
    154  Run("rsa_oaep_2048_sha1_mgf1sha1");
    155 }
    156 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha256MgfSha1) {
    157  Run("rsa_oaep_2048_sha256_mgf1sha1");
    158 }
    159 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha256) {
    160  Run("rsa_oaep_2048_sha256_mgf1sha256");
    161 }
    162 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha384MgfSha1) {
    163  Run("rsa_oaep_2048_sha384_mgf1sha1");
    164 }
    165 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha384) {
    166  Run("rsa_oaep_2048_sha384_mgf1sha384");
    167 }
    168 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha512MgfSha1) {
    169  Run("rsa_oaep_2048_sha512_mgf1sha1");
    170 }
    171 TEST_F(RsaOaepWycheproofTest, RsaOaep2048Sha512) {
    172  Run("rsa_oaep_2048_sha512_mgf1sha512");
    173 }
    174 
    175 TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha256MgfSha1) {
    176  Run("rsa_oaep_3072_sha256_mgf1sha1");
    177 }
    178 TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha256) {
    179  Run("rsa_oaep_3072_sha256_mgf1sha256");
    180 }
    181 TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha512MgfSha1) {
    182  Run("rsa_oaep_3072_sha512_mgf1sha1");
    183 }
    184 TEST_F(RsaOaepWycheproofTest, RsaOaep3072Sha512) {
    185  Run("rsa_oaep_3072_sha512_mgf1sha512");
    186 }
    187 
    188 TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha256MgfSha1) {
    189  Run("rsa_oaep_4096_sha256_mgf1sha1");
    190 }
    191 TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha256) {
    192  Run("rsa_oaep_4096_sha256_mgf1sha256");
    193 }
    194 TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha512MgfSha1) {
    195  Run("rsa_oaep_4096_sha512_mgf1sha1");
    196 }
    197 TEST_F(RsaOaepWycheproofTest, RsaOaep4096Sha512) {
    198  Run("rsa_oaep_4096_sha512_mgf1sha512");
    199 }
    200 
    201 TEST_F(RsaOaepWycheproofTest, RsaOaepMisc) { Run("rsa_oaep_misc"); }
    202 
    203 TEST(Pkcs11RsaOaepTest, TestOaepWrapUnwrap) {
    204  const size_t kRsaKeyBits = 2048;
    205  const size_t kwrappedBufLen = 4096;
    206 
    207  SECStatus rv = SECFailure;
    208 
    209  ScopedSECKEYPrivateKey priv;
    210  ScopedSECKEYPublicKey pub;
    211  PK11RSAGenParams rsa_params;
    212  rsa_params.keySizeInBits = kRsaKeyBits;
    213  rsa_params.pe = 65537;
    214 
    215  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
    216  ASSERT_NE(slot, nullptr);
    217 
    218  SECKEYPublicKey* p_pub_tmp = nullptr;
    219  priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
    220                                  &rsa_params, &p_pub_tmp, false, false,
    221                                  nullptr));
    222  pub.reset(p_pub_tmp);
    223 
    224  ASSERT_NE(priv.get(), nullptr);
    225  ASSERT_NE(pub.get(), nullptr);
    226 
    227  ScopedPK11SymKey to_wrap(
    228      PK11_KeyGen(slot.get(), CKM_AES_CBC, nullptr, 16, nullptr));
    229 
    230  CK_RSA_PKCS_OAEP_PARAMS oaep_params = {CKM_SHA256, CKG_MGF1_SHA256,
    231                                         CKZ_DATA_SPECIFIED, NULL, 0};
    232 
    233  SECItem param = {siBuffer, (unsigned char*)&oaep_params, sizeof(oaep_params)};
    234 
    235  ScopedSECItem wrapped(SECITEM_AllocItem(nullptr, nullptr, kwrappedBufLen));
    236  rv = PK11_PubWrapSymKeyWithMechanism(pub.get(), CKM_RSA_PKCS_OAEP, &param,
    237                                       to_wrap.get(), wrapped.get());
    238  ASSERT_EQ(rv, SECSuccess);
    239 
    240  PK11SymKey* p_unwrapped_tmp = nullptr;
    241 
    242  // Extract key's value in order to validate decryption worked.
    243  rv = PK11_ExtractKeyValue(to_wrap.get());
    244  ASSERT_EQ(rv, SECSuccess);
    245 
    246  // References owned by PKCS#11 layer; no need to scope and free.
    247  SECItem* expectedItem = PK11_GetKeyData(to_wrap.get());
    248 
    249  // This assumes CKM_RSA_PKCS and doesn't understand OAEP.
    250  // CKM_RSA_PKCS cannot safely return errors, however, as it can lead
    251  // to Bleichenbacher-like attacks. To solve this there's a new definition
    252  // that generates fake key material based on the message and private key.
    253  // This returned key material will not be the key we were expecting, so
    254  // make sure that's the case:
    255  p_unwrapped_tmp = PK11_PubUnwrapSymKey(priv.get(), wrapped.get(), CKM_AES_CBC,
    256                                         CKA_DECRYPT, 16);
    257  // As long as the wrapped data is the same length as the key
    258  // (which it should be), then CKM_RSA_PKCS should not fail.
    259  ASSERT_NE(p_unwrapped_tmp, nullptr);
    260  ScopedPK11SymKey fakeUnwrapped;
    261  fakeUnwrapped.reset(p_unwrapped_tmp);
    262  rv = PK11_ExtractKeyValue(fakeUnwrapped.get());
    263  ASSERT_EQ(rv, SECSuccess);
    264 
    265  // References owned by PKCS#11 layer; no need to scope and free.
    266  SECItem* fakeItem = PK11_GetKeyData(fakeUnwrapped.get());
    267  ASSERT_NE(SECITEM_CompareItem(fakeItem, expectedItem), 0);
    268 
    269  ScopedPK11SymKey unwrapped;
    270  p_unwrapped_tmp = PK11_PubUnwrapSymKeyWithMechanism(
    271      priv.get(), CKM_RSA_PKCS_OAEP, &param, wrapped.get(), CKM_AES_CBC,
    272      CKA_DECRYPT, 16);
    273  ASSERT_NE(p_unwrapped_tmp, nullptr);
    274 
    275  unwrapped.reset(p_unwrapped_tmp);
    276 
    277  rv = PK11_ExtractKeyValue(unwrapped.get());
    278  ASSERT_EQ(rv, SECSuccess);
    279 
    280  // References owned by PKCS#11 layer; no need to scope and free.
    281  SECItem* actualItem = PK11_GetKeyData(unwrapped.get());
    282 
    283  ASSERT_EQ(SECITEM_CompareItem(actualItem, expectedItem), 0);
    284 }
    285 }  // namespace nss_test