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