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