/src/mozilla-central/security/pkix/lib/pkixder.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 code is made available to you under your choice of the following sets |
4 | | * of licensing terms: |
5 | | */ |
6 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
7 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
8 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
9 | | */ |
10 | | /* Copyright 2013 Mozilla Contributors |
11 | | * |
12 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
13 | | * you may not use this file except in compliance with the License. |
14 | | * You may obtain a copy of the License at |
15 | | * |
16 | | * http://www.apache.org/licenses/LICENSE-2.0 |
17 | | * |
18 | | * Unless required by applicable law or agreed to in writing, software |
19 | | * distributed under the License is distributed on an "AS IS" BASIS, |
20 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
21 | | * See the License for the specific language governing permissions and |
22 | | * limitations under the License. |
23 | | */ |
24 | | |
25 | | #include "pkixder.h" |
26 | | |
27 | | #include "pkixutil.h" |
28 | | |
29 | | namespace mozilla { namespace pkix { namespace der { |
30 | | |
31 | | // Too complicated to be inline |
32 | | Result |
33 | | ReadTagAndGetValue(Reader& input, /*out*/ uint8_t& tag, /*out*/ Input& value) |
34 | 0 | { |
35 | 0 | Result rv; |
36 | 0 |
|
37 | 0 | rv = input.Read(tag); |
38 | 0 | if (rv != Success) { |
39 | 0 | return rv; |
40 | 0 | } |
41 | 0 | if ((tag & 0x1F) == 0x1F) { |
42 | 0 | return Result::ERROR_BAD_DER; // high tag number form not allowed |
43 | 0 | } |
44 | 0 | |
45 | 0 | uint16_t length; |
46 | 0 |
|
47 | 0 | // The short form of length is a single byte with the high order bit set |
48 | 0 | // to zero. The long form of length is one byte with the high order bit |
49 | 0 | // set, followed by N bytes, where N is encoded in the lowest 7 bits of |
50 | 0 | // the first byte. |
51 | 0 | uint8_t length1; |
52 | 0 | rv = input.Read(length1); |
53 | 0 | if (rv != Success) { |
54 | 0 | return rv; |
55 | 0 | } |
56 | 0 | if (!(length1 & 0x80)) { |
57 | 0 | length = length1; |
58 | 0 | } else if (length1 == 0x81) { |
59 | 0 | uint8_t length2; |
60 | 0 | rv = input.Read(length2); |
61 | 0 | if (rv != Success) { |
62 | 0 | return rv; |
63 | 0 | } |
64 | 0 | if (length2 < 128) { |
65 | 0 | // Not shortest possible encoding |
66 | 0 | return Result::ERROR_BAD_DER; |
67 | 0 | } |
68 | 0 | length = length2; |
69 | 0 | } else if (length1 == 0x82) { |
70 | 0 | rv = input.Read(length); |
71 | 0 | if (rv != Success) { |
72 | 0 | return rv; |
73 | 0 | } |
74 | 0 | if (length < 256) { |
75 | 0 | // Not shortest possible encoding |
76 | 0 | return Result::ERROR_BAD_DER; |
77 | 0 | } |
78 | 0 | } else { |
79 | 0 | // We don't support lengths larger than 2^16 - 1. |
80 | 0 | return Result::ERROR_BAD_DER; |
81 | 0 | } |
82 | 0 | |
83 | 0 | return input.Skip(length, value); |
84 | 0 | } |
85 | | |
86 | | static Result |
87 | | OptionalNull(Reader& input) |
88 | 0 | { |
89 | 0 | if (input.Peek(NULLTag)) { |
90 | 0 | return Null(input); |
91 | 0 | } |
92 | 0 | return Success; |
93 | 0 | } |
94 | | |
95 | | namespace { |
96 | | |
97 | | Result |
98 | | AlgorithmIdentifierValue(Reader& input, /*out*/ Reader& algorithmOIDValue) |
99 | 0 | { |
100 | 0 | Result rv = ExpectTagAndGetValue(input, der::OIDTag, algorithmOIDValue); |
101 | 0 | if (rv != Success) { |
102 | 0 | return rv; |
103 | 0 | } |
104 | 0 | return OptionalNull(input); |
105 | 0 | } |
106 | | |
107 | | } // namespace |
108 | | |
109 | | Result |
110 | | SignatureAlgorithmIdentifierValue(Reader& input, |
111 | | /*out*/ PublicKeyAlgorithm& publicKeyAlgorithm, |
112 | | /*out*/ DigestAlgorithm& digestAlgorithm) |
113 | 0 | { |
114 | 0 | // RFC 5758 Section 3.2 (ECDSA with SHA-2), and RFC 3279 Section 2.2.3 |
115 | 0 | // (ECDSA with SHA-1) say that parameters must be omitted. |
116 | 0 | // |
117 | 0 | // RFC 4055 Section 5 and RFC 3279 Section 2.2.1 both say that parameters for |
118 | 0 | // RSA must be encoded as NULL; we relax that requirement by allowing the |
119 | 0 | // NULL to be omitted, to match all the other signature algorithms we support |
120 | 0 | // and for compatibility. |
121 | 0 | Reader algorithmID; |
122 | 0 | Result rv = AlgorithmIdentifierValue(input, algorithmID); |
123 | 0 | if (rv != Success) { |
124 | 0 | return rv; |
125 | 0 | } |
126 | 0 | |
127 | 0 | // RFC 5758 Section 3.2 (ecdsa-with-SHA224 is intentionally excluded) |
128 | 0 | // python DottedOIDToCode.py ecdsa-with-SHA256 1.2.840.10045.4.3.2 |
129 | 0 | static const uint8_t ecdsa_with_SHA256[] = { |
130 | 0 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 |
131 | 0 | }; |
132 | 0 | // python DottedOIDToCode.py ecdsa-with-SHA384 1.2.840.10045.4.3.3 |
133 | 0 | static const uint8_t ecdsa_with_SHA384[] = { |
134 | 0 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 |
135 | 0 | }; |
136 | 0 | // python DottedOIDToCode.py ecdsa-with-SHA512 1.2.840.10045.4.3.4 |
137 | 0 | static const uint8_t ecdsa_with_SHA512[] = { |
138 | 0 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 |
139 | 0 | }; |
140 | 0 |
|
141 | 0 | // RFC 4055 Section 5 (sha224WithRSAEncryption is intentionally excluded) |
142 | 0 | // python DottedOIDToCode.py sha256WithRSAEncryption 1.2.840.113549.1.1.11 |
143 | 0 | static const uint8_t sha256WithRSAEncryption[] = { |
144 | 0 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b |
145 | 0 | }; |
146 | 0 | // python DottedOIDToCode.py sha384WithRSAEncryption 1.2.840.113549.1.1.12 |
147 | 0 | static const uint8_t sha384WithRSAEncryption[] = { |
148 | 0 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c |
149 | 0 | }; |
150 | 0 | // python DottedOIDToCode.py sha512WithRSAEncryption 1.2.840.113549.1.1.13 |
151 | 0 | static const uint8_t sha512WithRSAEncryption[] = { |
152 | 0 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d |
153 | 0 | }; |
154 | 0 |
|
155 | 0 | // RFC 3279 Section 2.2.1 |
156 | 0 | // python DottedOIDToCode.py sha-1WithRSAEncryption 1.2.840.113549.1.1.5 |
157 | 0 | static const uint8_t sha_1WithRSAEncryption[] = { |
158 | 0 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 |
159 | 0 | }; |
160 | 0 |
|
161 | 0 | // NIST Open Systems Environment (OSE) Implementor's Workshop (OIW) |
162 | 0 | // http://www.oiw.org/agreements/stable/12s-9412.txt (no longer works). |
163 | 0 | // http://www.imc.org/ietf-pkix/old-archive-97/msg01166.html |
164 | 0 | // We need to support this this non-PKIX OID for compatibility. |
165 | 0 | // python DottedOIDToCode.py sha1WithRSASignature 1.3.14.3.2.29 |
166 | 0 | static const uint8_t sha1WithRSASignature[] = { |
167 | 0 | 0x2b, 0x0e, 0x03, 0x02, 0x1d |
168 | 0 | }; |
169 | 0 |
|
170 | 0 | // RFC 3279 Section 2.2.3 |
171 | 0 | // python DottedOIDToCode.py ecdsa-with-SHA1 1.2.840.10045.4.1 |
172 | 0 | static const uint8_t ecdsa_with_SHA1[] = { |
173 | 0 | 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 |
174 | 0 | }; |
175 | 0 |
|
176 | 0 | // Matching is attempted based on a rough estimate of the commonality of the |
177 | 0 | // algorithm, to minimize the number of MatchRest calls. |
178 | 0 | if (algorithmID.MatchRest(sha256WithRSAEncryption)) { |
179 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; |
180 | 0 | digestAlgorithm = DigestAlgorithm::sha256; |
181 | 0 | } else if (algorithmID.MatchRest(ecdsa_with_SHA256)) { |
182 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; |
183 | 0 | digestAlgorithm = DigestAlgorithm::sha256; |
184 | 0 | } else if (algorithmID.MatchRest(sha_1WithRSAEncryption)) { |
185 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; |
186 | 0 | digestAlgorithm = DigestAlgorithm::sha1; |
187 | 0 | } else if (algorithmID.MatchRest(ecdsa_with_SHA1)) { |
188 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; |
189 | 0 | digestAlgorithm = DigestAlgorithm::sha1; |
190 | 0 | } else if (algorithmID.MatchRest(ecdsa_with_SHA384)) { |
191 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; |
192 | 0 | digestAlgorithm = DigestAlgorithm::sha384; |
193 | 0 | } else if (algorithmID.MatchRest(ecdsa_with_SHA512)) { |
194 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::ECDSA; |
195 | 0 | digestAlgorithm = DigestAlgorithm::sha512; |
196 | 0 | } else if (algorithmID.MatchRest(sha384WithRSAEncryption)) { |
197 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; |
198 | 0 | digestAlgorithm = DigestAlgorithm::sha384; |
199 | 0 | } else if (algorithmID.MatchRest(sha512WithRSAEncryption)) { |
200 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; |
201 | 0 | digestAlgorithm = DigestAlgorithm::sha512; |
202 | 0 | } else if (algorithmID.MatchRest(sha1WithRSASignature)) { |
203 | 0 | // XXX(bug 1042479): recognize this old OID for compatibility. |
204 | 0 | publicKeyAlgorithm = PublicKeyAlgorithm::RSA_PKCS1; |
205 | 0 | digestAlgorithm = DigestAlgorithm::sha1; |
206 | 0 | } else { |
207 | 0 | return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED; |
208 | 0 | } |
209 | 0 | |
210 | 0 | return Success; |
211 | 0 | } |
212 | | |
213 | | Result |
214 | | DigestAlgorithmIdentifier(Reader& input, /*out*/ DigestAlgorithm& algorithm) |
215 | 0 | { |
216 | 0 | return der::Nested(input, SEQUENCE, [&algorithm](Reader& r) -> Result { |
217 | 0 | Reader algorithmID; |
218 | 0 | Result rv = AlgorithmIdentifierValue(r, algorithmID); |
219 | 0 | if (rv != Success) { |
220 | 0 | return rv; |
221 | 0 | } |
222 | 0 | |
223 | 0 | // RFC 4055 Section 2.1 |
224 | 0 | // python DottedOIDToCode.py id-sha1 1.3.14.3.2.26 |
225 | 0 | static const uint8_t id_sha1[] = { |
226 | 0 | 0x2b, 0x0e, 0x03, 0x02, 0x1a |
227 | 0 | }; |
228 | 0 | // python DottedOIDToCode.py id-sha256 2.16.840.1.101.3.4.2.1 |
229 | 0 | static const uint8_t id_sha256[] = { |
230 | 0 | 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 |
231 | 0 | }; |
232 | 0 | // python DottedOIDToCode.py id-sha384 2.16.840.1.101.3.4.2.2 |
233 | 0 | static const uint8_t id_sha384[] = { |
234 | 0 | 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 |
235 | 0 | }; |
236 | 0 | // python DottedOIDToCode.py id-sha512 2.16.840.1.101.3.4.2.3 |
237 | 0 | static const uint8_t id_sha512[] = { |
238 | 0 | 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 |
239 | 0 | }; |
240 | 0 |
|
241 | 0 | // Matching is attempted based on a rough estimate of the commonality of the |
242 | 0 | // algorithm, to minimize the number of MatchRest calls. |
243 | 0 | if (algorithmID.MatchRest(id_sha1)) { |
244 | 0 | algorithm = DigestAlgorithm::sha1; |
245 | 0 | } else if (algorithmID.MatchRest(id_sha256)) { |
246 | 0 | algorithm = DigestAlgorithm::sha256; |
247 | 0 | } else if (algorithmID.MatchRest(id_sha384)) { |
248 | 0 | algorithm = DigestAlgorithm::sha384; |
249 | 0 | } else if (algorithmID.MatchRest(id_sha512)) { |
250 | 0 | algorithm = DigestAlgorithm::sha512; |
251 | 0 | } else { |
252 | 0 | return Result::ERROR_INVALID_ALGORITHM; |
253 | 0 | } |
254 | 0 | |
255 | 0 | return Success; |
256 | 0 | }); |
257 | 0 | } |
258 | | |
259 | | Result |
260 | | SignedData(Reader& input, /*out*/ Reader& tbs, |
261 | | /*out*/ SignedDataWithSignature& signedData) |
262 | 0 | { |
263 | 0 | Reader::Mark mark(input.GetMark()); |
264 | 0 |
|
265 | 0 | Result rv; |
266 | 0 | rv = ExpectTagAndGetValue(input, SEQUENCE, tbs); |
267 | 0 | if (rv != Success) { |
268 | 0 | return rv; |
269 | 0 | } |
270 | 0 | |
271 | 0 | rv = input.GetInput(mark, signedData.data); |
272 | 0 | if (rv != Success) { |
273 | 0 | return rv; |
274 | 0 | } |
275 | 0 | |
276 | 0 | rv = ExpectTagAndGetValue(input, der::SEQUENCE, signedData.algorithm); |
277 | 0 | if (rv != Success) { |
278 | 0 | return rv; |
279 | 0 | } |
280 | 0 | |
281 | 0 | rv = BitStringWithNoUnusedBits(input, signedData.signature); |
282 | 0 | if (rv == Result::ERROR_BAD_DER) { |
283 | 0 | rv = Result::ERROR_BAD_SIGNATURE; |
284 | 0 | } |
285 | 0 | return rv; |
286 | 0 | } |
287 | | |
288 | | Result |
289 | | BitStringWithNoUnusedBits(Reader& input, /*out*/ Input& value) |
290 | 0 | { |
291 | 0 | Reader valueWithUnusedBits; |
292 | 0 | Result rv = ExpectTagAndGetValue(input, BIT_STRING, valueWithUnusedBits); |
293 | 0 | if (rv != Success) { |
294 | 0 | return rv; |
295 | 0 | } |
296 | 0 | |
297 | 0 | uint8_t unusedBitsAtEnd; |
298 | 0 | if (valueWithUnusedBits.Read(unusedBitsAtEnd) != Success) { |
299 | 0 | return Result::ERROR_BAD_DER; |
300 | 0 | } |
301 | 0 | // XXX: Really the constraint should be that unusedBitsAtEnd must be less |
302 | 0 | // than 7. But, we suspect there are no real-world values in OCSP responses |
303 | 0 | // or certificates with non-zero unused bits. It seems like NSS assumes this |
304 | 0 | // in various places, so we enforce it too in order to simplify this code. If |
305 | 0 | // we find compatibility issues, we'll know we're wrong and we'll have to |
306 | 0 | // figure out how to shift the bits around. |
307 | 0 | if (unusedBitsAtEnd != 0) { |
308 | 0 | return Result::ERROR_BAD_DER; |
309 | 0 | } |
310 | 0 | return valueWithUnusedBits.SkipToEnd(value); |
311 | 0 | } |
312 | | |
313 | | static inline Result |
314 | | ReadDigit(Reader& input, /*out*/ unsigned int& value) |
315 | 0 | { |
316 | 0 | uint8_t b; |
317 | 0 | if (input.Read(b) != Success) { |
318 | 0 | return Result::ERROR_INVALID_DER_TIME; |
319 | 0 | } |
320 | 0 | if (b < '0' || b > '9') { |
321 | 0 | return Result::ERROR_INVALID_DER_TIME; |
322 | 0 | } |
323 | 0 | value = static_cast<unsigned int>(b - static_cast<uint8_t>('0')); |
324 | 0 | return Success; |
325 | 0 | } |
326 | | |
327 | | static inline Result |
328 | | ReadTwoDigits(Reader& input, unsigned int minValue, unsigned int maxValue, |
329 | | /*out*/ unsigned int& value) |
330 | 0 | { |
331 | 0 | unsigned int hi; |
332 | 0 | Result rv = ReadDigit(input, hi); |
333 | 0 | if (rv != Success) { |
334 | 0 | return rv; |
335 | 0 | } |
336 | 0 | unsigned int lo; |
337 | 0 | rv = ReadDigit(input, lo); |
338 | 0 | if (rv != Success) { |
339 | 0 | return rv; |
340 | 0 | } |
341 | 0 | value = (hi * 10) + lo; |
342 | 0 | if (value < minValue || value > maxValue) { |
343 | 0 | return Result::ERROR_INVALID_DER_TIME; |
344 | 0 | } |
345 | 0 | return Success; |
346 | 0 | } |
347 | | |
348 | | namespace internal { |
349 | | |
350 | | // We parse GeneralizedTime and UTCTime according to RFC 5280 and we do not |
351 | | // accept all time formats allowed in the ASN.1 spec. That is, |
352 | | // GeneralizedTime must always be in the format YYYYMMDDHHMMSSZ and UTCTime |
353 | | // must always be in the format YYMMDDHHMMSSZ. Timezone formats of the form |
354 | | // +HH:MM or -HH:MM or NOT accepted. |
355 | | Result |
356 | | TimeChoice(Reader& tagged, uint8_t expectedTag, /*out*/ Time& time) |
357 | 0 | { |
358 | 0 | unsigned int days; |
359 | 0 |
|
360 | 0 | Reader input; |
361 | 0 | Result rv = ExpectTagAndGetValue(tagged, expectedTag, input); |
362 | 0 | if (rv != Success) { |
363 | 0 | return rv; |
364 | 0 | } |
365 | 0 | |
366 | 0 | unsigned int yearHi; |
367 | 0 | unsigned int yearLo; |
368 | 0 | if (expectedTag == GENERALIZED_TIME) { |
369 | 0 | rv = ReadTwoDigits(input, 0, 99, yearHi); |
370 | 0 | if (rv != Success) { |
371 | 0 | return rv; |
372 | 0 | } |
373 | 0 | rv = ReadTwoDigits(input, 0, 99, yearLo); |
374 | 0 | if (rv != Success) { |
375 | 0 | return rv; |
376 | 0 | } |
377 | 0 | } else if (expectedTag == UTCTime) { |
378 | 0 | rv = ReadTwoDigits(input, 0, 99, yearLo); |
379 | 0 | if (rv != Success) { |
380 | 0 | return rv; |
381 | 0 | } |
382 | 0 | yearHi = yearLo >= 50u ? 19u : 20u; |
383 | 0 | } else { |
384 | 0 | return NotReached("invalid tag given to TimeChoice", |
385 | 0 | Result::ERROR_INVALID_DER_TIME); |
386 | 0 | } |
387 | 0 | unsigned int year = (yearHi * 100u) + yearLo; |
388 | 0 | if (year < 1970u) { |
389 | 0 | // We don't support dates before January 1, 1970 because that is the epoch. |
390 | 0 | return Result::ERROR_INVALID_DER_TIME; |
391 | 0 | } |
392 | 0 | days = DaysBeforeYear(year); |
393 | 0 |
|
394 | 0 | unsigned int month; |
395 | 0 | rv = ReadTwoDigits(input, 1u, 12u, month); |
396 | 0 | if (rv != Success) { |
397 | 0 | return rv; |
398 | 0 | } |
399 | 0 | unsigned int daysInMonth; |
400 | 0 | static const unsigned int jan = 31u; |
401 | 0 | const unsigned int feb = ((year % 4u == 0u) && |
402 | 0 | ((year % 100u != 0u) || (year % 400u == 0u))) |
403 | 0 | ? 29u |
404 | 0 | : 28u; |
405 | 0 | static const unsigned int mar = 31u; |
406 | 0 | static const unsigned int apr = 30u; |
407 | 0 | static const unsigned int may = 31u; |
408 | 0 | static const unsigned int jun = 30u; |
409 | 0 | static const unsigned int jul = 31u; |
410 | 0 | static const unsigned int aug = 31u; |
411 | 0 | static const unsigned int sep = 30u; |
412 | 0 | static const unsigned int oct = 31u; |
413 | 0 | static const unsigned int nov = 30u; |
414 | 0 | static const unsigned int dec = 31u; |
415 | 0 | switch (month) { |
416 | 0 | case 1: daysInMonth = jan; break; |
417 | 0 | case 2: daysInMonth = feb; days += jan; break; |
418 | 0 | case 3: daysInMonth = mar; days += jan + feb; break; |
419 | 0 | case 4: daysInMonth = apr; days += jan + feb + mar; break; |
420 | 0 | case 5: daysInMonth = may; days += jan + feb + mar + apr; break; |
421 | 0 | case 6: daysInMonth = jun; days += jan + feb + mar + apr + may; break; |
422 | 0 | case 7: daysInMonth = jul; days += jan + feb + mar + apr + may + jun; |
423 | 0 | break; |
424 | 0 | case 8: daysInMonth = aug; days += jan + feb + mar + apr + may + jun + |
425 | 0 | jul; |
426 | 0 | break; |
427 | 0 | case 9: daysInMonth = sep; days += jan + feb + mar + apr + may + jun + |
428 | 0 | jul + aug; |
429 | 0 | break; |
430 | 0 | case 10: daysInMonth = oct; days += jan + feb + mar + apr + may + jun + |
431 | 0 | jul + aug + sep; |
432 | 0 | break; |
433 | 0 | case 11: daysInMonth = nov; days += jan + feb + mar + apr + may + jun + |
434 | 0 | jul + aug + sep + oct; |
435 | 0 | break; |
436 | 0 | case 12: daysInMonth = dec; days += jan + feb + mar + apr + may + jun + |
437 | 0 | jul + aug + sep + oct + nov; |
438 | 0 | break; |
439 | 0 | default: |
440 | 0 | return NotReached("month already bounds-checked by ReadTwoDigits", |
441 | 0 | Result::FATAL_ERROR_INVALID_STATE); |
442 | 0 | } |
443 | 0 | |
444 | 0 | unsigned int dayOfMonth; |
445 | 0 | rv = ReadTwoDigits(input, 1u, daysInMonth, dayOfMonth); |
446 | 0 | if (rv != Success) { |
447 | 0 | return rv; |
448 | 0 | } |
449 | 0 | days += dayOfMonth - 1; |
450 | 0 |
|
451 | 0 | unsigned int hours; |
452 | 0 | rv = ReadTwoDigits(input, 0u, 23u, hours); |
453 | 0 | if (rv != Success) { |
454 | 0 | return rv; |
455 | 0 | } |
456 | 0 | unsigned int minutes; |
457 | 0 | rv = ReadTwoDigits(input, 0u, 59u, minutes); |
458 | 0 | if (rv != Success) { |
459 | 0 | return rv; |
460 | 0 | } |
461 | 0 | unsigned int seconds; |
462 | 0 | rv = ReadTwoDigits(input, 0u, 59u, seconds); |
463 | 0 | if (rv != Success) { |
464 | 0 | return rv; |
465 | 0 | } |
466 | 0 | |
467 | 0 | uint8_t b; |
468 | 0 | if (input.Read(b) != Success) { |
469 | 0 | return Result::ERROR_INVALID_DER_TIME; |
470 | 0 | } |
471 | 0 | if (b != 'Z') { |
472 | 0 | return Result::ERROR_INVALID_DER_TIME; |
473 | 0 | } |
474 | 0 | if (End(input) != Success) { |
475 | 0 | return Result::ERROR_INVALID_DER_TIME; |
476 | 0 | } |
477 | 0 | |
478 | 0 | uint64_t totalSeconds = (static_cast<uint64_t>(days) * 24u * 60u * 60u) + |
479 | 0 | (static_cast<uint64_t>(hours) * 60u * 60u) + |
480 | 0 | (static_cast<uint64_t>(minutes) * 60u) + |
481 | 0 | seconds; |
482 | 0 |
|
483 | 0 | time = TimeFromElapsedSecondsAD(totalSeconds); |
484 | 0 | return Success; |
485 | 0 | } |
486 | | |
487 | | Result |
488 | | IntegralBytes(Reader& input, uint8_t tag, |
489 | | IntegralValueRestriction valueRestriction, |
490 | | /*out*/ Input& value, |
491 | | /*optional out*/ Input::size_type* significantBytes) |
492 | 0 | { |
493 | 0 | Result rv = ExpectTagAndGetValue(input, tag, value); |
494 | 0 | if (rv != Success) { |
495 | 0 | return rv; |
496 | 0 | } |
497 | 0 | Reader reader(value); |
498 | 0 |
|
499 | 0 | // There must be at least one byte in the value. (Zero is encoded with a |
500 | 0 | // single 0x00 value byte.) |
501 | 0 | uint8_t firstByte; |
502 | 0 | rv = reader.Read(firstByte); |
503 | 0 | if (rv != Success) { |
504 | 0 | if (rv == Result::ERROR_BAD_DER) { |
505 | 0 | return Result::ERROR_INVALID_INTEGER_ENCODING; |
506 | 0 | } |
507 | 0 | |
508 | 0 | return rv; |
509 | 0 | } |
510 | 0 | |
511 | 0 | // If there is a byte after an initial 0x00/0xFF, then the initial byte |
512 | 0 | // indicates a positive/negative integer value with its high bit set/unset. |
513 | 0 | bool prefixed = !reader.AtEnd() && (firstByte == 0 || firstByte == 0xff); |
514 | 0 |
|
515 | 0 | if (prefixed) { |
516 | 0 | uint8_t nextByte; |
517 | 0 | if (reader.Read(nextByte) != Success) { |
518 | 0 | return NotReached("Read of one byte failed but not at end.", |
519 | 0 | Result::FATAL_ERROR_LIBRARY_FAILURE); |
520 | 0 | } |
521 | 0 | if ((firstByte & 0x80) == (nextByte & 0x80)) { |
522 | 0 | return Result::ERROR_INVALID_INTEGER_ENCODING; |
523 | 0 | } |
524 | 0 | } |
525 | 0 | |
526 | 0 | switch (valueRestriction) { |
527 | 0 | case IntegralValueRestriction::MustBe0To127: |
528 | 0 | if (value.GetLength() != 1 || (firstByte & 0x80) != 0) { |
529 | 0 | return Result::ERROR_INVALID_INTEGER_ENCODING; |
530 | 0 | } |
531 | 0 | break; |
532 | 0 |
|
533 | 0 | case IntegralValueRestriction::MustBePositive: |
534 | 0 | if ((value.GetLength() == 1 && firstByte == 0) || |
535 | 0 | (firstByte & 0x80) != 0) { |
536 | 0 | return Result::ERROR_INVALID_INTEGER_ENCODING; |
537 | 0 | } |
538 | 0 | break; |
539 | 0 |
|
540 | 0 | case IntegralValueRestriction::NoRestriction: |
541 | 0 | break; |
542 | 0 | } |
543 | 0 | |
544 | 0 | if (significantBytes) { |
545 | 0 | *significantBytes = value.GetLength(); |
546 | 0 | if (prefixed) { |
547 | 0 | assert(*significantBytes > 1); |
548 | 0 | --*significantBytes; |
549 | 0 | } |
550 | 0 |
|
551 | 0 | assert(*significantBytes > 0); |
552 | 0 | } |
553 | 0 |
|
554 | 0 | return Success; |
555 | 0 | } |
556 | | |
557 | | // This parser will only parse values between 0..127. If this range is |
558 | | // increased then callers will need to be changed. |
559 | | Result |
560 | | IntegralValue(Reader& input, uint8_t tag, /*out*/ uint8_t& value) |
561 | 0 | { |
562 | 0 | // Conveniently, all the Integers that we actually have to be able to parse |
563 | 0 | // are positive and very small. Consequently, this parser is *much* simpler |
564 | 0 | // than a general Integer parser would need to be. |
565 | 0 | Input valueBytes; |
566 | 0 | Result rv = IntegralBytes(input, tag, IntegralValueRestriction::MustBe0To127, |
567 | 0 | valueBytes, nullptr); |
568 | 0 | if (rv != Success) { |
569 | 0 | return rv; |
570 | 0 | } |
571 | 0 | Reader valueReader(valueBytes); |
572 | 0 | rv = valueReader.Read(value); |
573 | 0 | if (rv != Success) { |
574 | 0 | return NotReached("IntegralBytes already validated the value.", rv); |
575 | 0 | } |
576 | 0 | rv = End(valueReader); |
577 | 0 | assert(rv == Success); // guaranteed by IntegralBytes's range checks. |
578 | 0 | return rv; |
579 | 0 | } |
580 | | |
581 | | } // namespace internal |
582 | | |
583 | | Result |
584 | | OptionalVersion(Reader& input, /*out*/ Version& version) |
585 | 0 | { |
586 | 0 | static const uint8_t TAG = CONTEXT_SPECIFIC | CONSTRUCTED | 0; |
587 | 0 | if (!input.Peek(TAG)) { |
588 | 0 | version = Version::v1; |
589 | 0 | return Success; |
590 | 0 | } |
591 | 0 | return Nested(input, TAG, [&version](Reader& value) -> Result { |
592 | 0 | uint8_t integerValue; |
593 | 0 | Result rv = Integer(value, integerValue); |
594 | 0 | if (rv != Success) { |
595 | 0 | return rv; |
596 | 0 | } |
597 | 0 | // XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, |
598 | 0 | // but we do here for compatibility reasons. |
599 | 0 | switch (integerValue) { |
600 | 0 | case static_cast<uint8_t>(Version::v3): version = Version::v3; break; |
601 | 0 | case static_cast<uint8_t>(Version::v2): version = Version::v2; break; |
602 | 0 | case static_cast<uint8_t>(Version::v1): version = Version::v1; break; |
603 | 0 | case static_cast<uint8_t>(Version::v4): version = Version::v4; break; |
604 | 0 | default: |
605 | 0 | return Result::ERROR_BAD_DER; |
606 | 0 | } |
607 | 0 | return Success; |
608 | 0 | }); |
609 | 0 | } |
610 | | |
611 | | } } } // namespace mozilla::pkix::der |