diff options
Diffstat (limited to 'security/nss/gtests/pk11_gtest/pk11_signature_test.cc')
-rw-r--r-- | security/nss/gtests/pk11_gtest/pk11_signature_test.cc | 179 |
1 files changed, 179 insertions, 0 deletions
diff --git a/security/nss/gtests/pk11_gtest/pk11_signature_test.cc b/security/nss/gtests/pk11_gtest/pk11_signature_test.cc new file mode 100644 index 0000000000..c9700707fe --- /dev/null +++ b/security/nss/gtests/pk11_gtest/pk11_signature_test.cc @@ -0,0 +1,179 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <memory> +#include "nss.h" +#include "pk11pub.h" +#include "sechash.h" +#include "prerror.h" + +#include "cpputil.h" +#include "nss_scoped_ptrs.h" +#include "databuffer.h" + +#include "gtest/gtest.h" +#include "pk11_signature_test.h" + +namespace nss_test { + +ScopedSECKEYPrivateKey Pk11SignatureTest::ImportPrivateKey( + const DataBuffer& pkcs8) { + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + ADD_FAILURE() << "No slot"; + return nullptr; + } + + SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8.data()), + static_cast<unsigned int>(pkcs8.len())}; + + SECKEYPrivateKey* key = nullptr; + SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey( + slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key, + nullptr); + + if (rv != SECSuccess) { + return nullptr; + } + + return ScopedSECKEYPrivateKey(key); +} + +ScopedSECKEYPublicKey Pk11SignatureTest::ImportPublicKey( + const DataBuffer& spki) { + SECItem spkiItem = {siBuffer, toUcharPtr(spki.data()), + static_cast<unsigned int>(spki.len())}; + + ScopedCERTSubjectPublicKeyInfo certSpki( + SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); + if (!certSpki) { + return nullptr; + } + + return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get())); +} + +bool Pk11SignatureTest::SignHashedData(ScopedSECKEYPrivateKey& privKey, + const DataBuffer& hash, + DataBuffer* sig) { + SECItem hashItem = {siBuffer, toUcharPtr(hash.data()), + static_cast<unsigned int>(hash.len())}; + unsigned int sigLen = PK11_SignatureLen(privKey.get()); + EXPECT_LT(0, (int)sigLen); + sig->Allocate(static_cast<size_t>(sigLen)); + SECItem sigItem = {siBuffer, toUcharPtr(sig->data()), + static_cast<unsigned int>(sig->len())}; + SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism_, parameters(), + &sigItem, &hashItem); + EXPECT_EQ(sigLen, sigItem.len); + return rv == SECSuccess; +} + +bool Pk11SignatureTest::SignData(ScopedSECKEYPrivateKey& privKey, + const DataBuffer& data, DataBuffer* sig) { + unsigned int sigLen = PK11_SignatureLen(privKey.get()); + bool result = true; + EXPECT_LT(0, (int)sigLen); + sig->Allocate(static_cast<size_t>(sigLen)); + + // test the hash and verify interface */ + PK11Context* context = PK11_CreateContextByPrivKey( + combo_, CKA_SIGN, privKey.get(), parameters()); + if (context == NULL) { + ADD_FAILURE() << "Failed to sign data: couldn't create context" + << "\n" + << "mech=0x" << std::hex << combo_ << "\n" + << "Error: " << PORT_ErrorToString(PORT_GetError()); + return false; + } + SECStatus rv = PK11_DigestOp(context, data.data(), data.len()); + if (rv != SECSuccess) { + ADD_FAILURE() << "Failed to sign data: Update failed\n" + << "Error: " << PORT_ErrorToString(PORT_GetError()); + PK11_DestroyContext(context, PR_TRUE); + return false; + } + unsigned int len = sigLen; + rv = PK11_DigestFinal(context, sig->data(), &len, sigLen); + if (rv != SECSuccess) { + ADD_FAILURE() << "Failed to sign data: final failed\n" + << "Error: " << PORT_ErrorToString(PORT_GetError()); + result = false; + } + if (len != sigLen) { + ADD_FAILURE() << "sign data: unexpected len " << len << "expected" + << sigLen; + result = false; + } + PK11_DestroyContext(context, PR_TRUE); + return result; +} + +bool Pk11SignatureTest::ImportPrivateKeyAndSignHashedData( + const DataBuffer& pkcs8, const DataBuffer& data, DataBuffer* sig, + DataBuffer* sig2) { + ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8)); + if (!privKey) { + return false; + } + + DataBuffer hash; + if (!ComputeHash(data, &hash)) { + ADD_FAILURE() << "Failed to compute hash"; + return false; + } + if (!SignHashedData(privKey, hash, sig)) { + ADD_FAILURE() << "Failed to sign hashed data"; + return false; + } + if (!SignData(privKey, data, sig2)) { + /* failure was already added by SignData, with an error message */ + return false; + } + return true; +} + +void Pk11SignatureTest::Verify(ScopedSECKEYPublicKey& pubKey, + const DataBuffer& data, const DataBuffer& sig, + bool valid) { + SECStatus rv; + DataBuffer hash; + + SECItem sigItem = {siBuffer, toUcharPtr(sig.data()), + static_cast<unsigned int>(sig.len())}; + + /* RSA single shot requires encoding the hash before calling + * VerifyWithMechanism. We already check that mechanism + * with the VFY_ interface, so just do the combined hash/Verify + * in that case */ + if (!skip_raw_) { + ASSERT_TRUE(ComputeHash(data, &hash)); + + // Verify. + SECItem hashItem = {siBuffer, toUcharPtr(hash.data()), + static_cast<unsigned int>(hash.len())}; + rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism_, parameters(), + &sigItem, &hashItem, nullptr); + EXPECT_EQ(rv, valid ? SECSuccess : SECFailure); + } + + // test the hash and verify interface */ + PK11Context* context = PK11_CreateContextByPubKey( + combo_, CKA_VERIFY, pubKey.get(), parameters(), NULL); + /* we assert here because we'll crash if we try to continue + * without a context. */ + ASSERT_NE((void*)context, (void*)NULL) + << "CreateContext failed Error:" << PORT_ErrorToString(PORT_GetError()) + << "\n"; + rv = PK11_DigestOp(context, data.data(), data.len()); + /* expect success unconditionally here */ + EXPECT_EQ(rv, SECSuccess); + unsigned int len; + rv = PK11_DigestFinal(context, sigItem.data, &len, sigItem.len); + EXPECT_EQ(rv, valid ? SECSuccess : SECFailure) + << "verify failed Error:" << PORT_ErrorToString(PORT_GetError()) << "\n"; + PK11_DestroyContext(context, PR_TRUE); +} + +} // namespace nss_test |