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