/* * Copyright (c) 2020, Ali Mohammad Pur * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace Crypto::ASN1 { 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; }; }