blob: af541cc9c279f29770c18d4c6041ab4f11c550ef [file] [log] [blame]
// 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