Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/certverifier/CTLogVerifier.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "CTLogVerifier.h"
8
9
#include <stdint.h>
10
11
#include "CTSerialization.h"
12
#include "hasht.h"
13
#include "mozilla/ArrayUtils.h"
14
#include "mozilla/Assertions.h"
15
#include "pkix/pkixnss.h"
16
#include "pkixutil.h"
17
18
namespace mozilla { namespace ct {
19
20
using namespace mozilla::pkix;
21
22
// A TrustDomain used to extract the SCT log signature parameters
23
// given its subjectPublicKeyInfo.
24
// Only RSASSA-PKCS1v15 with SHA-256 and ECDSA (using the NIST P-256 curve)
25
// with SHA-256 are allowed.
26
// RSA keys must be at least 2048 bits.
27
// See See RFC 6962, Section 2.1.4.
28
class SignatureParamsTrustDomain final : public TrustDomain
29
{
30
public:
31
  SignatureParamsTrustDomain()
32
    : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous)
33
0
  {
34
0
  }
35
36
  Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
37
                      TrustLevel&) override
38
0
  {
39
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
40
0
  }
41
42
  Result FindIssuer(Input, IssuerChecker&, Time) override
43
0
  {
44
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
45
0
  }
46
47
  Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
48
                         const Input*, const Input*) override
49
0
  {
50
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
51
0
  }
52
53
  Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
54
0
  {
55
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
56
0
  }
57
58
  Result DigestBuf(Input, DigestAlgorithm, uint8_t*, size_t) override
59
0
  {
60
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
61
0
  }
62
63
  Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
64
                                       Time) override
65
0
  {
66
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
67
0
  }
68
69
  Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve curve) override
70
0
  {
71
0
    MOZ_ASSERT(mSignatureAlgorithm ==
72
0
      DigitallySigned::SignatureAlgorithm::Anonymous);
73
0
    if (curve != NamedCurve::secp256r1) {
74
0
      return Result::ERROR_UNSUPPORTED_ELLIPTIC_CURVE;
75
0
    }
76
0
    mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::ECDSA;
77
0
    return Success;
78
0
  }
79
80
  Result VerifyECDSASignedDigest(const SignedDigest&, Input) override
81
0
  {
82
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
83
0
  }
84
85
  Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
86
                                            unsigned int modulusSizeInBits)
87
                                            override
88
0
  {
89
0
    MOZ_ASSERT(mSignatureAlgorithm ==
90
0
      DigitallySigned::SignatureAlgorithm::Anonymous);
91
0
    // Require RSA keys of at least 2048 bits. See RFC 6962, Section 2.1.4.
92
0
    if (modulusSizeInBits < 2048) {
93
0
      return Result::ERROR_INADEQUATE_KEY_SIZE;
94
0
    }
95
0
    mSignatureAlgorithm = DigitallySigned::SignatureAlgorithm::RSA;
96
0
    return Success;
97
0
  }
98
99
  Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override
100
0
  {
101
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
102
0
  }
103
104
  Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
105
                                   KeyPurposeId) override
106
0
  {
107
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
108
0
  }
109
110
  Result NetscapeStepUpMatchesServerAuth(Time, bool&) override
111
0
  {
112
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
113
0
  }
114
115
  void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override
116
0
  {
117
0
  }
118
119
  DigitallySigned::SignatureAlgorithm mSignatureAlgorithm;
120
};
121
122
123
CTLogVerifier::CTLogVerifier()
124
  : mSignatureAlgorithm(DigitallySigned::SignatureAlgorithm::Anonymous)
125
  , mOperatorId(-1)
126
  , mDisqualified(false)
127
  , mDisqualificationTime(UINT64_MAX)
128
0
{
129
0
}
130
131
Result
132
CTLogVerifier::Init(Input subjectPublicKeyInfo,
133
                    CTLogOperatorId operatorId,
134
                    CTLogStatus logStatus,
135
                    uint64_t disqualificationTime)
