blob: c9700707fef3c80d51868ec56b84c4c2b6e61923 [file] [log] [blame]
/* 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