/* * Copyright (c) 2020, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include namespace Crypto::ASN1 { #define ERROR_WITH_SCOPE(error) \ do { \ s_error_string = TRY(String::formatted("{}: {}", current_scope, error)); \ return Error::from_string_view(s_error_string.bytes_as_string_view()); \ } while (0) #define ENTER_TYPED_SCOPE(tag_kind_name, scope) \ do { \ if (auto tag = decoder.peek(); tag.is_error() || tag.value().kind != Crypto::ASN1::Kind::tag_kind_name) { \ if (tag.is_error()) \ ERROR_WITH_SCOPE(TRY(String::formatted(scope " data was invalid: {}", tag.error()))); \ else \ ERROR_WITH_SCOPE(TRY(String::formatted(scope " data was not of kind " #tag_kind_name " was {}", Crypto::ASN1::kind_name(tag.value().kind)))); \ } \ ENTER_SCOPE(scope); \ } while (0) #define ENTER_SCOPE(scope) \ do { \ if (auto result = decoder.enter(); result.is_error()) { \ ERROR_WITH_SCOPE(TRY(String::formatted("Failed to enter scope: {}", scope))); \ } \ PUSH_SCOPE(scope) \ } while (0) #define PUSH_SCOPE(scope) current_scope.append(#scope##sv); #define EXIT_SCOPE() \ do { \ if (auto error = decoder.leave(); error.is_error()) { \ ERROR_WITH_SCOPE(TRY(String::formatted("Failed to exit scope: {}", error.error()))); \ } \ POP_SCOPE(); \ } while (0) #define POP_SCOPE() current_scope.remove(current_scope.size() - 1); #define READ_OBJECT(kind_name, type_name, value_name) \ auto value_name##_result = decoder.read(Crypto::ASN1::Class::Universal, Crypto::ASN1::Kind::kind_name); \ if (value_name##_result.is_error()) { \ ERROR_WITH_SCOPE(TRY(String::formatted("Read of kind " #kind_name " failed: {}", value_name##_result.error()))); \ } \ auto value_name = value_name##_result.release_value(); #define REWRITE_TAG(kind_name) \ auto value_name##_result = decoder.rewrite_tag(Crypto::ASN1::Kind::kind_name); \ if (value_name##_result.is_error()) { \ ERROR_WITH_SCOPE(TRY(String::formatted("Rewrite of kind " #kind_name " failed: {}", value_name##_result.error()))); \ } #define DROP_OBJECT() \ do { \ if (auto error = decoder.drop(); error.is_error()) { \ ERROR_WITH_SCOPE(TRY(String::formatted("Drop failed: {}", error.error()))); \ } \ } while (0) class BitStringView { public: BitStringView(ReadonlyBytes data, size_t unused_bits) : m_data(data) , m_unused_bits(unused_bits) { } ErrorOr raw_bytes() const { if (m_unused_bits != 0) return Error::from_string_literal("ASN1::Decoder: BitStringView contains unexpected partial bytes"); return m_data; } bool get(size_t index) const { if (index >= 8 * m_data.size() - m_unused_bits) return false; return 0 != (m_data[index / 8] & (1u << (7 - (index % 8)))); } size_t unused_bits() const { return m_unused_bits; } size_t byte_length() const { return m_data.size(); } ReadonlyBytes underlying_bytes() const { return m_data; } // FIXME: Improve me! I am naive! bool operator==(BitStringView const& other) const { for (size_t bit_index = 0; bit_index < 8 * m_data.size() - m_unused_bits; ++bit_index) { if (get(bit_index) != other.get(bit_index)) return false; } return true; } 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). ErrorOr peek(); bool eof() const; template struct TaggedValue { Tag tag; ValueType value; }; ErrorOr rewrite_tag(Kind kind) { if (m_stack.is_empty()) return Error::from_string_literal("Nothing on stack to rewrite"); if (eof()) return Error::from_string_literal("Stream is empty"); if (m_current_tag.has_value()) { m_current_tag->kind = kind; return {}; } auto tag = TRY(read_tag()); m_current_tag = tag; m_current_tag->kind = kind; return {}; } ErrorOr drop() { if (m_stack.is_empty()) return Error::from_string_literal("ASN1::Decoder: Trying to drop using an empty stack"); if (eof()) return Error::from_string_literal("ASN1::Decoder: Trying to drop using a decoder that is EOF"); 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.release_error(); } auto length_or_error = read_length(); if (length_or_error.is_error()) { m_stack = move(previous_position); return length_or_error.release_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.release_error(); } m_current_tag.clear(); return {}; } template ErrorOr read(Optional class_override = {}, Optional kind_override = {}) { if (m_stack.is_empty()) return Error::from_string_literal("ASN1::Decoder: Trying to read using an empty stack"); if (eof()) return Error::from_string_literal("ASN1::Decoder: Trying to read using a decoder that is EOF"); 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.release_error(); } auto length_or_error = read_length(); if (length_or_error.is_error()) { m_stack = move(previous_position); return length_or_error.release_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.release_error(); } m_current_tag.clear(); return value_or_error.release_value(); } ErrorOr enter(); ErrorOr leave(); ErrorOr peek_entry_bytes(); private: template ErrorOr with_type_check(DecodedType&& value) { if constexpr (requires { ValueType { value }; }) return ValueType { value }; return Error::from_string_literal("ASN1::Decoder: Trying to decode a value from an incompatible type"); } template ErrorOr with_type_check(ErrorOr&& value_or_error) { if (value_or_error.is_error()) return value_or_error.release_error(); if constexpr (IsSame && !IsSame) { return Error::from_string_literal("ASN1::Decoder: Trying to decode a boolean from a non-boolean type"); } else { auto&& value = value_or_error.value(); if constexpr (requires { ValueType { value }; }) return ValueType { value }; } return Error::from_string_literal("ASN1::Decoder: Trying to decode a value from an incompatible type"); } template ErrorOr read_value(Class klass, Kind kind, size_t length) { auto data = TRY(read_bytes(length)); if constexpr (IsSame) { return data; } else { 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); } } ErrorOr read_tag(); ErrorOr read_length(); ErrorOr read_byte(); ErrorOr read_bytes(size_t length); static ErrorOr decode_boolean(ReadonlyBytes); static ErrorOr decode_arbitrary_sized_integer(ReadonlyBytes); static ErrorOr decode_octet_string(ReadonlyBytes); static ErrorOr decode_null(ReadonlyBytes); static ErrorOr> decode_object_identifier(ReadonlyBytes); static ErrorOr decode_printable_string(ReadonlyBytes); static ErrorOr decode_bit_string(ReadonlyBytes); Vector m_stack; Optional m_current_tag; }; ErrorOr pretty_print(Decoder&, Stream&, int indent = 0); class Encoder { public: Encoder() { m_buffer_stack.empend(); } ReadonlyBytes active_bytes() const { return m_buffer_stack.last().bytes(); } ByteBuffer finish() { VERIFY(m_buffer_stack.size() == 1); return m_buffer_stack.take_last(); } template ErrorOr write(ValueType const& value, Optional class_override = {}, Optional kind_override = {}) { if constexpr (IsSame) { return write_boolean(value, class_override, kind_override); } else if constexpr (IsSame || (IsIntegral && IsUnsigned)) { return write_arbitrary_sized_integer(value, class_override, kind_override); } else if constexpr (IsOneOf) { return write_printable_string(value, class_override, kind_override); } else if constexpr (IsOneOf) { return write_octet_string(value, class_override, kind_override); } else if constexpr (IsSame) { return write_null(class_override, kind_override); } else if constexpr (IsOneOf, Span, Span>) { return write_object_identifier(value, class_override, kind_override); } else if constexpr (IsSame) { return write_bit_string(value, class_override, kind_override); } else { dbgln("Unsupported type: {}", __PRETTY_FUNCTION__); return Error::from_string_literal("ASN1::Encoder: Trying to encode a value of an unsupported type"); } } template ErrorOr write_constructed(Class class_, Kind kind, Fn&& fn) { return write_constructed(bit_cast(class_), bit_cast(kind), forward(fn)); } template ErrorOr write_constructed(u8 class_, u8 kind, Fn&& fn) { m_buffer_stack.empend(); using ResultType = decltype(fn()); if constexpr (IsSpecializationOf) { TRY(fn()); } else { fn(); } auto buffer = m_buffer_stack.take_last(); TRY(write_tag(bit_cast(class_), Type::Constructed, bit_cast(kind))); TRY(write_length(buffer.size())); TRY(write_bytes(buffer.bytes())); return {}; } private: ErrorOr write_tag(Class, Type, Kind); ErrorOr write_length(size_t); ErrorOr write_bytes(ReadonlyBytes); ErrorOr write_byte(u8); ErrorOr write_boolean(bool, Optional, Optional); ErrorOr write_arbitrary_sized_integer(UnsignedBigInteger const&, Optional, Optional); ErrorOr write_printable_string(StringView, Optional, Optional); ErrorOr write_octet_string(ReadonlyBytes, Optional, Optional); ErrorOr write_null(Optional, Optional); ErrorOr write_object_identifier(Span, Optional, Optional); ErrorOr write_bit_string(BitStringView, Optional, Optional); Vector m_buffer_stack; }; }