summaryrefslogtreecommitdiff
path: root/security/pkix/lib/pkixutil.h
blob: 390e5fd4dc8f62ac30e66f3b3241c7a5287bd5e1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This code is made available to you under your choice of the following sets
 * of licensing terms:
 */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
/* Copyright 2013 Mozilla Contributors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef mozilla_pkix_pkixutil_h
#define mozilla_pkix_pkixutil_h

#include "pkixder.h"

namespace mozilla { namespace pkix {

// During path building and verification, we build a linked list of BackCerts
// from the current cert toward the end-entity certificate. The linked list
// is used to verify properties that aren't local to the current certificate
// and/or the direct link between the current certificate and its issuer,
// such as name constraints.
//
// Each BackCert contains pointers to all the given certificate's extensions
// so that we can parse the extension block once and then process the
// extensions in an order that may be different than they appear in the cert.
class BackCert final
{
public:
  // certDER and childCert must be valid for the lifetime of BackCert.
  BackCert(Input certDER, EndEntityOrCA endEntityOrCA,
           const BackCert* childCert)
    : der(certDER)
    , endEntityOrCA(endEntityOrCA)
    , childCert(childCert)
  {
  }

  Result Init();

  const Input GetDER() const { return der; }
  const der::SignedDataWithSignature& GetSignedData() const {
    return signedData;
  }

  der::Version GetVersion() const { return version; }
  const Input GetSerialNumber() const { return serialNumber; }
  const Input GetSignature() const { return signature; }
  const Input GetIssuer() const { return issuer; }
  // XXX: "validity" is a horrible name for the structure that holds
  // notBefore & notAfter, but that is the name used in RFC 5280 and we use the
  // RFC 5280 names for everything.
  const Input GetValidity() const { return validity; }
  const Input GetSubject() const { return subject; }
  const Input GetSubjectPublicKeyInfo() const
  {
    return subjectPublicKeyInfo;
  }
  const Input* GetAuthorityInfoAccess() const
  {
    return MaybeInput(authorityInfoAccess);
  }
  const Input* GetBasicConstraints() const
  {
    return MaybeInput(basicConstraints);
  }
  const Input* GetCertificatePolicies() const
  {
    return MaybeInput(certificatePolicies);
  }
  const Input* GetExtKeyUsage() const
  {
    return MaybeInput(extKeyUsage);
  }
  const Input* GetKeyUsage() const
  {
    return MaybeInput(keyUsage);
  }
  const Input* GetInhibitAnyPolicy() const
  {
    return MaybeInput(inhibitAnyPolicy);
  }
  const Input* GetNameConstraints() const
  {
    return MaybeInput(nameConstraints);
  }
  const Input* GetSubjectAltName() const
  {
    return MaybeInput(subjectAltName);
  }
  const Input* GetRequiredTLSFeatures() const
  {
    return MaybeInput(requiredTLSFeatures);
  }
  const Input* GetSignedCertificateTimestamps() const
  {
    return MaybeInput(signedCertificateTimestamps);
  }

private:
  const Input der;

public:
  const EndEntityOrCA endEntityOrCA;
  BackCert const* const childCert;

private:
  // When parsing certificates in BackCert::Init, we don't accept empty
  // extensions. Consequently, we don't have to store a distinction between
  // empty extensions and extensions that weren't included. However, when
  // *processing* extensions, we distinguish between whether an extension was
  // included or not based on whetehr the GetXXX function for the extension
  // returns nullptr.
  static inline const Input* MaybeInput(const Input& item)
  {
    return item.GetLength() > 0 ? &item : nullptr;
  }

  der::SignedDataWithSignature signedData;

  der::Version version;
  Input serialNumber;
  Input signature;
  Input issuer;
  // XXX: "validity" is a horrible name for the structure that holds
  // notBefore & notAfter, but that is the name used in RFC 5280 and we use the
  // RFC 5280 names for everything.
  Input validity;
  Input subject;
  Input subjectPublicKeyInfo;

  Input authorityInfoAccess;
  Input basicConstraints;
  Input certificatePolicies;
  Input extKeyUsage;
  Input inhibitAnyPolicy;
  Input keyUsage;
  Input nameConstraints;
  Input subjectAltName;
  Input criticalNetscapeCertificateType;
  Input requiredTLSFeatures;
  Input signedCertificateTimestamps; // RFC 6962 (Certificate Transparency)

  Result RememberExtension(Reader& extnID, Input extnValue, bool critical,
                           /*out*/ bool& understood);

  BackCert(const BackCert&) = delete;
  void operator=(const BackCert&) = delete;
};

