#include "net/der/parse_values.h"
#include <tuple>
#include "base/check_op.h"
#include "base/notreached.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversion_utils.h"
#include "base/strings/utf_string_conversions.h"
#include "base/sys_byteorder.h"
#include "base/third_party/icu/icu_utf.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
namespace net::der {
namespace {
bool ParseBoolInternal(const Input& in, bool* out, bool relaxed) {
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;
}
if (byte == 0xff || relaxed) {
*out = true;
return true;
}
return false;
}
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;
}
bool ValidateGeneralizedTime(const GeneralizedTime& time) {
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;
if (time.seconds < 0 || time.seconds > 60)
return false;
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;
}
size_t GetUnsignedIntegerLength(const Input& in) {
der::ByteReader reader(in);
uint8_t first_byte;
if (!reader.ReadByte(&first_byte))
return 0;
if (first_byte == 0 && in.Length() > 1)
return in.Length() - 1;
return in.Length();
}
}
bool ParseBool(const Input& in, bool* out) {
return ParseBoolInternal(in, out, false );
}
bool ParseBoolRelaxed(const Input& in, bool* out) {
return ParseBoolInternal(in, out, true );
}
bool IsValidInteger(const Input& in, bool* negative) {
CBS cbs;
CBS_init(&cbs, in.UnsafeData(), in.Length());
int negative_int;
if (!CBS_is_valid_asn1_integer(&cbs, &negative_int)) {
return false;
}
*negative = !!negative_int;
return true;
}
bool ParseUint64(const Input& in, uint64_t* out) {
bool negative;
if (!IsValidInteger(in, &negative) || negative)
return false;
if (GetUnsignedIntegerLength(in) > sizeof(*out))
return false;
ByteReader reader(in);
uint8_t data;
uint64_t value = 0;
while (reader.ReadByte(&data)) {
value <<= 8;
value |= data;
}
*out = value;
return true;
}
bool ParseUint8(const Input& in, uint8_t* out) {
uint64_t value;
if (!ParseUint64(in, &value))
return false;
if (value > 0xFF)
return false;
*out = static_cast<uint8_t>(value);
return true;
}
BitString::BitString(const Input& bytes, uint8_t unused_bits)
: bytes_(bytes), unused_bits_(unused_bits) {
DCHECK_LT(unused_bits, 8);
DCHECK(unused_bits == 0 || bytes.Length() != 0);
DCHECK(bytes.Length() == 0 ||
(bytes.UnsafeData()[bytes.Length() - 1] & ((1u << unused_bits) - 1)) ==
0);
}
bool BitString::AssertsBit(size_t bit_index) const {
size_t byte_index = bit_index / 8;
if (byte_index >= bytes_.Length())
return false;
uint8_t bit_index_in_byte = 7 - (bit_index - byte_index * 8);
uint8_t byte = bytes_.UnsafeData()[byte_index];
return 0 != (byte & (1 << bit_index_in_byte));
}
absl::optional<BitString> ParseBitString(const Input& in) {
ByteReader reader(in);
uint8_t unused_bits;
if (!reader.ReadByte(&unused_bits))
return absl::nullopt;
if (unused_bits > 7)
return absl::nullopt;
Input bytes;
if (!reader.ReadBytes(reader.BytesLeft(), &bytes))
return absl::nullopt;
if (unused_bits > 0) {
if (bytes.Length() == 0)
return absl::nullopt;
uint8_t last_byte = bytes.UnsafeData()[bytes.Length() - 1];
uint8_t mask = 0xFF >> (8 - unused_bits);
if ((mask & last_byte) != 0)
return absl::nullopt;
}
return BitString(bytes, unused_bits);
}
bool GeneralizedTime::InUTCTimeRange() const {
return 1950 <= year && year < 2050;
}
bool operator<(const GeneralizedTime& lhs, const GeneralizedTime& rhs) {
return std::tie(lhs.year, lhs.month, lhs.day, lhs.hours, lhs.minutes,
lhs.seconds) < std::tie(rhs.year, rhs.month, rhs.day,
rhs.hours, rhs.minutes, rhs.seconds);
}
bool operator>(const GeneralizedTime& lhs, const GeneralizedTime& rhs) {
return rhs < lhs;
}
bool operator<=(const GeneralizedTime& lhs, const GeneralizedTime& rhs) {
return !(lhs > rhs);
}
bool operator>=(const GeneralizedTime& lhs, const GeneralizedTime& rhs) {
return !(lhs < rhs);
}
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 (time.year < 50) {
time.year += 2000;
} else {
time.year += 1900;
}
if (!ValidateGeneralizedTime(time))
return false;
*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;
}
bool ParseIA5String(Input in, std::string* out) {
for (char c : in.AsStringView()) {
if (static_cast<uint8_t>(c) > 127)
return false;
}
*out = in.AsString();
return true;
}
bool ParseVisibleString(Input in, std::string* out) {
for (char c : in.AsStringView()) {
if (static_cast<uint8_t>(c) < 32 || static_cast<uint8_t>(c) > 126)
return false;
}
*out = in.AsString();
return true;
}
bool ParsePrintableString(Input in, std::string* out) {
for (char c : in.AsStringView()) {
if (!(base::IsAsciiAlpha(c) || c == ' ' || (c >= '\'' && c <= ':') ||
c == '=' || c == '?')) {
return false;
}
}
*out = in.AsString();
return true;
}
bool ParseTeletexStringAsLatin1(Input in, std::string* out) {
out->clear();
size_t utf8_length = in.Length();
for (size_t i = 0; i < in.Length(); i++) {
if (in.UnsafeData()[i] > 0x7f)
utf8_length++;
}
out->reserve(utf8_length);
for (size_t i = 0; i < in.Length(); i++) {
uint8_t u = in.UnsafeData()[i];
if (u <= 0x7f) {
out->push_back(u);
} else {
out->push_back(0xc0 | (u >> 6));
out->push_back(0x80 | (u & 0x3f));
}
}
DCHECK_EQ(utf8_length, out->size());
return true;
}
bool ParseUniversalString(Input in, std::string* out) {
if (in.Length() % 4 != 0)
return false;
out->clear();
std::vector<uint32_t> in_32bit(in.Length() / 4);
if (in.Length())
memcpy(in_32bit.data(), in.UnsafeData(), in.Length());
for (const uint32_t c : in_32bit) {
auto codepoint = static_cast<base_icu::UChar32>(base::NetToHost32(c));
if (!CBU_IS_UNICODE_CHAR(codepoint))
return false;
base::WriteUnicodeCharacter(codepoint, out);
}
return true;
}
bool ParseBmpString(Input in, std::string* out) {
if (in.Length() % 2 != 0)
return false;
out->clear();
std::vector<uint16_t> in_16bit(in.Length() / 2);
if (in.Length())
memcpy(in_16bit.data(), in.UnsafeData(), in.Length());
for (const uint16_t c : in_16bit) {
base_icu::UChar32 codepoint = base::NetToHost16(c);
if (!CBU_IS_UNICODE_CHAR(codepoint))
return false;
base::WriteUnicodeCharacter(codepoint, out);
}
return true;
}
}