/* 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 #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(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(spki.len())}; ScopedCERTSubjectPublicKeyInfo certSpki( SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem)); if (!certSpki) { return nullptr; } return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get())); } bool Pk11SignatureTest::SignRaw(ScopedSECKEYPrivateKey& privKey, const DataBuffer& hash, DataBuffer* sig) { SECItem hashItem = {siBuffer, toUcharPtr(hash.data()), static_cast(hash.len())}; unsigned int sigLen = PK11_SignatureLen(privKey.get()); EXPECT_LT(0, (int)sigLen); sig->Allocate(static_cast(sigLen)); SECItem sigItem = {siBuffer, toUcharPtr(sig->data()), static_cast(sig->len())}; SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism_, parameters(), &sigItem, &hashItem); EXPECT_EQ(sigLen, sigItem.len); return rv == SECSuccess; } bool Pk11SignatureTest::DigestAndSign(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(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 (!SignRaw(privKey, hash, sig)) { ADD_FAILURE() << "Failed to sign hashed data"; return false; } if (!DigestAndSign(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; SECItem sigItem = {siBuffer, toUcharPtr(sig.data()), static_cast(sig.len())}; if (skip_digest_) { SECItem dataItem = {siBuffer, toUcharPtr(data.data()), static_cast(data.len())}; rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism_, parameters(), &sigItem, &dataItem, nullptr); EXPECT_EQ(rv, valid ? SECSuccess : SECFailure); return; } DataBuffer hash; /* 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(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