// 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" #include "net/der/parser.h" namespace net { namespace der { Parser::Parser() : input_(Input()), advance_mark_(Mark::NullMark()) { } Parser::Parser(const Input& input) : input_(input), advance_mark_(Mark::NullMark()) { } bool Parser::PeekTagAndValue(Tag* tag, Input* out) { ByteReader reader = input_; // Don't support tags > 30. uint8_t tag_byte; if (!reader.ReadByte(&tag_byte)) return false; // ITU-T X.690 section 8.1.2.3 specifies the format for identifiers with a // tag number no greater than 30. This parser only supports tag numbers up // to 30. // If the tag number is 31 (0x1F, the largest value that fits in the allotted // bytes), then the tag is more than one byte long and the continuation bytes // contain the real tag number. We only support tag numbers < 31 (and thus // single-byte tags). if ((tag_byte & kTagNumberMask) == 31) return false; // Parse length. The format for the length encoding is specified in // ITU-T X.690 section 8.1.3. size_t value_len = 0; // Number of bytes used to encode just the value. uint8_t length_first_byte; if (!reader.ReadByte(&length_first_byte)) return false; if ((length_first_byte & 0x80) == 0) { // Short form for length - it's only one byte long. value_len = length_first_byte & 0x7f; } else { // Long form for length - it's encoded across multiple bytes. if (length_first_byte == 0xff) { // ITU-T X.690 clause 8.1.3.5.c specifies the value 0xff shall not be // used. return false; } // The high bit indicated that this is the long form, while the next 7 bits // encode the number of subsequent octets used to encode the length // (ITU-T X.690 clause 8.1.3.5.b). size_t length_len = length_first_byte & 0x7f; if (length_len == 0) { // ITU-T X.690 section 10.1 (DER length forms) requires encoding the // length with the minimum number of octets. Besides, it makes no sense // for the length to be encoded in 0 octets. return false; } if (length_len > sizeof(value_len)) { // The length is encoded in multiple octets, with the first octet // indicating how many octets follow. Those octets need to be combined // to form a size_t, so the number of octets to follow (length_len) // must be small enough so that they fit in a size_t. return false; } uint8_t length_byte; for (size_t i = 0; i < length_len; i++) { if (!reader.ReadByte(&length_byte)) return false; // A first length byte of all zeroes means the length was not encoded in // minimum length. if (i == 0 && length_byte == 0) return false; value_len <<= 8; value_len += length_byte; } if (value_len < 0x80) { // If value_len is < 0x80, then it could have been encoded in a single // byte, meaning it was not encoded in minimum length. return false; } } if (!reader.ReadBytes(value_len, out)) return false; advance_mark_ = reader.NewMark(); *tag = tag_byte; return true; } bool Parser::Advance() { if (advance_mark_.IsEmpty()) return false; if (!input_.AdvanceToMark(advance_mark_)) return false; advance_mark_ = Mark::NullMark(); return true; } bool Parser::HasMore() { return input_.HasMore(); } bool Parser::ReadRawTLV(Input* out) { Tag tag; Input value; if (!PeekTagAndValue(&tag, &value)) return false; if (!input_.ReadToMark(advance_mark_, out)) return false; advance_mark_ = Mark::NullMark(); return true; } bool Parser::ReadTagAndValue(Tag* tag, Input* out) { if (!PeekTagAndValue(tag, out)) return false; CHECK(Advance()); return true; } bool Parser::ReadOptionalTag(Tag tag, Input* out, bool* present) { if (!HasMore()) { *present = false; return true; } Tag read_tag; Input value; if (!PeekTagAndValue(&read_tag, &value)) return false; *present = false; if (read_tag == tag) { *present = true; *out = value; CHECK(Advance()); } else { advance_mark_ = Mark::NullMark(); } return true; } bool Parser::SkipOptionalTag(Tag tag, bool* present) { Input out; return ReadOptionalTag(tag, &out, present); } bool Parser::ReadTag(Tag tag, Input* out) { bool present; return ReadOptionalTag(tag, out, &present) && present; } bool Parser::SkipTag(Tag tag) { Input out; return ReadTag(tag, &out); } // Type-specific variants of ReadTag bool Parser::ReadConstructed(Tag tag, Parser* out) { if (!IsConstructed(tag)) return false; Input data; if (!ReadTag(tag, &data)) return false; *out = Parser(data); return true; } bool Parser::ReadSequence(Parser* out) { return ReadConstructed(kSequence, out); } bool Parser::ReadUint64(uint64_t* out) { Input encodedInt; if (!ReadTag(kInteger, &encodedInt)) return false; return ParseUint64(encodedInt, out); } } // namespace der } // namespace net