Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/security/certverifier/MultiLogCTVerifier.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 "MultiLogCTVerifier.h"
8
9
#include "CTObjectsExtractor.h"
10
#include "CTSerialization.h"
11
#include "mozilla/Assertions.h"
12
#include "mozilla/Move.h"
13
14
namespace mozilla { namespace ct {
15
16
using namespace mozilla::pkix;
17
18
// Note: this moves |verifiedSct| to the target list in |result|.
19
static Result
20
StoreVerifiedSct(CTVerifyResult& result,
21
                 VerifiedSCT&& verifiedSct,
22
                 VerifiedSCT::Status status)
23
0
{
24
0
  verifiedSct.status = status;
25
0
  if (!result.verifiedScts.append(std::move(verifiedSct))) {
26
0
    return Result::FATAL_ERROR_NO_MEMORY;
27
0
  }
28
0
  return Success;
29
0
}
30
31
Result
32
MultiLogCTVerifier::AddLog(CTLogVerifier&& log)
33
0
{
34
0
  if (!mLogs.append(std::move(log))) {
35
0
    return Result::FATAL_ERROR_NO_MEMORY;
36
0
  }
37
0
  return Success;
38
0
}
39
40
Result
41
MultiLogCTVerifier::Verify(Input cert,
42
                           Input issuerSubjectPublicKeyInfo,
43
                           Input sctListFromCert,
44
                           Input sctListFromOCSPResponse,
45
                           Input sctListFromTLSExtension,
46
                           Time time,
47
                           CTVerifyResult& result)
48
0
{
49
0
  MOZ_ASSERT(cert.GetLength() > 0);
50
0
  result.Reset();
51
0
52
0
  Result rv;
53
0
54
0
  // Verify embedded SCTs
55
0
  if (issuerSubjectPublicKeyInfo.GetLength() > 0 &&
56
0
      sctListFromCert.GetLength() > 0) {
57
0
    LogEntry precertEntry;
58
0
    rv = GetPrecertLogEntry(cert, issuerSubjectPublicKeyInfo, precertEntry);
59
0
    if (rv != Success) {
60
0
      return rv;
61
0
    }
62
0
    rv = VerifySCTs(sctListFromCert, precertEntry,
63
0
                    VerifiedSCT::Origin::Embedded, time, result);
64
0
    if (rv != Success) {
65
0
      return rv;
66
0
    }
67
0
  }
68
0
69
0
  LogEntry x509Entry;
70
0
  rv = GetX509LogEntry(cert, x509Entry);
71
0
  if (rv != Success) {
72
0
    return rv;
73
0
  }
74
0
75
0
  // Verify SCTs from a stapled OCSP response
76
0
  if (sctListFromOCSPResponse.GetLength() > 0) {
77
0
    rv = VerifySCTs(sctListFromOCSPResponse, x509Entry,
78
0
                    VerifiedSCT::Origin::OCSPResponse, time, result);
79
0
    if (rv != Success) {
80
0
      return rv;
81
0
    }
82
0
  }
83
0
84
0
  // Verify SCTs from a TLS extension
85
0
  if (sctListFromTLSExtension.GetLength() > 0) {
86
0
    rv = VerifySCTs(sctListFromTLSExtension, x509Entry,
87
0
                    VerifiedSCT::Origin::TLSExtension, time, result);
88
0
    if (rv != Success) {
89
0
      return rv;
90
0
    }
91
0
  }
92
0
  return Success;
93
0
}
94
95
Result
96
MultiLogCTVerifier::VerifySCTs(Input encodedSctList,
97
                               const LogEntry& expectedEntry,
98
                               VerifiedSCT::Origin origin,
99
                               Time time,
100
                               CTVerifyResult& result)
101
0
{
102
0
  Reader listReader;
103
0
  Result rv = DecodeSCTList(encodedSctList, listReader);
104
0
  if (rv != Success) {
105
0
    result.decodingErrors++;
106
0
    return Success;
107
0
  }
108
0
109
0
  while (!listReader.AtEnd()) {
110
0
    Input encodedSct;
111
0
    rv = ReadSCTListItem(listReader, encodedSct);
112
0
    if (rv != Success) {
113
0
      result.decodingErrors++;
114
0
      return Success;
115
0
    }
116
0
117
0
    Reader encodedSctReader(encodedSct);
118
0
    SignedCertificateTimestamp sct;
119
0
    rv = DecodeSignedCertificateTimestamp(encodedSctReader, sct);
120
0
    if (rv != Success) {
121
0
      result.decodingErrors++;
122
0
      continue;
123
0
    }
124
0
125
0
    rv = VerifySingleSCT(std::move(sct), expectedEntry, origin, time, result);
126
0
    if (rv != Success) {
127
0
      return rv;
128
0
    }
129
0
  }
130
0
  return Success;
131
0
}
132
133
Result
134
MultiLogCTVerifier::VerifySingleSCT(SignedCertificateTimestamp&& sct,
135
                                    const LogEntry& expectedEntry,
136
                                    VerifiedSCT::Origin origin,
137
                                    Time time,
138
                                    CTVerifyResult& result)
139
0
{
140
0
  VerifiedSCT verifiedSct;
141
0
  verifiedSct.origin = origin;
142
0
  verifiedSct.sct = std::move(sct);
143
0
144
0
  CTLogVerifier* matchingLog = nullptr;
145
0
  for (auto& log : mLogs) {
146
0
    if (log.keyId() == verifiedSct.sct.logId) {
147
0
      matchingLog = &log;
148
0
      break;
149
0
    }
150
0
  }
151
0
152
0
  if (!matchingLog) {
153
0
    // SCT does not match any known log.
154
0
    return StoreVerifiedSct(result, std::move(verifiedSct),
155
0
                            VerifiedSCT::Status::UnknownLog);
156
0
  }
157
0
158
0
  verifiedSct.logOperatorId = matchingLog->operatorId();
159
0
160
0
  if (!matchingLog->SignatureParametersMatch(verifiedSct.sct.signature)) {
161
0
    // SCT signature parameters do not match the log's.
162
0
    return StoreVerifiedSct(result, std::move(verifiedSct),
163
0
                            VerifiedSCT::Status::InvalidSignature);
164
0
  }
165
0
166
0
  Result rv = matchingLog->Verify(expectedEntry, verifiedSct.sct);
167
0
  if (rv != Success) {
168
0
    if (rv == Result::ERROR_BAD_SIGNATURE) {
169
0
      return StoreVerifiedSct(result, std::move(verifiedSct),
170
0
                              VerifiedSCT::Status::InvalidSignature);
171
0
    }
172
0
    return rv;
173
0
  }
174
0
175
0
  // Make sure the timestamp is legitimate (not in the future).
176
0
  // SCT's |timestamp| is measured in milliseconds since the epoch,
177
0
  // ignoring leap seconds. When converting it to a second-level precision
178
0
  // pkix::Time, we need to round it either up or down. In our case, rounding up
179
0
  // (towards the future) is more "secure", although practically
180
0
  // it does not matter.
181
0
  Time sctTime =
182
0
    TimeFromEpochInSeconds((verifiedSct.sct.timestamp + 999u) / 1000u);
183
0
  if (sctTime > time) {
184
0
    return StoreVerifiedSct(result, std::move(verifiedSct),
185
0
                            VerifiedSCT::Status::InvalidTimestamp);
186
0
  }
187
0
188
0
  // SCT verified ok, see if the log is qualified. Since SCTs from
189
0
  // disqualified logs are treated as valid under certain circumstances (see
190
0
  // the CT Policy), the log qualification check must be the last one we do.
191
0
  if (matchingLog->isDisqualified()) {
192
0
    verifiedSct.logDisqualificationTime = matchingLog->disqualificationTime();
193
0
    return StoreVerifiedSct(result, std::move(verifiedSct),
194
0
                            VerifiedSCT::Status::ValidFromDisqualifiedLog);
195
0
  }
196
0
197
0
  return StoreVerifiedSct(result, std::move(verifiedSct),
198
0
                          VerifiedSCT::Status::Valid);
199
0
}
200
201
} } // namespace mozilla::ct