mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 14:52:43 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			210 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			210 lines
		
	
	
	
		
			6.2 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 2020, Ali Mohammad Pur <mpfard@serenityos.org>
 | |
|  *
 | |
|  * SPDX-License-Identifier: BSD-2-Clause
 | |
|  */
 | |
| 
 | |
| #pragma once
 | |
| 
 | |
| #include <AK/BitmapView.h>
 | |
| #include <AK/Result.h>
 | |
| #include <AK/Types.h>
 | |
| #include <LibCrypto/ASN1/ASN1.h>
 | |
| #include <LibCrypto/BigInt/UnsignedBigInteger.h>
 | |
| 
 | |
| 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<Tag, DecodeError> peek();
 | |
| 
 | |
|     bool eof() const;
 | |
| 
 | |
|     template<typename ValueType>
 | |
|     struct TaggedValue {
 | |
|         Tag tag;
 | |
|         ValueType value;
 | |
|     };
 | |
| 
 | |
|     Optional<DecodeError> 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<typename ValueType>
 | |
|     Result<ValueType, DecodeError> read(Optional<Class> class_override = {}, Optional<Kind> 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<ValueType>(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<DecodeError> enter();
 | |
|     Optional<DecodeError> leave();
 | |
| 
 | |
| private:
 | |
|     template<typename ValueType, typename DecodedType>
 | |
|     Result<ValueType, DecodeError> with_type_check(DecodedType&& value)
 | |
|     {
 | |
|         if constexpr (requires { ValueType { value }; })
 | |
|             return ValueType { value };
 | |
| 
 | |
|         return DecodeError::NonConformingType;
 | |
|     }
 | |
| 
 | |
|     template<typename ValueType, typename DecodedType>
 | |
|     Result<ValueType, DecodeError> with_type_check(Result<DecodedType, DecodeError>&& value_or_error)
 | |
|     {
 | |
|         if (value_or_error.is_error())
 | |
|             return value_or_error.error();
 | |
| 
 | |
|         if constexpr (IsSame<ValueType, bool> && !IsSame<DecodedType, bool>) {
 | |
|             return DecodeError::NonConformingType;
 | |
|         } else {
 | |
|             auto&& value = value_or_error.value();
 | |
|             if constexpr (requires { ValueType { value }; })
 | |
|                 return ValueType { value };
 | |
|         }
 | |
| 
 | |
|         return DecodeError::NonConformingType;
 | |
|     }
 | |
| 
 | |
|     template<typename ValueType>
 | |
|     Result<ValueType, DecodeError> 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<ValueType>(data);
 | |
| 
 | |
|         if (kind == Kind::Boolean)
 | |
|             return with_type_check<ValueType>(decode_boolean(data));
 | |
| 
 | |
|         if (kind == Kind::Integer)
 | |
|             return with_type_check<ValueType>(decode_arbitrary_sized_integer(data));
 | |
| 
 | |
|         if (kind == Kind::OctetString)
 | |
|             return with_type_check<ValueType>(decode_octet_string(data));
 | |
| 
 | |
|         if (kind == Kind::Null)
 | |
|             return with_type_check<ValueType>(decode_null(data));
 | |
| 
 | |
|         if (kind == Kind::ObjectIdentifier)
 | |
|             return with_type_check<ValueType>(decode_object_identifier(data));
 | |
| 
 | |
|         if (kind == Kind::PrintableString || kind == Kind::IA5String || kind == Kind::UTCTime)
 | |
|             return with_type_check<ValueType>(decode_printable_string(data));
 | |
| 
 | |
|         if (kind == Kind::Utf8String)
 | |
|             return with_type_check<ValueType>(StringView { data.data(), data.size() });
 | |
| 
 | |
|         if (kind == Kind::BitString)
 | |
|             return with_type_check<ValueType>(decode_bit_string(data));
 | |
| 
 | |
|         return with_type_check<ValueType>(data);
 | |
|     }
 | |
| 
 | |
|     Result<Tag, DecodeError> read_tag();
 | |
|     Result<size_t, DecodeError> read_length();
 | |
|     Result<u8, DecodeError> read_byte();
 | |
|     Result<ReadonlyBytes, DecodeError> read_bytes(size_t length);
 | |
| 
 | |
|     static Result<bool, DecodeError> decode_boolean(ReadonlyBytes);
 | |
|     static Result<UnsignedBigInteger, DecodeError> decode_arbitrary_sized_integer(ReadonlyBytes);
 | |
|     static Result<StringView, DecodeError> decode_octet_string(ReadonlyBytes);
 | |
|     static Result<std::nullptr_t, DecodeError> decode_null(ReadonlyBytes);
 | |
|     static Result<Vector<int>, DecodeError> decode_object_identifier(ReadonlyBytes);
 | |
|     static Result<StringView, DecodeError> decode_printable_string(ReadonlyBytes);
 | |
|     static Result<const BitmapView, DecodeError> decode_bit_string(ReadonlyBytes);
 | |
| 
 | |
|     Vector<ReadonlyBytes> m_stack;
 | |
|     Optional<Tag> m_current_tag;
 | |
| };
 | |
| 
 | |
| void pretty_print(Decoder&, OutputStream&, int indent = 0);
 | |
| 
 | |
| }
 | |
| 
 | |
| template<>
 | |
| struct AK::Formatter<Crypto::ASN1::DecodeError> : Formatter<StringView> {
 | |
|     void format(FormatBuilder&, Crypto::ASN1::DecodeError);
 | |
| };
 | 
