Line data Source code
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
3 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 :
5 : #include "nsDataSignatureVerifier.h"
6 :
7 : #include "ScopedNSSTypes.h"
8 : #include "SharedCertVerifier.h"
9 : #include "cms.h"
10 : #include "cryptohi.h"
11 : #include "keyhi.h"
12 : #include "mozilla/Base64.h"
13 : #include "mozilla/Casting.h"
14 : #include "mozilla/Unused.h"
15 : #include "nsCOMPtr.h"
16 : #include "nsNSSComponent.h"
17 : #include "nsString.h"
18 : #include "pkix/pkixnss.h"
19 : #include "pkix/pkixtypes.h"
20 : #include "secerr.h"
21 :
22 : using namespace mozilla;
23 : using namespace mozilla::pkix;
24 : using namespace mozilla::psm;
25 :
26 : SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
27 :
28 0 : NS_IMPL_ISUPPORTS(nsDataSignatureVerifier, nsIDataSignatureVerifier)
29 :
30 : const SEC_ASN1Template CERT_SignatureDataTemplate[] =
31 : {
32 : { SEC_ASN1_SEQUENCE,
33 : 0, nullptr, sizeof(CERTSignedData) },
34 : { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
35 : offsetof(CERTSignedData,signatureAlgorithm),
36 : SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate), },
37 : { SEC_ASN1_BIT_STRING,
38 : offsetof(CERTSignedData,signature), },
39 : { 0, }
40 : };
41 :
42 0 : nsDataSignatureVerifier::~nsDataSignatureVerifier()
43 : {
44 0 : nsNSSShutDownPreventionLock locker;
45 0 : if (isAlreadyShutDown()) {
46 0 : return;
47 : }
48 :
49 0 : shutdown(ShutdownCalledFrom::Object);
50 0 : }
51 :
52 : NS_IMETHODIMP
53 0 : nsDataSignatureVerifier::VerifyData(const nsACString& aData,
54 : const nsACString& aSignature,
55 : const nsACString& aPublicKey,
56 : bool* _retval)
57 : {
58 0 : NS_ENSURE_ARG_POINTER(_retval);
59 :
60 0 : nsNSSShutDownPreventionLock locker;
61 0 : if (isAlreadyShutDown()) {
62 0 : return NS_ERROR_NOT_AVAILABLE;
63 : }
64 :
65 : // Allocate an arena to handle the majority of the allocations
66 0 : UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
67 0 : if (!arena) {
68 0 : return NS_ERROR_OUT_OF_MEMORY;
69 : }
70 :
71 : // Base 64 decode the key
72 : // For compatibility reasons we need to remove all whitespace first, since
73 : // Base64Decode() will not accept invalid characters.
74 0 : nsAutoCString b64KeyNoWhitespace(aPublicKey);
75 0 : b64KeyNoWhitespace.StripWhitespace();
76 0 : nsAutoCString key;
77 0 : nsresult rv = Base64Decode(b64KeyNoWhitespace, key);
78 0 : if (NS_FAILED(rv)) {
79 0 : return rv;
80 : }
81 :
82 : // Extract the public key from the data
83 : SECItem keyItem = {
84 : siBuffer,
85 0 : BitwiseCast<unsigned char*, const char*>(key.get()),
86 0 : key.Length(),
87 0 : };
88 : UniqueCERTSubjectPublicKeyInfo pki(
89 0 : SECKEY_DecodeDERSubjectPublicKeyInfo(&keyItem));
90 0 : if (!pki) {
91 0 : return NS_ERROR_FAILURE;
92 : }
93 :
94 0 : UniqueSECKEYPublicKey publicKey(SECKEY_ExtractPublicKey(pki.get()));
95 0 : if (!publicKey) {
96 0 : return NS_ERROR_FAILURE;
97 : }
98 :
99 : // Base 64 decode the signature
100 : // For compatibility reasons we need to remove all whitespace first, since
101 : // Base64Decode() will not accept invalid characters.
102 0 : nsAutoCString b64SignatureNoWhitespace(aSignature);
103 0 : b64SignatureNoWhitespace.StripWhitespace();
104 0 : nsAutoCString signature;
105 0 : rv = Base64Decode(b64SignatureNoWhitespace, signature);
106 0 : if (NS_FAILED(rv)) {
107 0 : return rv;
108 : }
109 :
110 : // Decode the signature and algorithm
111 : CERTSignedData sigData;
112 0 : PORT_Memset(&sigData, 0, sizeof(CERTSignedData));
113 : SECItem signatureItem = {
114 : siBuffer,
115 0 : BitwiseCast<unsigned char*, const char*>(signature.get()),
116 0 : signature.Length(),
117 0 : };
118 0 : SECStatus srv = SEC_QuickDERDecodeItem(arena.get(), &sigData,
119 : CERT_SignatureDataTemplate,
120 0 : &signatureItem);
121 0 : if (srv != SECSuccess) {
122 0 : return NS_ERROR_FAILURE;
123 : }
124 :
125 : // Perform the final verification
126 0 : DER_ConvertBitString(&(sigData.signature));
127 0 : srv = VFY_VerifyDataWithAlgorithmID(
128 : BitwiseCast<const unsigned char*, const char*>(
129 0 : PromiseFlatCString(aData).get()),
130 0 : aData.Length(), publicKey.get(), &(sigData.signature),
131 0 : &(sigData.signatureAlgorithm), nullptr, nullptr);
132 :
133 0 : *_retval = (srv == SECSuccess);
134 :
135 0 : return NS_OK;
136 : }
137 :
138 : namespace mozilla {
139 :
140 : nsresult
141 0 : VerifyCMSDetachedSignatureIncludingCertificate(
142 : const SECItem& buffer, const SECItem& detachedDigest,
143 : nsresult (*verifyCertificate)(CERTCertificate* cert, void* context,
144 : void* pinArg),
145 : void* verifyCertificateContext, void* pinArg,
146 : const nsNSSShutDownPreventionLock& /*proofOfLock*/)
147 : {
148 : // XXX: missing pinArg is tolerated.
149 0 : if (NS_WARN_IF(!buffer.data && buffer.len > 0) ||
150 0 : NS_WARN_IF(!detachedDigest.data && detachedDigest.len > 0) ||
151 0 : (!verifyCertificate) ||
152 0 : NS_WARN_IF(!verifyCertificateContext)) {
153 0 : return NS_ERROR_INVALID_ARG;
154 : }
155 :
156 : UniqueNSSCMSMessage
157 : cmsMsg(NSS_CMSMessage_CreateFromDER(const_cast<SECItem*>(&buffer), nullptr,
158 : nullptr, nullptr, nullptr, nullptr,
159 0 : nullptr));
160 0 : if (!cmsMsg) {
161 0 : return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
162 : }
163 :
164 0 : if (!NSS_CMSMessage_IsSigned(cmsMsg.get())) {
165 0 : return NS_ERROR_CMS_VERIFY_NOT_SIGNED;
166 : }
167 :
168 0 : NSSCMSContentInfo* cinfo = NSS_CMSMessage_ContentLevel(cmsMsg.get(), 0);
169 0 : if (!cinfo) {
170 0 : return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
171 : }
172 :
173 : // signedData is non-owning
174 : NSSCMSSignedData* signedData =
175 0 : static_cast<NSSCMSSignedData*>(NSS_CMSContentInfo_GetContent(cinfo));
176 0 : if (!signedData) {
177 0 : return NS_ERROR_CMS_VERIFY_NO_CONTENT_INFO;
178 : }
179 :
180 : // Set digest value.
181 0 : if (NSS_CMSSignedData_SetDigestValue(signedData, SEC_OID_SHA1,
182 : const_cast<SECItem*>(&detachedDigest))) {
183 0 : return NS_ERROR_CMS_VERIFY_BAD_DIGEST;
184 : }
185 :
186 : // Parse the certificates into CERTCertificate objects held in memory so
187 : // verifyCertificate will be able to find them during path building.
188 0 : UniqueCERTCertList certs(CERT_NewCertList());
189 0 : if (!certs) {
190 0 : return NS_ERROR_OUT_OF_MEMORY;
191 : }
192 0 : if (signedData->rawCerts) {
193 0 : for (size_t i = 0; signedData->rawCerts[i]; ++i) {
194 : UniqueCERTCertificate
195 : cert(CERT_NewTempCertificate(CERT_GetDefaultCertDB(),
196 0 : signedData->rawCerts[i], nullptr, false,
197 0 : true));
198 : // Skip certificates that fail to parse
199 0 : if (!cert) {
200 0 : continue;
201 : }
202 :
203 0 : if (CERT_AddCertToListTail(certs.get(), cert.get()) != SECSuccess) {
204 0 : return NS_ERROR_OUT_OF_MEMORY;
205 : }
206 :
207 0 : Unused << cert.release(); // Ownership transferred to the cert list.
208 : }
209 : }
210 :
211 : // Get the end-entity certificate.
212 0 : int numSigners = NSS_CMSSignedData_SignerInfoCount(signedData);
213 0 : if (NS_WARN_IF(numSigners != 1)) {
214 0 : return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
215 : }
216 : // signer is non-owning.
217 0 : NSSCMSSignerInfo* signer = NSS_CMSSignedData_GetSignerInfo(signedData, 0);
218 0 : if (NS_WARN_IF(!signer)) {
219 0 : return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
220 : }
221 : CERTCertificate* signerCert =
222 0 : NSS_CMSSignerInfo_GetSigningCertificate(signer, CERT_GetDefaultCertDB());
223 0 : if (!signerCert) {
224 0 : return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
225 : }
226 :
227 0 : nsresult rv = verifyCertificate(signerCert, verifyCertificateContext, pinArg);
228 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
229 0 : return rv;
230 : }
231 :
232 : // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
233 : SECOidData* contentTypeOidData =
234 0 : SECOID_FindOID(&signedData->contentInfo.contentType);
235 0 : if (!contentTypeOidData) {
236 0 : return NS_ERROR_CMS_VERIFY_ERROR_PROCESSING;
237 : }
238 :
239 0 : return MapSECStatus(NSS_CMSSignerInfo_Verify(signer,
240 : const_cast<SECItem*>(&detachedDigest),
241 0 : &contentTypeOidData->oid));
242 : }
243 :
244 : } // namespace mozilla
245 :
246 : namespace {
247 :
248 0 : struct VerifyCertificateContext
249 : {
250 : nsCOMPtr<nsIX509Cert> signingCert;
251 : UniqueCERTCertList builtChain;
252 : };
253 :
254 : static nsresult
255 0 : VerifyCertificate(CERTCertificate* cert, void* voidContext, void* pinArg)
256 : {
257 : // XXX: missing pinArg is tolerated
258 0 : if (NS_WARN_IF(!cert) || NS_WARN_IF(!voidContext)) {
259 0 : return NS_ERROR_INVALID_ARG;
260 : }
261 :
262 : VerifyCertificateContext* context =
263 0 : static_cast<VerifyCertificateContext*>(voidContext);
264 :
265 0 : nsCOMPtr<nsIX509Cert> xpcomCert(nsNSSCertificate::Create(cert));
266 0 : if (!xpcomCert) {
267 0 : return NS_ERROR_OUT_OF_MEMORY;
268 : }
269 :
270 0 : context->signingCert = xpcomCert;
271 :
272 0 : RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
273 0 : NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
274 :
275 : mozilla::pkix::Result result =
276 0 : certVerifier->VerifyCert(cert,
277 : certificateUsageObjectSigner,
278 : Now(), pinArg,
279 : nullptr, // hostname
280 0 : context->builtChain);
281 0 : if (result != Success) {
282 0 : return GetXPCOMFromNSSError(MapResultToPRErrorCode(result));
283 : }
284 :
285 0 : return NS_OK;
286 : }
287 :
288 : } // namespace
289 :
290 : NS_IMETHODIMP
291 0 : nsDataSignatureVerifier::VerifySignature(const nsACString& aRSABuf,
292 : const nsACString& aPlaintext,
293 : int32_t* aErrorCode,
294 : nsIX509Cert** aSigningCert)
295 : {
296 0 : if (!aErrorCode || !aSigningCert) {
297 0 : return NS_ERROR_INVALID_ARG;
298 : }
299 :
300 0 : nsNSSShutDownPreventionLock locker;
301 0 : if (isAlreadyShutDown()) {
302 0 : return NS_ERROR_NOT_AVAILABLE;
303 : }
304 :
305 0 : *aErrorCode = VERIFY_ERROR_OTHER;
306 0 : *aSigningCert = nullptr;
307 :
308 0 : Digest digest;
309 0 : nsresult rv = digest.DigestBuf(
310 : SEC_OID_SHA1,
311 : BitwiseCast<const uint8_t*, const char*>(aPlaintext.BeginReading()),
312 0 : aPlaintext.Length());
313 0 : if (NS_WARN_IF(NS_FAILED(rv))) {
314 0 : return rv;
315 : }
316 :
317 : SECItem buffer = {
318 : siBuffer,
319 0 : BitwiseCast<unsigned char*, const char*>(aRSABuf.BeginReading()),
320 0 : aRSABuf.Length(),
321 0 : };
322 :
323 0 : VerifyCertificateContext context;
324 : // XXX: pinArg is missing
325 0 : rv = VerifyCMSDetachedSignatureIncludingCertificate(buffer, digest.get(),
326 : VerifyCertificate,
327 0 : &context, nullptr, locker);
328 0 : if (NS_SUCCEEDED(rv)) {
329 0 : *aErrorCode = VERIFY_OK;
330 0 : } else if (NS_ERROR_GET_MODULE(rv) == NS_ERROR_MODULE_SECURITY) {
331 0 : if (rv == GetXPCOMFromNSSError(SEC_ERROR_UNKNOWN_ISSUER)) {
332 0 : *aErrorCode = VERIFY_ERROR_UNKNOWN_ISSUER;
333 : } else {
334 0 : *aErrorCode = VERIFY_ERROR_OTHER;
335 : }
336 0 : rv = NS_OK;
337 : }
338 0 : if (rv == NS_OK) {
339 0 : context.signingCert.forget(aSigningCert);
340 : }
341 :
342 0 : return rv;
343 : }
|