136
0
{
137
0
  switch (logStatus) {
138
0
    case CTLogStatus::Included:
139
0
      mDisqualified = false;
140
0
      mDisqualificationTime = UINT64_MAX;
141
0
      break;
142
0
    case CTLogStatus::Disqualified:
143
0
      mDisqualified = true;
144
0
      mDisqualificationTime = disqualificationTime;
145
0
      break;
146
0
    case CTLogStatus::Unknown:
147
0
    default:
148
0
      MOZ_ASSERT_UNREACHABLE("Unsupported CTLogStatus");
149
0
      return Result::FATAL_ERROR_INVALID_ARGS;
150
0
  }
151
0
152
0
  SignatureParamsTrustDomain trustDomain;
153
0
  Result rv = CheckSubjectPublicKeyInfo(subjectPublicKeyInfo, trustDomain,
154
0
                                        EndEntityOrCA::MustBeEndEntity);
155
0
  if (rv != Success) {
156
0
    return rv;
157
0
  }
158
0
  mSignatureAlgorithm = trustDomain.mSignatureAlgorithm;
159
0
160
0
  rv = InputToBuffer(subjectPublicKeyInfo, mSubjectPublicKeyInfo);
161
0
  if (rv != Success) {
162
0
    return rv;
163
0
  }
164
0
165
0
  if (mSignatureAlgorithm == DigitallySigned::SignatureAlgorithm::ECDSA) {
166
0
    SECItem spkiSECItem = {
167
0
      siBuffer,
168
0
      mSubjectPublicKeyInfo.begin(),
169
0
      static_cast<unsigned int>(mSubjectPublicKeyInfo.length())
170
0
    };
171
0
    UniqueCERTSubjectPublicKeyInfo spki(
172
0
      SECKEY_DecodeDERSubjectPublicKeyInfo(&spkiSECItem));
173
0
    if (!spki) {
174
0
      return MapPRErrorCodeToResult(PR_GetError());
175
0
    }
176
0
    mPublicECKey.reset(SECKEY_ExtractPublicKey(spki.get()));
177
0
    if (!mPublicECKey) {
178
0
      return MapPRErrorCodeToResult(PR_GetError());
179
0
    }
180
0
    UniquePK11SlotInfo slot(PK11_GetInternalSlot());
181
0
    if (!slot) {
182
0
      return MapPRErrorCodeToResult(PR_GetError());
183
0
    }
184
0
    CK_OBJECT_HANDLE handle = PK11_ImportPublicKey(slot.get(),
185
0
                                                   mPublicECKey.get(), false);
186
0
    if (handle == CK_INVALID_HANDLE) {
187
0
      return MapPRErrorCodeToResult(PR_GetError());
188
0
    }
189
0
  } else {
190
0
    mPublicECKey.reset(nullptr);
191
0
  }
192
0
193
0
  if (!mKeyId.resizeUninitialized(SHA256_LENGTH)) {
194
0
    return Result::FATAL_ERROR_NO_MEMORY;
195
0
  }
196
0
  rv = DigestBufNSS(subjectPublicKeyInfo, DigestAlgorithm::sha256,
197
0
                    mKeyId.begin(), mKeyId.length());
198
0
  if (rv != Success) {
199
0
    return rv;
200
0
  }
201
0
202
0
  mOperatorId = operatorId;
203
0
  return Success;
204
0
}
205
206
Result
207
CTLogVerifier::Verify(const LogEntry& entry,
208
                      const SignedCertificateTimestamp& sct)
209
0
{
210
0
  if (mKeyId.empty() || sct.logId != mKeyId) {
211
0
    return Result::FATAL_ERROR_INVALID_ARGS;
212
0
  }
213
0
  if (!SignatureParametersMatch(sct.signature)) {
214
0
    return Result::FATAL_ERROR_INVALID_ARGS;
215
0
  }
216
0
217
0
  Buffer serializedLogEntry;
218
0
  Result rv = EncodeLogEntry(entry, serializedLogEntry);
219
0
  if (rv != Success) {
220
0
    return rv;
221
0
  }
222
0
223
0
  Input logEntryInput;
224
0
  rv = BufferToInput(serializedLogEntry, logEntryInput);
225
0
  if (rv != Success) {
226
0
    return rv;
227
0
  }
228
0
229
0
  // sct.extensions may be empty.  If it is, sctExtensionsInput will remain in
230
0
  // its default state, which is valid but of length 0.
231
0
  Input sctExtensionsInput;
232
0
  if (sct.extensions.length() > 0) {
233
0
    rv = sctExtensionsInput.Init(sct.extensions.begin(),
234
0
                                 sct.extensions.length());
235
0
    if (rv != Success) {
236
0
      return rv;
237
0
    }
238
0
  }
239
0
240
0
  Buffer serializedData;
241
0
  rv = EncodeV1SCTSignedData(sct.timestamp, logEntryInput, sctExtensionsInput,
242
0
                             serializedData);
243
0
  if (rv != Success) {
244
0
    return rv;
245
0
  }
246
0
  return VerifySignature(serializedData, sct.signature.signatureData);
247
0
}
248
249
Result
250
CTLogVerifier::VerifySignedTreeHead(const SignedTreeHead& sth)
251
0
{
252
0
  if (!SignatureParametersMatch(sth.signature)) {
253
0
    return Result::FATAL_ERROR_INVALID_ARGS;
254
0
  }
255
0
256
0
  Buffer serializedData;
257
0
  Result rv = EncodeTreeHeadSignature(sth, serializedData);
258
0
  if (rv != Success) {
259
0
    return rv;
260
0
  }
261
0
  return VerifySignature(serializedData, sth.signature.signatureData);
262
0
}
263
264
bool
265
CTLogVerifier::SignatureParametersMatch(const DigitallySigned& signature)
266
0
{
267
0
  return signature.SignatureParametersMatch(
268
0
    DigitallySigned::HashAlgorithm::SHA256, mSignatureAlgorithm);
269
0
}
270
271
static Result
272
FasterVerifyECDSASignedDigestNSS(const SignedDigest& sd,
273
                                 UniqueSECKEYPublicKey& pubkey)
