tor-browser

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

pk11_rsapkcs1_unittest.cc (12293B)


      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 <algorithm>
      8 #include <cstdint>
      9 #include <memory>
     10 #include "cryptohi.h"
     11 #include "cpputil.h"
     12 #include "databuffer.h"
     13 #include "json_reader.h"
     14 #include "gtest/gtest.h"
     15 #include "nss.h"
     16 #include "nss_scoped_ptrs.h"
     17 #include "pk11pub.h"
     18 #include "secerr.h"
     19 #include "sechash.h"
     20 #include "pk11_signature_test.h"
     21 #include "testvectors/rsa_signature-vectors.h"
     22 
     23 namespace nss_test {
     24 
     25 CK_MECHANISM_TYPE RsaHashToComboMech(SECOidTag hash) {
     26  switch (hash) {
     27    case SEC_OID_SHA1:
     28      return CKM_SHA1_RSA_PKCS;
     29    case SEC_OID_SHA224:
     30      return CKM_SHA224_RSA_PKCS;
     31    case SEC_OID_SHA256:
     32      return CKM_SHA256_RSA_PKCS;
     33    case SEC_OID_SHA384:
     34      return CKM_SHA384_RSA_PKCS;
     35    case SEC_OID_SHA512:
     36      return CKM_SHA512_RSA_PKCS;
     37    default:
     38      break;
     39  }
     40  return CKM_INVALID_MECHANISM;
     41 }
     42 
     43 class Pkcs11RsaBaseTest : public Pk11SignatureTest {
     44 protected:
     45  Pkcs11RsaBaseTest(SECOidTag hashOid)
     46      : Pk11SignatureTest(CKM_RSA_PKCS, hashOid, RsaHashToComboMech(hashOid)) {}
     47 
     48  void Verify(const RsaSignatureTestVector& vec) {
     49    Pkcs11SignatureTestParams params = {
     50        DataBuffer(), DataBuffer(vec.public_key.data(), vec.public_key.size()),
     51        DataBuffer(vec.msg.data(), vec.msg.size()),
     52        DataBuffer(vec.sig.data(), vec.sig.size())};
     53    Pk11SignatureTest::Verify(params, (bool)vec.valid);
     54  }
     55 };
     56 
     57 class Pkcs11RsaPkcs1WycheproofTest : public ::testing::Test {
     58 protected:
     59  static void ReadTestAttr(RsaSignatureTestVector& t, const std::string& n,
     60                           JsonReader& r) {
     61    if (n == "msg") {
     62      t.msg = r.ReadHex();
     63    } else if (n == "sig") {
     64      t.sig = r.ReadHex();
     65    } else {
     66      FAIL() << "unknown test key: " << n;
     67    }
     68  }
     69 
     70  void RunGroup(JsonReader& r) {
     71    std::vector<RsaSignatureTestVector> tests;
     72    std::vector<uint8_t> public_key;
     73    SECOidTag hash_oid = SEC_OID_UNKNOWN;
     74    uint64_t keysize = 0;
     75    while (r.NextItem()) {
     76      std::string n = r.ReadLabel();
     77      if (n == "") {
     78        break;
     79      }
     80      if (n == "e" || n == "keyAsn" || n == "keyJwk" || n == "keyPem" ||
     81          n == "n") {
     82        r.SkipValue();
     83      } else if (n == "keyDer") {
     84        public_key = r.ReadHex();
     85      } else if (n == "keysize") {
     86        keysize = r.ReadInt();
     87      } else if (n == "type") {
     88        ASSERT_EQ("RsassaPkcs1Verify", r.ReadString());
     89      } else if (n == "sha") {
     90        hash_oid = r.ReadHash();
     91      } else if (n == "tests") {
     92        WycheproofReadTests(
     93            r, &tests, ReadTestAttr, false,
     94            [keysize](RsaSignatureTestVector& t, const std::string& result,
     95                      const std::vector<std::string>& flags) {
     96              if (result == "acceptable" && keysize >= 1024 &&
     97                  std::find_if(flags.begin(), flags.end(), [](std::string v) {
     98                    return v == "SmallModulus" || v == "SmallPublicKey";
     99                  }) != flags.end()) {
    100                t.valid = true;
    101              };
    102            });
    103      } else {
    104        FAIL() << "unknown group label: " << n;
    105      }
    106    }
    107 
    108    for (auto& t : tests) {
    109      Pkcs11RsaBaseTestWrap test(hash_oid);
    110      t.hash_oid = hash_oid;
    111      t.public_key = public_key;
    112      test.Run(t);
    113    }
    114  }
    115 
    116 private:
    117  class Pkcs11RsaBaseTestWrap : public Pkcs11RsaBaseTest {
    118   public:
    119    Pkcs11RsaBaseTestWrap(SECOidTag hash) : Pkcs11RsaBaseTest(hash) {}
    120    void TestBody() {}
    121 
    122    void Verify1(const RsaSignatureTestVector& vec) {
    123      SECItem spki_item = {siBuffer, toUcharPtr(vec.public_key.data()),
    124                           static_cast<unsigned int>(vec.public_key.size())};
    125 
    126      ScopedCERTSubjectPublicKeyInfo cert_spki(
    127          SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
    128      ASSERT_TRUE(cert_spki);
    129 
    130      ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
    131      ASSERT_TRUE(pub_key);
    132 
    133      DataBuffer hash;
    134      hash.Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(vec.hash_oid)));
    135      SECStatus rv = PK11_HashBuf(vec.hash_oid, toUcharPtr(hash.data()),
    136                                  toUcharPtr(vec.msg.data()), vec.msg.size());
    137      ASSERT_EQ(rv, SECSuccess);
    138 
    139      // Verify.
    140      SECItem hash_item = {siBuffer, toUcharPtr(hash.data()),
    141                           static_cast<unsigned int>(hash.len())};
    142      SECItem sig_item = {siBuffer, toUcharPtr(vec.sig.data()),
    143                          static_cast<unsigned int>(vec.sig.size())};
    144 
    145      rv = VFY_VerifyDigestDirect(&hash_item, pub_key.get(), &sig_item,
    146                                  SEC_OID_PKCS1_RSA_ENCRYPTION, vec.hash_oid,
    147                                  nullptr);
    148      EXPECT_EQ(rv, vec.valid ? SECSuccess : SECFailure);
    149    };
    150 
    151    void Run(const RsaSignatureTestVector& vec) {
    152      /* Using VFY_ interface */
    153      Verify1(vec);
    154      /* Using PKCS #11 interface */
    155      setSkipRaw(true);
    156      Verify(vec);
    157    }
    158  };
    159 };
    160 
    161 /* Test that PKCS #1 v1.5 verification requires a minimum of 8B
    162 * of padding, per-RFC3447. The padding formula is
    163 * `pad_len = em_len - t_len - 3`, where em_len is the octet length
    164 * of the RSA modulus and t_len is the length of the `DigestInfo ||
    165 * Hash(message)` sequence. For SHA512, t_len is 83. We'll tweak the
    166 * modulus size to test with a pad_len of 8 (valid) and 6 (invalid):
    167 *   em_len = `8 + 83 + 3` = `94*8` = 752b
    168 *   em_len = `6 + 83 + 3` = `92*8` = 736b
    169 * Use 6 as the invalid value since modLen % 16 must be zero.
    170 */
    171 TEST(RsaPkcs1Test, Pkcs1MinimumPadding) {
    172 #define RSA_SHORT_KEY_LENGTH 736
    173 /* if our minimum supported key length is big enough to handle
    174 * our largest Hash function, we can't test a short length */
    175 #if RSA_MIN_MODULUS_BITS < RSA_SHORT_KEY_LENGTH
    176  const size_t kRsaShortKeyBits = RSA_SHORT_KEY_LENGTH;
    177  const size_t kRsaKeyBits = 752;
    178  static const std::vector<uint8_t> kMsg{'T', 'E', 'S', 'T'};
    179  static const std::vector<uint8_t> kSha512DigestInfo{
    180      0x30, 0x51, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
    181      0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
    182  static const std::vector<uint8_t> kMsgSha512{
    183      0x7B, 0xFA, 0x95, 0xA6, 0x88, 0x92, 0x4C, 0x47, 0xC7, 0xD2, 0x23,
    184      0x81, 0xF2, 0x0C, 0xC9, 0x26, 0xF5, 0x24, 0xBE, 0xAC, 0xB1, 0x3F,
    185      0x84, 0xE2, 0x03, 0xD4, 0xBD, 0x8C, 0xB6, 0xBA, 0x2F, 0xCE, 0x81,
    186      0xC5, 0x7A, 0x5F, 0x05, 0x9B, 0xF3, 0xD5, 0x09, 0x92, 0x64, 0x87,
    187      0xBD, 0xE9, 0x25, 0xB3, 0xBC, 0xEE, 0x06, 0x35, 0xE4, 0xF7, 0xBA,
    188      0xEB, 0xA0, 0x54, 0xE5, 0xDB, 0xA6, 0x96, 0xB2, 0xBF};
    189 
    190  ScopedSECKEYPrivateKey short_priv, good_priv;
    191  ScopedSECKEYPublicKey short_pub, good_pub;
    192  PK11RSAGenParams rsa_params;
    193  rsa_params.keySizeInBits = kRsaShortKeyBits;
    194  rsa_params.pe = 65537;
    195 
    196  ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
    197  ASSERT_TRUE(slot);
    198  SECKEYPublicKey* p_pub_tmp = nullptr;
    199  short_priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
    200                                        &rsa_params, &p_pub_tmp, false, false,
    201                                        nullptr));
    202  short_pub.reset(p_pub_tmp);
    203 
    204  rsa_params.keySizeInBits = kRsaKeyBits;
    205  good_priv.reset(PK11_GenerateKeyPair(slot.get(), CKM_RSA_PKCS_KEY_PAIR_GEN,
    206                                       &rsa_params, &p_pub_tmp, false, false,
    207                                       nullptr));
    208  good_pub.reset(p_pub_tmp);
    209 
    210  size_t em_len = kRsaShortKeyBits / 8;
    211  size_t t_len = kSha512DigestInfo.size() + kMsgSha512.size();
    212  size_t pad_len = em_len - t_len - 3;
    213  ASSERT_EQ(6U, pad_len);
    214 
    215  std::vector<uint8_t> invalid_pkcs;
    216  invalid_pkcs.push_back(0x00);
    217  invalid_pkcs.push_back(0x01);
    218  invalid_pkcs.insert(invalid_pkcs.end(), pad_len, 0xff);
    219  invalid_pkcs.insert(invalid_pkcs.end(), 1, 0x00);
    220  invalid_pkcs.insert(invalid_pkcs.end(), kSha512DigestInfo.begin(),
    221                      kSha512DigestInfo.end());
    222  invalid_pkcs.insert(invalid_pkcs.end(), kMsgSha512.begin(), kMsgSha512.end());
    223  ASSERT_EQ(em_len, invalid_pkcs.size());
    224 
    225  // Sign it indirectly. Signing functions check for a proper pad_len.
    226  std::vector<uint8_t> sig(em_len);
    227  uint32_t sig_len;
    228  SECStatus rv =
    229      PK11_PubDecryptRaw(short_priv.get(), sig.data(), &sig_len, sig.size(),
    230                         invalid_pkcs.data(), invalid_pkcs.size());
    231  EXPECT_EQ(SECSuccess, rv);
    232 
    233  // Verify it.
    234  DataBuffer hash;
    235  hash.Allocate(static_cast<size_t>(HASH_ResultLenByOidTag(SEC_OID_SHA512)));
    236  rv = PK11_HashBuf(SEC_OID_SHA512, toUcharPtr(hash.data()),
    237                    toUcharPtr(kMsg.data()), kMsg.size());
    238  ASSERT_EQ(rv, SECSuccess);
    239  SECItem hash_item = {siBuffer, toUcharPtr(hash.data()),
    240                       static_cast<unsigned int>(hash.len())};
    241  SECItem sig_item = {siBuffer, toUcharPtr(sig.data()), sig_len};
    242  /* don't let policy foil us */
    243  NSS_OptionSet(NSS_KEY_SIZE_POLICY_CLEAR_FLAGS,
    244                NSS_KEY_SIZE_POLICY_VERIFY_FLAG);
    245  rv = VFY_VerifyDigestDirect(&hash_item, short_pub.get(), &sig_item,
    246                              SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA512,
    247                              nullptr);
    248  EXPECT_EQ(SECFailure, rv);
    249  EXPECT_EQ(SEC_ERROR_BAD_SIGNATURE, PORT_GetError());
    250 
    251  // Repeat the test with the sufficiently-long key.
    252  em_len = kRsaKeyBits / 8;
    253  t_len = kSha512DigestInfo.size() + kMsgSha512.size();
    254  pad_len = em_len - t_len - 3;
    255  ASSERT_EQ(8U, pad_len);
    256 
    257  std::vector<uint8_t> valid_pkcs;
    258  valid_pkcs.push_back(0x00);
    259  valid_pkcs.push_back(0x01);
    260  valid_pkcs.insert(valid_pkcs.end(), pad_len, 0xff);
    261  valid_pkcs.insert(valid_pkcs.end(), 1, 0x00);
    262  valid_pkcs.insert(valid_pkcs.end(), kSha512DigestInfo.begin(),
    263                    kSha512DigestInfo.end());
    264  valid_pkcs.insert(valid_pkcs.end(), kMsgSha512.begin(), kMsgSha512.end());
    265  ASSERT_EQ(em_len, valid_pkcs.size());
    266 
    267  // Sign it the same way as above (even though we could use sign APIs now).
    268  sig.resize(em_len);
    269  rv = PK11_PubDecryptRaw(good_priv.get(), sig.data(), &sig_len, sig.size(),
    270                          valid_pkcs.data(), valid_pkcs.size());
    271  EXPECT_EQ(SECSuccess, rv);
    272 
    273  // Verify it.
    274  sig_item = {siBuffer, toUcharPtr(sig.data()), sig_len};
    275  rv = VFY_VerifyDigestDirect(&hash_item, good_pub.get(), &sig_item,
    276                              SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA512,
    277                              nullptr);
    278  EXPECT_EQ(SECSuccess, rv);
    279 #else
    280  GTEST_SKIP();
    281 #endif
    282 }
    283 
    284 TEST(RsaPkcs1Test, RequireNullParameter) {
    285  // The test vectors may be verified with:
    286  //
    287  // openssl rsautl -keyform der -pubin -inkey spki.bin -in sig.bin | der2ascii
    288  // openssl rsautl -keyform der -pubin -inkey spki.bin -in sig2.bin | der2ascii
    289 
    290  // Import public key.
    291  SECItem spki_item = {siBuffer, toUcharPtr(kSpki), sizeof(kSpki)};
    292  ScopedCERTSubjectPublicKeyInfo cert_spki(
    293      SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
    294  ASSERT_TRUE(cert_spki);
    295  ScopedSECKEYPublicKey pub_key(SECKEY_ExtractPublicKey(cert_spki.get()));
    296  ASSERT_TRUE(pub_key);
    297 
    298  SECItem hash = {siBuffer, toUcharPtr(kHash), sizeof(kHash)};
    299 
    300  // kSignature is a valid signature.
    301  SECItem sig_item = {siBuffer, toUcharPtr(kSignature), sizeof(kSignature)};
    302  SECStatus rv = VFY_VerifyDigestDirect(&hash, pub_key.get(), &sig_item,
    303                                        SEC_OID_PKCS1_RSA_ENCRYPTION,
    304                                        SEC_OID_SHA256, nullptr);
    305  EXPECT_EQ(SECSuccess, rv);
    306 
    307  // kSignatureInvalid is not.
    308  sig_item = {siBuffer, toUcharPtr(kSignatureInvalid),
    309              sizeof(kSignatureInvalid)};
    310  rv = VFY_VerifyDigestDirect(&hash, pub_key.get(), &sig_item,
    311                              SEC_OID_PKCS1_RSA_ENCRYPTION, SEC_OID_SHA256,
    312                              nullptr);
    313 #ifdef NSS_PKCS1_AllowMissingParameters
    314  EXPECT_EQ(SECSuccess, rv);
    315 #else
    316  EXPECT_EQ(SECFailure, rv);
    317 #endif
    318 }
    319 
    320 TEST_F(Pkcs11RsaPkcs1WycheproofTest, Pkcs11RsaPkcs1WycheproofTest) {
    321  WycheproofHeader("rsa_signature", "RSASSA-PKCS1-v1_5",
    322                   "rsassa_pkcs1_verify_schema.json",
    323                   [this](JsonReader& r) { RunGroup(r); });
    324 }
    325 
    326 }  // namespace nss_test