From 2559c80b098cec1bfcb30be1ca976f595aaf1fd6 Mon Sep 17 00:00:00 2001 From: Jack Lloyd Date: Fri, 8 Jun 2018 05:47:56 -0400 Subject: Reduce copying/allocations when BER decoding We are constrained in how far we can go because BER_Object must mandatorily copy its value (due to the public member variable exposting the bytes). But this reduces the number of allocations when parsing a sample X.509 certificate by about 15% --- src/lib/asn1/ber_dec.h | 164 +++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 132 insertions(+), 32 deletions(-) (limited to 'src/lib/asn1/ber_dec.h') diff --git a/src/lib/asn1/ber_dec.h b/src/lib/asn1/ber_dec.h index d454c45d6..2086eacd3 100644 --- a/src/lib/asn1/ber_dec.h +++ b/src/lib/asn1/ber_dec.h @@ -1,6 +1,6 @@ /* * BER Decoder -* (C) 1999-2010 Jack Lloyd +* (C) 1999-2010,2018 Jack Lloyd * * Botan is released under the Simplified BSD License (see license.txt) */ @@ -13,28 +13,103 @@ namespace Botan { +class BigInt; + /** * BER Decoding Object */ class BOTAN_PUBLIC_API(2,0) BER_Decoder final { public: + /** + * Set up to BER decode the data in buf of length len + */ + BER_Decoder(const uint8_t buf[], size_t len); + + /** + * Set up to BER decode the data in vec + */ + explicit BER_Decoder(const secure_vector& vec); + + /** + * Set up to BER decode the data in vec + */ + explicit BER_Decoder(const std::vector& vec); + + /** + * Set up to BER decode the data in src + */ + explicit BER_Decoder(DataSource& src); + + /** + * Set up to BER decode the data in obj + */ + BER_Decoder(const BER_Object& obj) : + BER_Decoder(obj.bits(), obj.length()) {} + + BER_Decoder(const BER_Decoder& other); + + BER_Decoder& operator=(const BER_Decoder&) = delete; + + /** + * Get the next object in the data stream. + * If EOF, returns an object with type NO_OBJECT. + */ BER_Object get_next_object(); - std::vector get_next_octet_string(); + BER_Decoder& get_next(BER_Object& ber) + { + ber = get_next_object(); + return (*this); + } + /** + * Push an object back onto the stream. Throws if another + * object was previously pushed and has not been subsequently + * read out. + */ void push_back(const BER_Object& obj); + /** + * Push an object back onto the stream. Throws if another + * object was previously pushed and has not been subsequently + * read out. + */ + void push_back(BER_Object&& obj); + + /** + * Return true if there is at least one more item remaining + */ bool more_items() const; + + /** + * Verify the stream is concluded, throws otherwise. + * Returns (*this) + */ BER_Decoder& verify_end(); + + /** + * Verify the stream is concluded, throws otherwise. + * Returns (*this) + */ BER_Decoder& verify_end(const std::string& err_msg); + /** + * Discard any data that remains unread + * Returns (*this) + */ BER_Decoder& discard_remaining(); - BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL); - BER_Decoder& end_cons(); + /** + * Start decoding a constructed data (sequence or set) + */ + BER_Decoder start_cons(ASN1_Tag type_tag, ASN1_Tag class_tag = UNIVERSAL); - BER_Decoder& get_next(BER_Object& ber); + /** + * Finish decoding a constructed data, throws if any data remains. + * Returns the parent of *this (ie the object on which start_cons was called). + */ + BER_Decoder& end_cons(); /** * Get next object and copy value to POD type @@ -82,9 +157,37 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final } BER_Decoder& decode_null(); - BER_Decoder& decode(bool& v); - BER_Decoder& decode(size_t& v); - BER_Decoder& decode(class BigInt& v); + + /** + * Decode a BER encoded BOOLEAN + */ + BER_Decoder& decode(bool& out) + { + return decode(out, BOOLEAN, UNIVERSAL); + } + + /* + * Decode a small BER encoded INTEGER + */ + BER_Decoder& decode(size_t& out) + { + return decode(out, INTEGER, UNIVERSAL); + } + + /* + * Decode a BER encoded INTEGER + */ + BER_Decoder& decode(BigInt& out) + { + return decode(out, INTEGER, UNIVERSAL); + } + + std::vector get_next_octet_string() + { + std::vector out_vec; + decode(out_vec, OCTET_STRING); + return out_vec; + } /* * BER decode a BIT STRING or OCTET STRING @@ -103,7 +206,7 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final ASN1_Tag type_tag, ASN1_Tag class_tag = CONTEXT_SPECIFIC); - BER_Decoder& decode(class BigInt& v, + BER_Decoder& decode(BigInt& v, ASN1_Tag type_tag, ASN1_Tag class_tag = CONTEXT_SPECIFIC); @@ -121,11 +224,14 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final ASN1_Tag type_tag = NO_OBJECT, ASN1_Tag class_tag = NO_OBJECT); - BER_Decoder& decode_octet_string_bigint(class BigInt& b); + /** + * Decode an integer value which is typed as an octet string + */ + BER_Decoder& decode_octet_string_bigint(BigInt& b); uint64_t decode_constrained_integer(ASN1_Tag type_tag, - ASN1_Tag class_tag, - size_t T_bytes); + ASN1_Tag class_tag, + size_t T_bytes); template BER_Decoder& decode_integer_type(T& out) { @@ -190,36 +296,27 @@ class BOTAN_PUBLIC_API(2,0) BER_Decoder final if(obj.is_a(type_tag, class_tag)) { if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC)) + { BER_Decoder(obj).decode(out, real_type).verify_end(); + } else { - push_back(obj); + push_back(std::move(obj)); decode(out, real_type, type_tag, class_tag); } } else { out.clear(); - push_back(obj); + push_back(std::move(obj)); } return (*this); } - BER_Decoder& operator=(const BER_Decoder&) = delete; - - explicit BER_Decoder(DataSource&); - - BER_Decoder(const uint8_t[], size_t); - - explicit BER_Decoder(const BER_Object& obj); - - explicit BER_Decoder(const secure_vector&); - - explicit BER_Decoder(const std::vector& vec); - - BER_Decoder(const BER_Decoder&); private: + BER_Decoder(BER_Object&& obj, BER_Decoder* parent); + BER_Decoder* m_parent = nullptr; BER_Object m_pushed; // either m_data_src.get() or an unowned pointer @@ -241,17 +338,19 @@ BER_Decoder& BER_Decoder::decode_optional(T& out, if(obj.is_a(type_tag, class_tag)) { if((class_tag & CONSTRUCTED) && (class_tag & CONTEXT_SPECIFIC)) + { BER_Decoder(obj).decode(out).verify_end(); + } else { - push_back(obj); + push_back(std::move(obj)); decode(out, type_tag, class_tag); } } else { out = default_value; - push_back(obj); + push_back(std::move(obj)); } return (*this); @@ -274,13 +373,14 @@ BER_Decoder& BER_Decoder::decode_optional_implicit( if(obj.is_a(type_tag, class_tag)) { obj.set_tagging(real_type, real_class); - push_back(obj); + push_back(std::move(obj)); decode(out, real_type, real_class); } else { + // Not what we wanted, push it back on the stream out = default_value; - push_back(obj); + push_back(std::move(obj)); } return (*this); @@ -299,7 +399,7 @@ BER_Decoder& BER_Decoder::decode_list(std::vector& vec, { T value; list.decode(value); - vec.push_back(value); + vec.push_back(std::move(value)); } list.end_cons(); -- cgit v1.2.3