/* * Copyright (c) 2020, Ali Mohammad Pur * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include namespace Crypto::ASN1 { enum class DecodeError { NoInput, NonConformingType, EndOfStream, NotEnoughData, EnteringNonConstructedTag, LeavingMainContext, InvalidInputFormat, Overflow, UnsupportedFormat, }; 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; }; template Result read() { 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(tag.class_, 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(); 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; }; } template<> struct AK::Formatter : Formatter { void format(FormatBuilder&, Crypto::ASN1::DecodeError); };