274
0
{
275
0
  MOZ_ASSERT(pubkey);
276
0
  if (!pubkey) {
277
0
    return Result::FATAL_ERROR_LIBRARY_FAILURE;
278
0
  }
279
0
  // The signature is encoded as a DER SEQUENCE of two INTEGERs. PK11_Verify
280
0
  // expects the signature as only the two integers r and s (so no encoding -
281
0
  // just two series of bytes each half as long as SECKEY_SignatureLen(pubkey)).
282
0
  // DSAU_DecodeDerSigToLen converts from the former format to the latter.
283
0
  SECItem derSignatureSECItem(UnsafeMapInputToSECItem(sd.signature));
284
0
  size_t signatureLen = SECKEY_SignatureLen(pubkey.get());
285
0
  if (signatureLen == 0) {
286
0
    return MapPRErrorCodeToResult(PR_GetError());
287
0
  }
288
0
  UniqueSECItem signatureSECItem(DSAU_DecodeDerSigToLen(&derSignatureSECItem,
289
0
                                                        signatureLen));
290
0
  if (!signatureSECItem) {
291
0
    return MapPRErrorCodeToResult(PR_GetError());
292
0
  }
293
0
  SECItem digestSECItem(UnsafeMapInputToSECItem(sd.digest));
294
0
  SECStatus srv = PK11_Verify(pubkey.get(), signatureSECItem.get(),
295
0
                              &digestSECItem, nullptr);
296
0
  if (srv != SECSuccess) {
297
0
    return MapPRErrorCodeToResult(PR_GetError());
298
0
  }
299
0
300
0
  return Success;
301
0
}
302
303
Result
304
CTLogVerifier::VerifySignature(Input data, Input signature)
305
0
{
306
0
  uint8_t digest[SHA256_LENGTH];
307
0
  Result rv = DigestBufNSS(data, DigestAlgorithm::sha256, digest,
308
0
                           ArrayLength(digest));
309
0
  if (rv != Success) {
310
0
    return rv;
311
0
  }
312
0
313
0
  SignedDigest signedDigest;
314
0
  signedDigest.digestAlgorithm = DigestAlgorithm::sha256;
315
0
  rv = signedDigest.digest.Init(digest, ArrayLength(digest));
316
0
  if (rv != Success) {
317
0
    return rv;
318
0
  }
319
0
  rv = signedDigest.signature.Init(signature);
320
0
  if (rv != Success) {
321
0
    return rv;
322
0
  }
323
0
324
0
  Input spki;
325
0
  rv = BufferToInput(mSubjectPublicKeyInfo, spki);
326
0
  if (rv != Success) {
327
0
    return rv;
328
0
  }
329
0
330
0
  switch (mSignatureAlgorithm) {
331
0
    case DigitallySigned::SignatureAlgorithm::RSA:
332
0
      rv = VerifyRSAPKCS1SignedDigestNSS(signedDigest, spki, nullptr);
333
0
      break;
334
0
    case DigitallySigned::SignatureAlgorithm::ECDSA:
335
0
      rv = FasterVerifyECDSASignedDigestNSS(signedDigest, mPublicECKey);
336
0
      break;
337
0
    // We do not expect new values added to this enum any time soon,
338
0
    // so just listing all the available ones seems to be the easiest way
339
0
    // to suppress warning C4061 on MSVC (which expects all values of the
340
0
    // enum to be explicitly handled).
341
0
    case DigitallySigned::SignatureAlgorithm::Anonymous:
342
0
    case DigitallySigned::SignatureAlgorithm::DSA:
343
0
    default:
344
0
      MOZ_ASSERT_UNREACHABLE("RSA/ECDSA expected");
345
0
      return Result::FATAL_ERROR_INVALID_ARGS;
346
0
  }
347
0
  if (rv != Success) {
348
0
    if (IsFatalError(rv)) {
349
0
      return rv;
350
0
    }
351
0
    // If the error is non-fatal, we assume the signature was invalid.
352
0
    return Result::ERROR_BAD_SIGNATURE;
353
0
  }
354
0
  return Success;
355
0
}
356
357
Result
358
CTLogVerifier::VerifySignature(const Buffer& data, const Buffer& signature)
359
0
{
360
0
  Input dataInput;
361
0
  Result rv = BufferToInput(data, dataInput);
362
0
  if (rv != Success) {
363
0
    return rv;
364
0
  }
365
0
  Input signatureInput;
366
0
  rv = BufferToInput(signature, signatureInput);
367
0
  if (rv != Success) {
368
0
    return rv;
369
0
  }
370
0
  return VerifySignature(dataInput, signatureInput);
371
0
}
372
373
} } // namespace mozilla::ct