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, ¶ms_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, ¶m, 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, ¶m, 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