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 <memory>
6 #include "nss.h"
7 #include "pk11pub.h"
8 #include "sechash.h"
9 #include "prerror.h"
10
11 #include "cpputil.h"
12 #include "nss_scoped_ptrs.h"
13 #include "databuffer.h"
14
15 #include "gtest/gtest.h"
16 #include "pk11_signature_test.h"
17
18 namespace nss_test {
19
ImportPrivateKey(const DataBuffer & pkcs8)20 ScopedSECKEYPrivateKey Pk11SignatureTest::ImportPrivateKey(
21 const DataBuffer& pkcs8) {
22 ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
23 if (!slot) {
24 ADD_FAILURE() << "No slot";
25 return nullptr;
26 }
27
28 SECItem pkcs8Item = {siBuffer, toUcharPtr(pkcs8.data()),
29 static_cast<unsigned int>(pkcs8.len())};
30
31 SECKEYPrivateKey* key = nullptr;
32 SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
33 slot.get(), &pkcs8Item, nullptr, nullptr, false, false, KU_ALL, &key,
34 nullptr);
35
36 if (rv != SECSuccess) {
37 return nullptr;
38 }
39
40 return ScopedSECKEYPrivateKey(key);
41 }
42
ImportPublicKey(const DataBuffer & spki)43 ScopedSECKEYPublicKey Pk11SignatureTest::ImportPublicKey(
44 const DataBuffer& spki) {
45 SECItem spkiItem = {siBuffer, toUcharPtr(spki.data()),
46 static_cast<unsigned int>(spki.len())};
47
48 ScopedCERTSubjectPublicKeyInfo certSpki(
49 SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiItem));
50 if (!certSpki) {
51 return nullptr;
52 }
53
54 return ScopedSECKEYPublicKey(SECKEY_ExtractPublicKey(certSpki.get()));
55 }
56
SignHashedData(ScopedSECKEYPrivateKey & privKey,const DataBuffer & hash,DataBuffer * sig)57 bool Pk11SignatureTest::SignHashedData(ScopedSECKEYPrivateKey& privKey,
58 const DataBuffer& hash,
59 DataBuffer* sig) {
60 SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
61 static_cast<unsigned int>(hash.len())};
62 unsigned int sigLen = PK11_SignatureLen(privKey.get());
63 EXPECT_LT(0, (int)sigLen);
64 sig->Allocate(static_cast<size_t>(sigLen));
65 SECItem sigItem = {siBuffer, toUcharPtr(sig->data()),
66 static_cast<unsigned int>(sig->len())};
67 SECStatus rv = PK11_SignWithMechanism(privKey.get(), mechanism_, parameters(),
68 &sigItem, &hashItem);
69 EXPECT_EQ(sigLen, sigItem.len);
70 return rv == SECSuccess;
71 }
72
SignData(ScopedSECKEYPrivateKey & privKey,const DataBuffer & data,DataBuffer * sig)73 bool Pk11SignatureTest::SignData(ScopedSECKEYPrivateKey& privKey,
74 const DataBuffer& data, DataBuffer* sig) {
75 unsigned int sigLen = PK11_SignatureLen(privKey.get());
76 bool result = true;
77 EXPECT_LT(0, (int)sigLen);
78 sig->Allocate(static_cast<size_t>(sigLen));
79
80 // test the hash and verify interface */
81 PK11Context* context = PK11_CreateContextByPrivKey(
82 combo_, CKA_SIGN, privKey.get(), parameters());
83 if (context == NULL) {
84 ADD_FAILURE() << "Failed to sign data: couldn't create context"
85 << "\n"
86 << "mech=0x" << std::hex << combo_ << "\n"
87 << "Error: " << PORT_ErrorToString(PORT_GetError());
88 return false;
89 }
90 SECStatus rv = PK11_DigestOp(context, data.data(), data.len());
91 if (rv != SECSuccess) {
92 ADD_FAILURE() << "Failed to sign data: Update failed\n"
93 << "Error: " << PORT_ErrorToString(PORT_GetError());
94 PK11_DestroyContext(context, PR_TRUE);
95 return false;
96 }
97 unsigned int len = sigLen;
98 rv = PK11_DigestFinal(context, sig->data(), &len, sigLen);
99 if (rv != SECSuccess) {
100 ADD_FAILURE() << "Failed to sign data: final failed\n"
101 << "Error: " << PORT_ErrorToString(PORT_GetError());
102 result = false;
103 }
104 if (len != sigLen) {
105 ADD_FAILURE() << "sign data: unexpected len " << len << "expected"
106 << sigLen;
107 result = false;
108 }
109 PK11_DestroyContext(context, PR_TRUE);
110 return result;
111 }
112
ImportPrivateKeyAndSignHashedData(const DataBuffer & pkcs8,const DataBuffer & data,DataBuffer * sig,DataBuffer * sig2)113 bool Pk11SignatureTest::ImportPrivateKeyAndSignHashedData(
114 const DataBuffer& pkcs8, const DataBuffer& data, DataBuffer* sig,
115 DataBuffer* sig2) {
116 ScopedSECKEYPrivateKey privKey(ImportPrivateKey(pkcs8));
117 if (!privKey) {
118 return false;
119 }
120
121 DataBuffer hash;
122 if (!ComputeHash(data, &hash)) {
123 ADD_FAILURE() << "Failed to compute hash";
124 return false;
125 }
126 if (!SignHashedData(privKey, hash, sig)) {
127 ADD_FAILURE() << "Failed to sign hashed data";
128 return false;
129 }
130 if (!SignData(privKey, data, sig2)) {
131 /* failure was already added by SignData, with an error message */
132 return false;
133 }
134 return true;
135 }
136
Verify(ScopedSECKEYPublicKey & pubKey,const DataBuffer & data,const DataBuffer & sig,bool valid)137 void Pk11SignatureTest::Verify(ScopedSECKEYPublicKey& pubKey,
138 const DataBuffer& data, const DataBuffer& sig,
139 bool valid) {
140 SECStatus rv;
141 DataBuffer hash;
142
143 SECItem sigItem = {siBuffer, toUcharPtr(sig.data()),
144 static_cast<unsigned int>(sig.len())};
145
146 /* RSA single shot requires encoding the hash before calling
147 * VerifyWithMechanism. We already check that mechanism
148 * with the VFY_ interface, so just do the combined hash/Verify
149 * in that case */
150 if (!skip_raw_) {
151 ASSERT_TRUE(ComputeHash(data, &hash));
152
153 // Verify.
154 SECItem hashItem = {siBuffer, toUcharPtr(hash.data()),
155 static_cast<unsigned int>(hash.len())};
156 rv = PK11_VerifyWithMechanism(pubKey.get(), mechanism_, parameters(),
157 &sigItem, &hashItem, nullptr);
158 EXPECT_EQ(rv, valid ? SECSuccess : SECFailure);
159 }
160
161 // test the hash and verify interface */
162 PK11Context* context = PK11_CreateContextByPubKey(
163 combo_, CKA_VERIFY, pubKey.get(), parameters(), NULL);
164 /* we assert here because we'll crash if we try to continue
165 * without a context. */
166 ASSERT_NE((void*)context, (void*)NULL)
167 << "CreateContext failed Error:" << PORT_ErrorToString(PORT_GetError())
168 << "\n";
169 rv = PK11_DigestOp(context, data.data(), data.len());
170 /* expect success unconditionally here */
171 EXPECT_EQ(rv, SECSuccess);
172 unsigned int len;
173 rv = PK11_DigestFinal(context, sigItem.data, &len, sigItem.len);
174 EXPECT_EQ(rv, valid ? SECSuccess : SECFailure)
175 << "verify failed Error:" << PORT_ErrorToString(PORT_GetError()) << "\n";
176 PK11_DestroyContext(context, PR_TRUE);
177 }
178
179 } // namespace nss_test
180