|
@@ -26,449 +26,165 @@
|
|
|
|
|
|
#pragma once
|
|
#pragma once
|
|
|
|
|
|
|
|
+#include <AK/Bitmap.h>
|
|
|
|
+#include <AK/Result.h>
|
|
#include <AK/Types.h>
|
|
#include <AK/Types.h>
|
|
#include <LibCrypto/ASN1/ASN1.h>
|
|
#include <LibCrypto/ASN1/ASN1.h>
|
|
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
|
#include <LibCrypto/BigInt/UnsignedBigInteger.h>
|
|
|
|
|
|
-namespace Crypto {
|
|
|
|
-
|
|
|
|
-static bool der_decode_integer(const u8* in, size_t length, UnsignedBigInteger& number)
|
|
|
|
-{
|
|
|
|
- if (length < 3) {
|
|
|
|
- dbgln("invalid header size");
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+namespace Crypto::ASN1 {
|
|
|
|
+
|
|
|
|
+enum class DecodeError {
|
|
|
|
+ NoInput,
|
|
|
|
+ NonConformingType,
|
|
|
|
+ EndOfStream,
|
|
|
|
+ NotEnoughData,
|
|
|
|
+ EnteringNonConstructedTag,
|
|
|
|
+ LeavingMainContext,
|
|
|
|
+ InvalidInputFormat,
|
|
|
|
+ Overflow,
|
|
|
|
+ UnsupportedFormat,
|
|
|
|
+};
|
|
|
|
|
|
- size_t x { 0 };
|
|
|
|
- // must start with 0x02
|
|
|
|
- if ((in[x++] & 0x1f) != 0x02) {
|
|
|
|
- dbgln("not an integer {} ({} follows)", in[x - 1], in[x]);
|
|
|
|
- return false;
|
|
|
|
|
|
+class Decoder {
|
|
|
|
+public:
|
|
|
|
+ Decoder(ReadonlyBytes data)
|
|
|
|
+ {
|
|
|
|
+ m_stack.append(data);
|
|
}
|
|
}
|
|
|
|
|
|
- // decode length
|
|
|
|
- size_t z = in[x++];
|
|
|
|
- if ((x & 0x80) == 0) {
|
|
|
|
- // overflow
|
|
|
|
- if (x + z > length) {
|
|
|
|
- dbgln("would overflow {} > {}", z + x, length);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- number = UnsignedBigInteger::import_data(in + x, z);
|
|
|
|
- return true;
|
|
|
|
- } else {
|
|
|
|
- // actual big integer
|
|
|
|
- z &= 0x7f;
|
|
|
|
|
|
+ // Read a tag without consuming it (and its data).
|
|
|
|
+ Result<Tag, DecodeError> peek();
|
|
|
|
|
|
- // overflow
|
|
|
|
- if ((x + z) > length || z > 4 || z == 0) {
|
|
|
|
- dbgln("would overflow {} > {}", z + x, length);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ bool eof() const;
|
|
|
|
|
|
- size_t y = 0;
|
|
|
|
- while (z--) {
|
|
|
|
- y = ((size_t)(in[x++])) | (y << 8);
|
|
|
|
- }
|
|
|
|
|
|
+ template<typename ValueType>
|
|
|
|
+ struct TaggedValue {
|
|
|
|
+ Tag tag;
|
|
|
|
+ ValueType value;
|
|
|
|
+ };
|
|
|
|
|
|
- // overflow
|
|
|
|
- if (x + y > length) {
|
|
|
|
- dbgln("would overflow {} > {}", y + x, length);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ template<typename ValueType>
|
|
|
|
+ Result<ValueType, DecodeError> read()
|
|
|
|
+ {
|
|
|
|
+ if (m_stack.is_empty())
|
|
|
|
+ return DecodeError::NoInput;
|
|
|
|
|
|
- number = UnsignedBigInteger::import_data(in + x, y);
|
|
|
|
- return true;
|
|
|
|
- }
|
|
|
|
|
|
+ if (eof())
|
|
|
|
+ return DecodeError::EndOfStream;
|
|
|
|
|
|
- // see if it's negative
|
|
|
|
- if (in[x] & 0x80) {
|
|
|
|
- dbgln("negative bigint unsupported in der_decode_integer");
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ auto previous_position = m_stack;
|
|
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
-static bool der_length_integer(UnsignedBigInteger* num, size_t* out_length)
|
|
|
|
-{
|
|
|
|
- auto& bigint = *num;
|
|
|
|
- size_t value_length = bigint.trimmed_length() * sizeof(u32);
|
|
|
|
- auto length = value_length;
|
|
|
|
- if (length == 0) {
|
|
|
|
- ++length;
|
|
|
|
- } else {
|
|
|
|
- // the number comes with a 0 padding to make it positive in 2's comp
|
|
|
|
- // add that zero if the msb is 1, but only if we haven't padded it
|
|
|
|
- // ourselves
|
|
|
|
- auto ms2b = (u16)(bigint.words()[bigint.trimmed_length() - 1] >> 16);
|
|
|
|
-
|
|
|
|
- if ((ms2b & 0xff00) == 0) {
|
|
|
|
- if (!(((u8)ms2b) & 0x80))
|
|
|
|
- --length;
|
|
|
|
- } else if (ms2b & 0x8000) {
|
|
|
|
- ++length;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (value_length < 128) {
|
|
|
|
- ++length;
|
|
|
|
- } else {
|
|
|
|
- ++length;
|
|
|
|
- while (value_length) {
|
|
|
|
- ++length;
|
|
|
|
- value_length >>= 8;
|
|
|
|
|
|
+ auto tag_or_error = peek();
|
|
|
|
+ if (tag_or_error.is_error()) {
|
|
|
|
+ m_stack = move(previous_position);
|
|
|
|
+ return tag_or_error.error();
|
|
}
|
|
}
|
|
- }
|
|
|
|
- // kind
|
|
|
|
- ++length;
|
|
|
|
- *out_length = length;
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
-constexpr static bool der_decode_object_identifier(const u8* in, size_t in_length, u8* words, u8* out_length)
|
|
|
|
-{
|
|
|
|
- if (in_length < 3)
|
|
|
|
- return false; // invalid header
|
|
|
|
-
|
|
|
|
- if (*out_length < 2)
|
|
|
|
- return false; // need at least two words
|
|
|
|
-
|
|
|
|
- size_t x { 0 };
|
|
|
|
- if ((in[x++] & 0x1f) != 0x06) {
|
|
|
|
- return false; // invalid header value
|
|
|
|
- }
|
|
|
|
|
|
|
|
- size_t length { 0 };
|
|
|
|
- if (in[x] < 128) {
|
|
|
|
- length = in[x++];
|
|
|
|
- } else {
|
|
|
|
- if ((in[x] < 0x81) | (in[x] > 0x82))
|
|
|
|
- return false; // invalid header
|
|
|
|
|
|
+ auto length_or_error = read_length();
|
|
|
|
+ if (length_or_error.is_error()) {
|
|
|
|
+ m_stack = move(previous_position);
|
|
|
|
+ return length_or_error.error();
|
|
|
|
+ }
|
|
|
|
|
|
- size_t y = in[x++] & 0x7f;
|
|
|
|
- while (y--)
|
|
|
|
- length = (length << 8) | (size_t)in[x++];
|
|
|
|
- }
|
|
|
|
|
|
+ auto tag = tag_or_error.value();
|
|
|
|
+ auto length = length_or_error.value();
|
|
|
|
|
|
- if (length < 1 || length + x > in_length)
|
|
|
|
- return false; // invalid length or overflow
|
|
|
|
-
|
|
|
|
- size_t y { 0 }, t { 0 };
|
|
|
|
- while (length--) {
|
|
|
|
- t = (t << 7) | (in[x] & 0x7f);
|
|
|
|
- if (!(in[x++] & 0x80)) {
|
|
|
|
- if (y >= *out_length)
|
|
|
|
- return false; // overflow
|
|
|
|
-
|
|
|
|
- if (y == 0) {
|
|
|
|
- words[0] = t / 40;
|
|
|
|
- words[1] = t % 40;
|
|
|
|
- y = 2;
|
|
|
|
- } else {
|
|
|
|
- words[y++] = t;
|
|
|
|
- }
|
|
|
|
- t = 0;
|
|
|
|
|
|
+ auto value_or_error = read_value<ValueType>(tag.class_, tag.kind, length);
|
|
|
|
+ if (value_or_error.is_error()) {
|
|
|
|
+ m_stack = move(previous_position);
|
|
|
|
+ return value_or_error.error();
|
|
}
|
|
}
|
|
- }
|
|
|
|
- *out_length = y;
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
|
|
|
|
-static constexpr size_t der_object_identifier_bits(size_t x)
|
|
|
|
-{
|
|
|
|
- x &= 0xffffffff;
|
|
|
|
- size_t c { 0 };
|
|
|
|
- while (x) {
|
|
|
|
- ++c;
|
|
|
|
- x >>= 1;
|
|
|
|
- }
|
|
|
|
- return c;
|
|
|
|
-}
|
|
|
|
-
|
|
|
|
-constexpr static bool der_length_object_identifier(u8* words, size_t num_words, size_t* out_length)
|
|
|
|
-{
|
|
|
|
- if (num_words < 2)
|
|
|
|
- return false;
|
|
|
|
-
|
|
|
|
- if (words[0] > 3 || (words[0] < 2 && words[1] > 39))
|
|
|
|
- return false;
|
|
|
|
-
|
|
|
|
- size_t z { 0 };
|
|
|
|
- size_t wordbuf = words[0] * 40 + words[1];
|
|
|
|
- for (size_t y = 0; y < num_words; ++y) {
|
|
|
|
- auto t = der_object_identifier_bits(wordbuf);
|
|
|
|
- z = t / 7 + (!!(t % 7)) + (!!(wordbuf == 0));
|
|
|
|
- if (y < num_words - 1)
|
|
|
|
- wordbuf = words[y + 1];
|
|
|
|
- }
|
|
|
|
|
|
+ m_current_tag.clear();
|
|
|
|
|
|
- if (z < 128) {
|
|
|
|
- z += 2;
|
|
|
|
- } else if (z < 256) {
|
|
|
|
- z += 3;
|
|
|
|
- } else {
|
|
|
|
- z += 4;
|
|
|
|
|
|
+ return value_or_error.release_value();
|
|
}
|
|
}
|
|
- *out_length = z;
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
|
|
|
|
-constexpr static bool der_length_sequence(ASN1::List* list, size_t in_length, size_t* out_length)
|
|
|
|
-{
|
|
|
|
- size_t y { 0 }, x { 0 };
|
|
|
|
- for (size_t i = 0; i < in_length; ++i) {
|
|
|
|
- auto type = list[i].kind;
|
|
|
|
- auto size = list[i].size;
|
|
|
|
- auto data = list[i].data;
|
|
|
|
-
|
|
|
|
- if (type == ASN1::Kind::Eol)
|
|
|
|
- break;
|
|
|
|
-
|
|
|
|
- switch (type) {
|
|
|
|
- case ASN1::Kind::Integer:
|
|
|
|
- if (!der_length_integer((UnsignedBigInteger*)data, &x)) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- y += x;
|
|
|
|
- break;
|
|
|
|
- case ASN1::Kind::ObjectIdentifier:
|
|
|
|
- if (!der_length_object_identifier((u8*)data, size, &x)) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- y += x;
|
|
|
|
- break;
|
|
|
|
- case ASN1::Kind::Sequence:
|
|
|
|
- if (!der_length_sequence((ASN1::List*)data, size, &x)) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- y += x;
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- dbgln("Unhandled Kind {}", ASN1::kind_name(type));
|
|
|
|
- ASSERT_NOT_REACHED();
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ Optional<DecodeError> enter();
|
|
|
|
+ Optional<DecodeError> leave();
|
|
|
|
|
|
- if (y < 128) {
|
|
|
|
- y += 2;
|
|
|
|
- } else if (y < 256) {
|
|
|
|
- y += 3;
|
|
|
|
- } else if (y < 65536) {
|
|
|
|
- y += 4;
|
|
|
|
- } else if (y < 16777216ul) {
|
|
|
|
- y += 5;
|
|
|
|
- } else {
|
|
|
|
- dbgln("invalid length {}", y);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- *out_length = y;
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
|
|
+private:
|
|
|
|
+ template<typename ValueType, typename DecodedType>
|
|
|
|
+ Result<ValueType, DecodeError> with_type_check(DecodedType&& value)
|
|
|
|
+ {
|
|
|
|
+ if constexpr (requires { ValueType { value }; })
|
|
|
|
+ return ValueType { value };
|
|
|
|
|
|
-static inline bool der_decode_sequence(const u8* in, size_t in_length, ASN1::List* list, size_t out_length, bool ordered = true)
|
|
|
|
-{
|
|
|
|
- if (in_length < 2) {
|
|
|
|
- dbgln("header too small");
|
|
|
|
- return false; // invalid header
|
|
|
|
|
|
+ return DecodeError::NonConformingType;
|
|
}
|
|
}
|
|
- size_t x { 0 };
|
|
|
|
- if (in[x++] != 0x30) {
|
|
|
|
- dbgln("not a sequence: {}", in[x - 1]);
|
|
|
|
- return false; // not a sequence
|
|
|
|
- }
|
|
|
|
- size_t block_size { 0 };
|
|
|
|
- size_t y { 0 };
|
|
|
|
- if (in[x] < 128) {
|
|
|
|
- block_size = in[x++];
|
|
|
|
- } else if (in[x] & 0x80) {
|
|
|
|
- if ((in[x] < 0x81) || (in[x] > 0x83)) {
|
|
|
|
- dbgln("invalid length element {}", in[x]);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
|
|
- y = in[x++] & 0x7f;
|
|
|
|
|
|
+ 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 (x + y > in_length) {
|
|
|
|
- dbgln("would overflow {} > {}", x + y, in_length);
|
|
|
|
- return false; // overflow
|
|
|
|
- }
|
|
|
|
- block_size = 0;
|
|
|
|
- while (y--)
|
|
|
|
- block_size = (block_size << 8) | (size_t)in[x++];
|
|
|
|
- }
|
|
|
|
|
|
+ auto&& value = value_or_error.value();
|
|
|
|
+ if constexpr (requires { ValueType { value }; })
|
|
|
|
+ return ValueType { value };
|
|
|
|
|
|
- // overflow
|
|
|
|
- if (x + block_size > in_length) {
|
|
|
|
- dbgln("would overflow {} > {}", x + block_size, in_length);
|
|
|
|
- return false;
|
|
|
|
|
|
+ return DecodeError::NonConformingType;
|
|
}
|
|
}
|
|
|
|
|
|
- for (size_t i = 0; i < out_length; ++i)
|
|
|
|
- list[i].used = false;
|
|
|
|
|
|
+ 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();
|
|
|
|
|
|
- in_length = block_size;
|
|
|
|
- for (size_t i = 0; i < out_length; ++i) {
|
|
|
|
- size_t z = 0;
|
|
|
|
- auto kind = list[i].kind;
|
|
|
|
- auto size = list[i].size;
|
|
|
|
- auto data = list[i].data;
|
|
|
|
|
|
+ if (klass != Class::Universal)
|
|
|
|
+ return with_type_check<ValueType>(data);
|
|
|
|
|
|
- if (!ordered && list[i].used) {
|
|
|
|
- continue;
|
|
|
|
- }
|
|
|
|
|
|
+ if (kind == Kind::Boolean)
|
|
|
|
+ return with_type_check<ValueType>(decode_boolean(data));
|
|
|
|
|
|
- switch (kind) {
|
|
|
|
- case ASN1::Kind::Integer:
|
|
|
|
- z = in_length;
|
|
|
|
- if (!der_decode_integer(in + x, z, *(UnsignedBigInteger*)data)) {
|
|
|
|
- dbgln("could not decode an integer");
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- if (!der_length_integer((UnsignedBigInteger*)data, &z)) {
|
|
|
|
- dbgln("could not figure out the length");
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- case ASN1::Kind::ObjectIdentifier:
|
|
|
|
- z = in_length;
|
|
|
|
- if (!der_decode_object_identifier(in + x, z, (u8*)data, (u8*)&size)) {
|
|
|
|
- if (!ordered)
|
|
|
|
- continue;
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- list[i].size = size;
|
|
|
|
- if (!der_length_object_identifier((u8*)data, size, &z)) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- case ASN1::Kind::Sequence:
|
|
|
|
- if ((in[x] & 0x3f) != 0x30) {
|
|
|
|
- dbgln("Not a sequence: {}", (in[x] & 0x3f));
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- z = in_length;
|
|
|
|
- if (!der_decode_sequence(in + x, z, (ASN1::List*)data, size)) {
|
|
|
|
- if (!ordered)
|
|
|
|
- continue;
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- if (!der_length_sequence((ASN1::List*)data, size, &z)) {
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
- break;
|
|
|
|
- default:
|
|
|
|
- dbgln("Unhandled ASN1 kind {}", ASN1::kind_name(kind));
|
|
|
|
- ASSERT_NOT_REACHED();
|
|
|
|
- break;
|
|
|
|
- }
|
|
|
|
- x += z;
|
|
|
|
- in_length -= z;
|
|
|
|
- list[i].used = true;
|
|
|
|
- if (!ordered)
|
|
|
|
- i = -1;
|
|
|
|
- }
|
|
|
|
- for (size_t i = 0; i < out_length; ++i)
|
|
|
|
- if (!list[i].used) {
|
|
|
|
- dbgln("index {} was not read", i);
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
|
|
+ if (kind == Kind::Integer)
|
|
|
|
+ return with_type_check<ValueType>(decode_arbitrary_sized_integer(data));
|
|
|
|
|
|
- return true;
|
|
|
|
-}
|
|
|
|
|
|
+ if (kind == Kind::OctetString)
|
|
|
|
+ return with_type_check<ValueType>(decode_octet_string(data));
|
|
|
|
|
|
-template<size_t element_count>
|
|
|
|
-struct der_decode_sequence_many_base {
|
|
|
|
- constexpr void set(size_t index, ASN1::Kind kind, size_t size, void* data)
|
|
|
|
- {
|
|
|
|
- ASN1::set(m_list[index], kind, data, size);
|
|
|
|
- }
|
|
|
|
|
|
+ if (kind == Kind::Null)
|
|
|
|
+ return with_type_check<ValueType>(decode_null(data));
|
|
|
|
|
|
- constexpr der_decode_sequence_many_base(const u8* in, size_t in_length)
|
|
|
|
- : m_in(in)
|
|
|
|
- , m_in_length(in_length)
|
|
|
|
- {
|
|
|
|
- }
|
|
|
|
|
|
+ if (kind == Kind::ObjectIdentifier)
|
|
|
|
+ return with_type_check<ValueType>(decode_object_identifier(data));
|
|
|
|
|
|
- ASN1::List* list() { return m_list; }
|
|
|
|
- const u8* in() { return m_in; }
|
|
|
|
- size_t in_length() { return m_in_length; }
|
|
|
|
|
|
+ if (kind == Kind::PrintableString || kind == Kind::IA5String || kind == Kind::UTCTime)
|
|
|
|
+ return with_type_check<ValueType>(decode_printable_string(data));
|
|
|
|
|
|
-protected:
|
|
|
|
- ASN1::List m_list[element_count];
|
|
|
|
- const u8* m_in;
|
|
|
|
- size_t m_in_length;
|
|
|
|
-};
|
|
|
|
|
|
+ if (kind == Kind::Utf8String)
|
|
|
|
+ return with_type_check<ValueType>(StringView { data.data(), data.size() });
|
|
|
|
|
|
-template<size_t element_count>
|
|
|
|
-struct der_decode_sequence_many : public der_decode_sequence_many_base<element_count> {
|
|
|
|
|
|
+ if (kind == Kind::BitString)
|
|
|
|
+ return with_type_check<ValueType>(decode_bit_string(data));
|
|
|
|
|
|
- template<typename ElementType, typename... Args>
|
|
|
|
- constexpr void construct(size_t index, ASN1::Kind kind, size_t size, ElementType data, Args... args)
|
|
|
|
- {
|
|
|
|
- der_decode_sequence_many_base<element_count>::set(index, kind, size, (void*)data);
|
|
|
|
- construct(index + 1, args...);
|
|
|
|
|
|
+ return with_type_check<ValueType>(data);
|
|
}
|
|
}
|
|
|
|
|
|
- constexpr void construct(size_t index)
|
|
|
|
- {
|
|
|
|
- ASSERT(index == element_count);
|
|
|
|
- }
|
|
|
|
|
|
+ Result<Tag, DecodeError> read_tag();
|
|
|
|
+ Result<size_t, DecodeError> read_length();
|
|
|
|
+ Result<u8, DecodeError> read_byte();
|
|
|
|
+ Result<ReadonlyBytes, DecodeError> read_bytes(size_t length);
|
|
|
|
|
|
- template<typename... Args>
|
|
|
|
- constexpr der_decode_sequence_many(const u8* in, size_t in_length, Args... args)
|
|
|
|
- : der_decode_sequence_many_base<element_count>(in, in_length)
|
|
|
|
- {
|
|
|
|
- construct(0, args...);
|
|
|
|
- }
|
|
|
|
|
|
+ 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<Bitmap, DecodeError> decode_bit_string(ReadonlyBytes);
|
|
|
|
|
|
- constexpr operator bool()
|
|
|
|
- {
|
|
|
|
- return der_decode_sequence(this->m_in, this->m_in_length, this->m_list, element_count);
|
|
|
|
- }
|
|
|
|
|
|
+ Vector<ReadonlyBytes> m_stack;
|
|
|
|
+ Optional<Tag> m_current_tag;
|
|
};
|
|
};
|
|
|
|
|
|
-// FIXME: Move these terrible constructs into their own place
|
|
|
|
-constexpr static void decode_b64_block(const u8 in[4], u8 out[3])
|
|
|
|
-{
|
|
|
|
- out[0] = (u8)(in[0] << 2 | in[1] >> 4);
|
|
|
|
- out[1] = (u8)(in[1] << 4 | in[2] >> 2);
|
|
|
|
- out[2] = (u8)(((in[2] << 6) & 0xc0) | in[3]);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
-constexpr static char base64_chars[] { "|$$$}rstuvwxyz{$$$$$$$>?@ABCDEFGHIJKLMNOPQRSTUVW$$$$$$XYZ[\\]^_`abcdefghijklmnopq" };
|
|
|
|
-constexpr static size_t decode_b64(const u8* in_buffer, size_t in_length, ByteBuffer& out_buffer)
|
|
|
|
-{
|
|
|
|
- u8 in[4] { 0 }, out[3] { 0 }, v { 0 };
|
|
|
|
- size_t i { 0 }, length { 0 };
|
|
|
|
- size_t output_offset { 0 };
|
|
|
|
-
|
|
|
|
- const u8* ptr = in_buffer;
|
|
|
|
-
|
|
|
|
- while (ptr <= in_buffer + in_length) {
|
|
|
|
- for (length = 0, i = 0; i < 4 && (ptr <= in_buffer + in_length); ++i) {
|
|
|
|
- v = 0;
|
|
|
|
- while ((ptr <= in_buffer + in_length) && !v) {
|
|
|
|
- v = ptr[0];
|
|
|
|
- ++ptr;
|
|
|
|
- v = (u8)((v < 43 || v > 122) ? 0 : base64_chars[v - 43]);
|
|
|
|
- if (v)
|
|
|
|
- v = (u8)(v == '$' ? 0 : v - 61);
|
|
|
|
- }
|
|
|
|
- if (ptr <= in_buffer + in_length) {
|
|
|
|
- ++length;
|
|
|
|
- if (v)
|
|
|
|
- in[i] = v - 1;
|
|
|
|
-
|
|
|
|
- } else {
|
|
|
|
- in[i] = 0;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (length) {
|
|
|
|
- decode_b64_block(in, out);
|
|
|
|
- out_buffer.overwrite(output_offset, out, length - 1);
|
|
|
|
- output_offset += length - 1;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return output_offset;
|
|
|
|
-}
|
|
|
|
-}
|
|
|
|
|
|
+template<>
|
|
|
|
+struct AK::Formatter<Crypto::ASN1::DecodeError> : Formatter<StringView> {
|
|
|
|
+ void format(FormatBuilder&, Crypto::ASN1::DecodeError);
|
|
|
|
+};
|