/* * Copyright (c) 2020, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace Crypto::ASN1 { enum class DecodeError { NoInput, NonConformingType, EndOfStream, NotEnoughData, EnteringNonConstructedTag, LeavingMainContext, InvalidInputFormat, Overflow, UnsupportedFormat, }; class BitStringView { public: BitStringView(ReadonlyBytes data, size_t unused_bits) : m_data(data) , m_unused_bits(unused_bits) { } ReadonlyBytes raw_bytes() const { VERIFY(m_unused_bits == 0); return m_data; } bool get(size_t index) { if (index >= 8 * m_data.size() - m_unused_bits) return false; return 0 != (m_data[index / 8] & (1u << (7 - (index % 8)))); } private: ReadonlyBytes m_data; size_t m_unused_bits; }; class Decoder { public: Decoder(ReadonlyBytes data) { m_stack.append(data); } // Read a tag without consuming it (and its data). Result peek(); bool eof() const; template struct TaggedValue { Tag tag; ValueType value; }; Optional drop() { if (m_stack.is_empty()) return DecodeError::NoInput; if (eof()) return DecodeError::EndOfStream; auto previous_position = m_stack; auto tag_or_error = peek(); if (tag_or_error.is_error()) { m_stack = move(previous_position); return tag_or_error.error(); } auto length_or_error = read_length(); if (length_or_error.is_error()) { m_stack = move(previous_position); return length_or_error.error(); } auto length = length_or_error.value(); auto bytes_result = read_bytes(length); if (bytes_result.is_error()) { m_stack = move(previous_position); return bytes_result.error(); } m_current_tag.clear(); return {}; } template Result read(Optional class_override = {}, Optional kind_override = {}) { if (m_stack.is_empty()) return DecodeError::NoInput; if (eof()) return DecodeError::EndOfStream; auto previous_position = m_stack; auto tag_or_error = peek(); if (tag_or_error.is_error()) { m_stack = move(previous_position); return tag_or_error.error(); } auto length_or_error = read_length(); if (length_or_error.is_error()) { m_stack = move(previous_position); return length_or_error.error(); } auto tag = tag_or_error.value(); auto length = length_or_error.value(); auto value_or_error = read_value(class_override.value_or(tag.class_), kind_override.value_or(tag.kind), length); if (value_or_error.is_error()) { m_stack = move(previous_position); return value_or_error.error(); } m_current_tag.clear(); return value_or_error.release_value(); } Optional enter(); Optional leave(); private: template Result with_type_check(DecodedType&& value) { if constexpr (requires { ValueType { value }; }) return ValueType { value }; return DecodeError::NonConformingType; } template Result with_type_check(Result&& value_or_error) { if (value_or_error.is_error()) return value_or_error.error(); if constexpr (IsSame && !IsSame) { return DecodeError::NonConformingType; } else { auto&& value = value_or_error.value(); if constexpr (requires { ValueType { value }; }) return ValueType { value }; } return DecodeError::NonConformingType; } template Result read_value(Class klass, Kind kind, size_t length) { auto data_or_error = read_bytes(length); if (data_or_error.is_error()) return data_or_error.error(); auto data = data_or_error.value(); if (klass != Class::Universal) return with_type_check(data); if (kind == Kind::Boolean) return with_type_check(decode_boolean(data)); if (kind == Kind::Integer) return with_type_check(decode_arbitrary_sized_integer(data)); if (kind == Kind::OctetString) return with_type_check(decode_octet_string(data)); if (kind == Kind::Null) return with_type_check(decode_null(data)); if (kind == Kind::ObjectIdentifier) return with_type_check(decode_object_identifier(data)); if (kind == Kind::PrintableString || kind == Kind::IA5String || kind == Kind::UTCTime) return with_type_check(decode_printable_string(data)); if (kind == Kind::Utf8String) return with_type_check(StringView { data.data(), data.size() }); if (kind == Kind::BitString) return with_type_check(decode_bit_string(data)); return with_type_check(data); } Result read_tag(); Result read_length(); Result read_byte(); Result read_bytes(size_t length); static Result decode_boolean(ReadonlyBytes); static Result decode_arbitrary_sized_integer(ReadonlyBytes); static Result decode_octet_string(ReadonlyBytes); static Result decode_null(ReadonlyBytes); static Result, DecodeError> decode_object_identifier(ReadonlyBytes); static Result decode_printable_string(ReadonlyBytes); static Result decode_bit_string(ReadonlyBytes); Vector m_stack; Optional m_current_tag; }; void pretty_print(Decoder&, OutputStream&, int indent = 0); } template<> struct AK::Formatter : Formatter { ErrorOr format(FormatBuilder&, Crypto::ASN1::DecodeError); };