tor-browser

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

pk11_curve25519_unittest.cc (9317B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
      3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #include <algorithm>
      6 #include <memory>
      7 #include "nss.h"
      8 #include "pk11pub.h"
      9 #include "prerror.h"
     10 #include "cpputil.h"
     11 #include "nss_scoped_ptrs.h"
     12 #include "json_reader.h"
     13 
     14 #include "testvectors/curve25519-vectors.h"
     15 #include "gtest/gtest.h"
     16 
     17 namespace nss_test {
     18 
     19 class Pkcs11Curve25519TestBase {
     20 protected:
     21  void Derive(const uint8_t* pkcs8, size_t pkcs8_len, const uint8_t* spki,
     22              size_t spki_len, const uint8_t* secret, size_t secret_len,
     23              bool expect_success) {
     24    ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
     25    ASSERT_TRUE(slot);
     26 
     27    SECItem pkcs8_item = {siBuffer, toUcharPtr(pkcs8),
     28                          static_cast<unsigned int>(pkcs8_len)};
     29 
     30    SECKEYPrivateKey* key = nullptr;
     31    SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
     32        slot.get(), &pkcs8_item, nullptr, nullptr, false, false, KU_ALL, &key,
     33        nullptr);
     34    EXPECT_EQ(SECSuccess, rv);
     35 
     36    ScopedSECKEYPrivateKey priv_key_sess(key);
     37    ASSERT_TRUE(priv_key_sess);
     38 
     39    SECItem spki_item = {siBuffer, toUcharPtr(spki),
     40                         static_cast<unsigned int>(spki_len)};
     41 
     42    ScopedCERTSubjectPublicKeyInfo cert_spki(
     43        SECKEY_DecodeDERSubjectPublicKeyInfo(&spki_item));
     44    if (!expect_success && !cert_spki) {
     45      return;
     46    }
     47    ASSERT_TRUE(cert_spki);
     48 
     49    ScopedSECKEYPublicKey pub_key_remote(
     50        SECKEY_ExtractPublicKey(cert_spki.get()));
     51    ASSERT_TRUE(pub_key_remote);
     52 
     53    // sym_key_sess = ECDH(session_import(private_test), public_test)
     54    ScopedPK11SymKey sym_key_sess(PK11_PubDeriveWithKDF(
     55        priv_key_sess.get(), pub_key_remote.get(), false, nullptr, nullptr,
     56        CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr,
     57        nullptr));
     58    ASSERT_EQ(expect_success, !!sym_key_sess);
     59 
     60    if (expect_success) {
     61      rv = PK11_ExtractKeyValue(sym_key_sess.get());
     62      EXPECT_EQ(SECSuccess, rv);
     63 
     64      SECItem* key_data = PK11_GetKeyData(sym_key_sess.get());
     65      EXPECT_EQ(secret_len, key_data->len);
     66      EXPECT_EQ(memcmp(key_data->data, secret, secret_len), 0);
     67 
     68      // Perform wrapped export on the imported private, import it as
     69      // permanent, and verify we derive the same shared secret
     70      static const uint8_t pw[] = "pw";
     71      SECItem pwItem = {siBuffer, toUcharPtr(pw), sizeof(pw)};
     72      ScopedSECKEYEncryptedPrivateKeyInfo epki(PK11_ExportEncryptedPrivKeyInfo(
     73          slot.get(), SEC_OID_AES_256_CBC, &pwItem, priv_key_sess.get(), 1,
     74          nullptr));
     75      ASSERT_NE(nullptr, epki) << "PK11_ExportEncryptedPrivKeyInfo failed: "
     76                               << PORT_ErrorToName(PORT_GetError());
     77 
     78      ScopedSECKEYPublicKey pub_key_local(
     79          SECKEY_ConvertToPublicKey(priv_key_sess.get()));
     80 
     81      SECKEYPrivateKey* priv_key_tok = nullptr;
     82      rv = PK11_ImportEncryptedPrivateKeyInfoAndReturnKey(
     83          slot.get(), epki.get(), &pwItem, nullptr,
     84          &pub_key_local->u.ec.publicValue, PR_TRUE, PR_TRUE, ecKey, 0,
     85          &priv_key_tok, nullptr);
     86      ASSERT_EQ(SECSuccess, rv) << "PK11_ImportEncryptedPrivateKeyInfo failed "
     87                                << PORT_ErrorToName(PORT_GetError());
     88      ASSERT_TRUE(priv_key_tok);
     89 
     90      // sym_key_tok = ECDH(token_import(export(private_test)),
     91      // public_test)
     92      ScopedPK11SymKey sym_key_tok(PK11_PubDeriveWithKDF(
     93          priv_key_tok, pub_key_remote.get(), false, nullptr, nullptr,
     94          CKM_ECDH1_DERIVE, CKM_SHA512_HMAC, CKA_DERIVE, 0, CKD_NULL, nullptr,
     95          nullptr));
     96      EXPECT_TRUE(sym_key_tok);
     97 
     98      if (sym_key_tok) {
     99        rv = PK11_ExtractKeyValue(sym_key_tok.get());
    100        EXPECT_EQ(SECSuccess, rv);
    101 
    102        key_data = PK11_GetKeyData(sym_key_tok.get());
    103        EXPECT_EQ(secret_len, key_data->len);
    104        EXPECT_EQ(memcmp(key_data->data, secret, secret_len), 0);
    105      }
    106      rv = PK11_DeleteTokenPrivateKey(priv_key_tok, true);
    107      EXPECT_EQ(SECSuccess, rv);
    108    }
    109  }
    110 
    111  void Derive(const EcdhTestVector& testvector) {
    112    std::cout << "Running test: " << testvector.id << std::endl;
    113 
    114    Derive(testvector.private_key.data(), testvector.private_key.size(),
    115           testvector.public_key.data(), testvector.public_key.size(),
    116           testvector.secret.data(), testvector.secret.size(),
    117           testvector.valid);
    118  }
    119 };
    120 
    121 class Pkcs11Curve25519Wycheproof : public Pkcs11Curve25519TestBase,
    122                                   public ::testing::Test {
    123 protected:
    124  void RunGroup(JsonReader& r) {
    125    std::vector<EcdhTestVector> tests;
    126    while (r.NextItem()) {
    127      std::string n = r.ReadLabel();
    128      if (n == "") {
    129        break;
    130      }
    131      if (n == "curve") {
    132        ASSERT_EQ("curve25519", r.ReadString());
    133      } else if (n == "type") {
    134        ASSERT_EQ("XdhComp", r.ReadString());
    135      } else if (n == "tests") {
    136        WycheproofReadTests(r, &tests, ReadTestAttr, true,
    137                            Pkcs11Curve25519Wycheproof::FilterInvalid);
    138      } else {
    139        FAIL() << "unknown group label: " << n;
    140      }
    141    }
    142 
    143    for (auto& t : tests) {
    144      Derive(t);
    145    }
    146  }
    147 
    148 private:
    149  static void FilterInvalid(EcdhTestVector& t, const std::string& result,
    150                            const std::vector<std::string>& flags) {
    151    static const std::vector<uint8_t> kNonCanonPublic1 = {
    152        0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
    153        0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
    154        0x03, 0x21, 0x00, 0xda, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    155        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    156        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    157    };
    158    static const std::vector<uint8_t> kNonCanonPublic2 = {
    159        0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02,
    160        0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda, 0x47, 0x0f, 0x01,
    161        0x03, 0x21, 0x00, 0xdb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    162        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    163        0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    164    };
    165 
    166    if (result == "acceptable" &&
    167        (std::find_if(flags.begin(), flags.end(),
    168                      [](const std::string& flag) {
    169                        return flag == "SmallPublicKey" ||
    170                               flag == "ZeroSharedSecret";
    171                      }) != flags.end() ||
    172         t.public_key == kNonCanonPublic1 ||
    173         t.public_key == kNonCanonPublic2)) {
    174      t.valid = false;
    175    }
    176  }
    177 
    178  static void ReadTestAttr(EcdhTestVector& t, const std::string& n,
    179                           JsonReader& r) {
    180    // Static PKCS#8 and SPKI wrappers for the raw keys from Wycheproof.
    181    static const std::vector<uint8_t> kPrivatePrefix = {
    182        0x30, 0x67, 0x02, 0x01, 0x00, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
    183        0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xda,
    184        0x47, 0x0f, 0x01, 0x04, 0x4c, 0x30, 0x4a, 0x02, 0x01, 0x01, 0x04, 0x20};
    185    // The public key section of the PKCS#8 wrapper is filled up with 0's, which
    186    // is not correct, but acceptable for the tests at this moment because
    187    // validity of the public key is not checked.
    188    // It's still necessary because of
    189    // https://searchfox.org/nss/rev/7bc70a3317b800aac07bad83e74b6c79a9ec5bff/lib/pk11wrap/pk11pk12.c#171
    190    static const std::vector<uint8_t> kPrivateSuffix = {
    191        0xa1, 0x23, 0x03, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    192        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    193        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
    194        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    195    static const std::vector<uint8_t> kPublicPrefix = {
    196        0x30, 0x39, 0x30, 0x14, 0x06, 0x07, 0x2a, 0x86, 0x48,
    197        0xce, 0x3d, 0x02, 0x01, 0x06, 0x09, 0x2b, 0x06, 0x01,
    198        0x04, 0x01, 0xda, 0x47, 0x0f, 0x01, 0x03, 0x21, 0x00};
    199 
    200    if (n == "public") {
    201      t.public_key = kPublicPrefix;
    202      std::vector<uint8_t> pub = r.ReadHex();
    203      t.public_key.insert(t.public_key.end(), pub.begin(), pub.end());
    204    } else if (n == "private") {
    205      t.private_key = kPrivatePrefix;
    206      std::vector<uint8_t> priv = r.ReadHex();
    207      t.private_key.insert(t.private_key.end(), priv.begin(), priv.end());
    208      t.private_key.insert(t.private_key.end(), kPrivateSuffix.begin(),
    209                           kPrivateSuffix.end());
    210    } else if (n == "shared") {
    211      t.secret = r.ReadHex();
    212    } else {
    213      FAIL() << "unsupported test case field: " << n;
    214    }
    215  }
    216 };
    217 
    218 TEST_F(Pkcs11Curve25519Wycheproof, Run) {
    219  WycheproofHeader("x25519", "XDH", "xdh_comp_schema.json",
    220                   [this](JsonReader& r) { RunGroup(r); });
    221 }
    222 
    223 class Pkcs11Curve25519ParamTest
    224    : public Pkcs11Curve25519TestBase,
    225      public ::testing::TestWithParam<EcdhTestVector> {};
    226 
    227 TEST_P(Pkcs11Curve25519ParamTest, TestVectors) { Derive(GetParam()); }
    228 
    229 INSTANTIATE_TEST_SUITE_P(NSSTestVector, Pkcs11Curve25519ParamTest,
    230                         ::testing::ValuesIn(kCurve25519Vectors));
    231 
    232 }  // namespace nss_test