class NonOwningDERArray final : public DERArray
{
public:
  NonOwningDERArray()
    : numItems(0)
  {
    // we don't need to initialize the items array because we always check
    // numItems before accessing i.
  }

  size_t GetLength() const override { return numItems; }

  const Input* GetDER(size_t i) const override
  {
    return i < numItems ? &items[i] : nullptr;
  }

  Result Append(Input der)
  {
    if (numItems >= MAX_LENGTH) {
      return Result::FATAL_ERROR_INVALID_ARGS;
    }
    Result rv = items[numItems].Init(der); // structure assignment
    if (rv != Success) {
      return rv;
    }
    ++numItems;
    return Success;
  }

  // Public so we can static_assert on this. Keep in sync with MAX_SUBCA_COUNT.
  static const size_t MAX_LENGTH = 8;
private:
  Input items[MAX_LENGTH]; // avoids any heap allocations
  size_t numItems;

  NonOwningDERArray(const NonOwningDERArray&) = delete;
  void operator=(const NonOwningDERArray&) = delete;
};

// Extracts the SignedCertificateTimestampList structure which is encoded as an
// OCTET STRING within the X.509v3 / OCSP extensions (see RFC 6962 section 3.3).
Result
ExtractSignedCertificateTimestampListFromExtension(Input extnValue,
                                                   Input& sctList);

inline unsigned int
DaysBeforeYear(unsigned int year)
{
  assert(year <= 9999);
  return ((year - 1u) * 365u)
       + ((year - 1u) / 4u)    // leap years are every 4 years,
       - ((year - 1u) / 100u)  // except years divisible by 100,
       + ((year - 1u) / 400u); // except years divisible by 400.
}

static const size_t MAX_DIGEST_SIZE_IN_BYTES = 512 / 8; // sha-512

Result DigestSignedData(TrustDomain& trustDomain,
                        const der::SignedDataWithSignature& signedData,
                        /*out*/ uint8_t(&digestBuf)[MAX_DIGEST_SIZE_IN_BYTES],
                        /*out*/ der::PublicKeyAlgorithm& publicKeyAlg,
                        /*out*/ SignedDigest& signedDigest);

Result VerifySignedDigest(TrustDomain& trustDomain,
                          der::PublicKeyAlgorithm publicKeyAlg,
                          const SignedDigest& signedDigest,
                          Input signerSubjectPublicKeyInfo);

// Combines DigestSignedData and VerifySignedDigest
Result VerifySignedData(TrustDomain& trustDomain,
                        const der::SignedDataWithSignature& signedData,
                        Input signerSubjectPublicKeyInfo);

// Extracts the key parameters from |subjectPublicKeyInfo|, invoking
// the relevant methods of |trustDomain|.
Result
CheckSubjectPublicKeyInfo(Input subjectPublicKeyInfo, TrustDomain& trustDomain,
                          EndEntityOrCA endEntityOrCA);

// In a switch over an enum, sometimes some compilers are not satisfied that
// all control flow paths have been considered unless there is a default case.
// However, in our code, such a default case is almost always unreachable dead
// code. That can be particularly problematic when the compiler wants the code
// to choose a value, such as a return value, for the default case, but there's
// no appropriate "impossible case" value to choose.
//
// MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM accounts for this. Example:
//
//     // In xy.cpp
//     #include "xt.h"
//
//     enum class XY { X, Y };
//
//     int func(XY xy) {
//       switch (xy) {
//         case XY::X: return 1;
//         case XY::Y; return 2;
//         MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM
//       }
//     }
#if defined(__clang__)
// Clang will warn if not all cases are covered (-Wswitch-enum) AND it will
// warn if a switch statement that covers every enum label has a default case
// (-W-covered-switch-default). Versions prior to 3.5 warned about unreachable
// code in such default cases (-Wunreachable-code) even when
// -W-covered-switch-default was disabled, but that changed in Clang 3.5.
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM // empty
#elif defined(__GNUC__)
// GCC will warn if not all cases are covered (-Wswitch-enum). It does not
// assume that the default case is unreachable.
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \
        default: assert(false); __builtin_unreachable();
#elif defined(_MSC_VER)
// MSVC will warn if not all cases are covered (C4061, level 4). It does not
// assume that the default case is unreachable.
#define MOZILLA_PKIX_UNREACHABLE_DEFAULT_ENUM \
        default: assert(false); __assume(0);
#else
#error Unsupported compiler for MOZILLA_PKIX_UNREACHABLE_DEFAULT.
#endif

} } // namespace mozilla::pkix

#endif // mozilla_pkix_pkixutil_h