diff options
author | nharper <nharper@chromium.org> | 2015-04-22 16:13:50 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-22 23:13:50 +0000 |
commit | 00e51a363a532f1e2c0d2defcf9035e7bf33ddc0 (patch) | |
tree | 5aa3debb016cdef1a3b771e7b4469fe352888393 /net/der/parse_values.cc | |
parent | 2d6f0f7d524088b0ea2577433d8b46fa020369ec (diff) | |
download | chromium_src-00e51a363a532f1e2c0d2defcf9035e7bf33ddc0.zip chromium_src-00e51a363a532f1e2c0d2defcf9035e7bf33ddc0.tar.gz chromium_src-00e51a363a532f1e2c0d2defcf9035e7bf33ddc0.tar.bz2 |
Add DER parser to //net
The DER parsing code is split into 2 main sections - the Parser class for parsing constructed values, and the ParseType free functions for parsing primitive values. The Parser class should be complete at this point (with the exception of adding more convenience methods to match free functions). This CL has only a very limited number of free functions for parsing primitive values; more will be added in later CLs based on the demand needed for other types.
Review URL: https://codereview.chromium.org/878813002
Cr-Commit-Position: refs/heads/master@{#326389}
Diffstat (limited to 'net/der/parse_values.cc')
-rw-r--r-- | net/der/parse_values.cc | 269 |
1 files changed, 269 insertions, 0 deletions
diff --git a/net/der/parse_values.cc b/net/der/parse_values.cc new file mode 100644 index 0000000..bbf765f --- /dev/null +++ b/net/der/parse_values.cc @@ -0,0 +1,269 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "net/der/parse_values.h" + +namespace net { + +namespace der { + +namespace { + +bool ParseBoolInternal(const Input& in, bool* out, bool relaxed) { + // According to ITU-T X.690 section 8.2, a bool is encoded as a single octet + // where the octet of all zeroes is FALSE and a non-zero value for the octet + // is TRUE. + if (in.Length() != 1) + return false; + ByteReader data(in); + uint8_t byte; + if (!data.ReadByte(&byte)) + return false; + if (byte == 0) { + *out = false; + return true; + } + // ITU-T X.690 section 11.1 specifies that for DER, the TRUE value must be + // encoded as an octet of all ones. + if (byte == 0xff || relaxed) { + *out = true; + return true; + } + return false; +} + +// Reads a positive decimal number with |digits| digits and stores it in +// |*out|. This function does not check that the type of |*out| is large +// enough to hold 10^digits - 1; the caller must choose an appropriate type +// based on the number of digits they wish to parse. +template <typename UINT> +bool DecimalStringToUint(ByteReader& in, size_t digits, UINT* out) { + UINT value = 0; + for (size_t i = 0; i < digits; ++i) { + uint8_t digit; + if (!in.ReadByte(&digit)) { + return false; + } + if (digit < '0' || digit > '9') { + return false; + } + value = (value * 10) + (digit - '0'); + } + *out = value; + return true; +} + +// Checks that the values in a GeneralizedTime struct are valid. This involves +// checking that the year is 4 digits, the month is between 1 and 12, the day +// is a day that exists in that month (following current leap year rules), +// hours are between 0 and 23, minutes between 0 and 59, and seconds between +// 0 and 60 (to allow for leap seconds; no validation is done that a leap +// second is on a day that could be a leap second). +bool ValidateGeneralizedTime(const GeneralizedTime& time) { + CHECK(time.year > 0 && time.year < 9999); + if (time.month < 1 || time.month > 12) + return false; + if (time.day < 1) + return false; + if (time.hours < 0 || time.hours > 23) + return false; + if (time.minutes < 0 || time.minutes > 59) + return false; + // Leap seconds are allowed. + if (time.seconds < 0 || time.seconds > 60) + return false; + + // validate upper bound for day of month + switch (time.month) { + case 4: + case 6: + case 9: + case 11: + if (time.day > 30) + return false; + break; + case 1: + case 3: + case 5: + case 7: + case 8: + case 10: + case 12: + if (time.day > 31) + return false; + break; + case 2: + if (time.year % 4 == 0 && + (time.year % 100 != 0 || time.year % 400 == 0)) { + if (time.day > 29) + return false; + } else { + if (time.day > 28) + return false; + } + break; + default: + NOTREACHED(); + return false; + } + return true; +} + +} // namespace + +bool ParseBool(const Input& in, bool* out) { + return ParseBoolInternal(in, out, false /* relaxed */); +} + +// BER interprets any non-zero value as true, while DER requires a bool to +// have either all bits zero (false) or all bits one (true). To support +// malformed certs, we recognized the BER encoding instead of failing to +// parse. +bool ParseBoolRelaxed(const Input& in, bool* out) { + return ParseBoolInternal(in, out, true /* relaxed */); +} + +bool ParseUint64(const Input& in, uint64_t* out) { + ByteReader reader(in); + size_t bytes_read = 0; + uint8_t data; + uint64_t value = 0; + // Note that for simplicity, this check only admits integers up to 2^63-1. + if (in.Length() > sizeof(uint64_t) || in.Length() == 0) + return false; + while (reader.ReadByte(&data)) { + if (bytes_read == 0 && (data & 0x80)) { + return false; + } + value <<= 8; + value |= data; + bytes_read++; + } + // ITU-T X.690 section 8.3.2 specifies that an integer value must be encoded + // in the smallest number of octets. If the encoding consists of more than + // one octet, then the bits of the first octet and the most significant bit + // of the second octet must not be all zeroes or all ones. + // Because this function only parses unsigned integers, there's no need to + // check for the all ones case. + if (bytes_read > 1) { + ByteReader first_bytes_reader(in); + uint8_t first_byte; + uint8_t second_byte; + if (!first_bytes_reader.ReadByte(&first_byte) || + !first_bytes_reader.ReadByte(&second_byte)) { + return false; + } + if (first_byte == 0 && !(second_byte & 0x80)) { + return false; + } + } + *out = value; + return true; +} + +bool operator<(const GeneralizedTime& lhs, const GeneralizedTime& rhs) { + if (lhs.year != rhs.year) + return lhs.year < rhs.year; + if (lhs.month != rhs.month) + return lhs.month < rhs.month; + if (lhs.day != rhs.day) + return lhs.day < rhs.day; + if (lhs.hours != rhs.hours) + return lhs.hours < rhs.hours; + if (lhs.minutes != rhs.minutes) + return lhs.minutes < rhs.minutes; + return lhs.seconds < rhs.seconds; +} + +// A UTC Time in DER encoding should be YYMMDDHHMMSSZ, but some CAs encode +// the time following BER rules, which allows for YYMMDDHHMMZ. If the length +// is 11, assume it's YYMMDDHHMMZ, and in converting it to a GeneralizedTime, +// add in the seconds (set to 0). +bool ParseUTCTimeRelaxed(const Input& in, GeneralizedTime* value) { + ByteReader reader(in); + GeneralizedTime time; + if (!DecimalStringToUint(reader, 2, &time.year) || + !DecimalStringToUint(reader, 2, &time.month) || + !DecimalStringToUint(reader, 2, &time.day) || + !DecimalStringToUint(reader, 2, &time.hours) || + !DecimalStringToUint(reader, 2, &time.minutes)) { + return false; + } + + // Try to read the 'Z' at the end. If we read something else, then for it to + // be valid the next bytes should be seconds (and then followed by 'Z'). + uint8_t zulu; + ByteReader zulu_reader = reader; + if (!zulu_reader.ReadByte(&zulu)) + return false; + if (zulu == 'Z' && !zulu_reader.HasMore()) { + time.seconds = 0; + *value = time; + return true; + } + if (!DecimalStringToUint(reader, 2, &time.seconds)) + return false; + if (!reader.ReadByte(&zulu) || zulu != 'Z' || reader.HasMore()) + return false; + if (!ValidateGeneralizedTime(time)) + return false; + if (time.year < 50) { + time.year += 2000; + } else { + time.year += 1900; + } + *value = time; + return true; +} + +bool ParseUTCTime(const Input& in, GeneralizedTime* value) { + ByteReader reader(in); + GeneralizedTime time; + if (!DecimalStringToUint(reader, 2, &time.year) || + !DecimalStringToUint(reader, 2, &time.month) || + !DecimalStringToUint(reader, 2, &time.day) || + !DecimalStringToUint(reader, 2, &time.hours) || + !DecimalStringToUint(reader, 2, &time.minutes) || + !DecimalStringToUint(reader, 2, &time.seconds)) { + return false; + } + uint8_t zulu; + if (!reader.ReadByte(&zulu) || zulu != 'Z' || reader.HasMore()) + return false; + if (!ValidateGeneralizedTime(time)) + return false; + if (time.year < 50) { + time.year += 2000; + } else { + time.year += 1900; + } + *value = time; + return true; +} + +bool ParseGeneralizedTime(const Input& in, GeneralizedTime* value) { + ByteReader reader(in); + GeneralizedTime time; + if (!DecimalStringToUint(reader, 4, &time.year) || + !DecimalStringToUint(reader, 2, &time.month) || + !DecimalStringToUint(reader, 2, &time.day) || + !DecimalStringToUint(reader, 2, &time.hours) || + !DecimalStringToUint(reader, 2, &time.minutes) || + !DecimalStringToUint(reader, 2, &time.seconds)) { + return false; + } + uint8_t zulu; + if (!reader.ReadByte(&zulu) || zulu != 'Z' || reader.HasMore()) + return false; + if (!ValidateGeneralizedTime(time)) + return false; + *value = time; + return true; +} + +} // namespace der + +} // namespace net |