diff options
-rw-r--r-- | asn1_decoder.cpp | 253 | ||||
-rw-r--r-- | asn1_decoder.h | 45 | ||||
-rw-r--r-- | install.cpp | 2 | ||||
-rw-r--r-- | tests/unit/asn1_decoder_test.cpp | 397 | ||||
-rw-r--r-- | updater/Android.mk | 20 | ||||
-rw-r--r-- | verifier.cpp | 115 | ||||
-rw-r--r-- | verifier.h | 2 |
7 files changed, 386 insertions, 448 deletions
diff --git a/asn1_decoder.cpp b/asn1_decoder.cpp index e7aef781c..a9dfccc58 100644 --- a/asn1_decoder.cpp +++ b/asn1_decoder.cpp @@ -14,178 +14,145 @@ * limitations under the License. */ -#include <malloc.h> -#include <stdint.h> -#include <string.h> - #include "asn1_decoder.h" +#include <stdint.h> -typedef struct asn1_context { - size_t length; - uint8_t* p; - int app_type; -} asn1_context_t; - - -static const int kMaskConstructed = 0xE0; -static const int kMaskTag = 0x7F; -static const int kMaskAppType = 0x1F; - -static const int kTagOctetString = 0x04; -static const int kTagOid = 0x06; -static const int kTagSequence = 0x30; -static const int kTagSet = 0x31; -static const int kTagConstructed = 0xA0; - -asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length) { - asn1_context_t* ctx = (asn1_context_t*) calloc(1, sizeof(asn1_context_t)); - if (ctx == NULL) { - return NULL; - } - ctx->p = buffer; - ctx->length = length; - return ctx; -} - -void asn1_context_free(asn1_context_t* ctx) { - free(ctx); +int asn1_context::peek_byte() const { + if (length_ <= 0) { + return -1; + } + return *p_; } -static inline int peek_byte(asn1_context_t* ctx) { - if (ctx->length <= 0) { - return -1; - } - return *ctx->p; -} +int asn1_context::get_byte() { + if (length_ <= 0) { + return -1; + } -static inline int get_byte(asn1_context_t* ctx) { - if (ctx->length <= 0) { - return -1; - } - int byte = *ctx->p; - ctx->p++; - ctx->length--; - return byte; + int byte = *p_; + p_++; + length_--; + return byte; } -static inline bool skip_bytes(asn1_context_t* ctx, size_t num_skip) { - if (ctx->length < num_skip) { - return false; - } - ctx->p += num_skip; - ctx->length -= num_skip; - return true; +bool asn1_context::skip_bytes(size_t num_skip) { + if (length_ < num_skip) { + return false; + } + p_ += num_skip; + length_ -= num_skip; + return true; } -static bool decode_length(asn1_context_t* ctx, size_t* out_len) { - int num_octets = get_byte(ctx); - if (num_octets == -1) { - return false; - } - if ((num_octets & 0x80) == 0x00) { - *out_len = num_octets; - return 1; - } - num_octets &= kMaskTag; - if ((size_t)num_octets >= sizeof(size_t)) { - return false; - } - size_t length = 0; - for (int i = 0; i < num_octets; ++i) { - int byte = get_byte(ctx); - if (byte == -1) { - return false; - } - length <<= 8; - length += byte; - } - *out_len = length; +bool asn1_context::decode_length(size_t* out_len) { + int num_octets = get_byte(); + if (num_octets == -1) { + return false; + } + if ((num_octets & 0x80) == 0x00) { + *out_len = num_octets; return true; + } + num_octets &= kMaskTag; + if (static_cast<size_t>(num_octets) >= sizeof(size_t)) { + return false; + } + size_t length = 0; + for (int i = 0; i < num_octets; ++i) { + int byte = get_byte(); + if (byte == -1) { + return false; + } + length <<= 8; + length += byte; + } + *out_len = length; + return true; } /** * Returns the constructed type and advances the pointer. E.g. A0 -> 0 */ -asn1_context_t* asn1_constructed_get(asn1_context_t* ctx) { - int type = get_byte(ctx); - if (type == -1 || (type & kMaskConstructed) != kTagConstructed) { - return NULL; - } - size_t length; - if (!decode_length(ctx, &length) || length > ctx->length) { - return NULL; - } - asn1_context_t* app_ctx = asn1_context_new(ctx->p, length); - app_ctx->app_type = type & kMaskAppType; - return app_ctx; +asn1_context* asn1_context::asn1_constructed_get() { + int type = get_byte(); + if (type == -1 || (type & kMaskConstructed) != kTagConstructed) { + return nullptr; + } + size_t length; + if (!decode_length(&length) || length > length_) { + return nullptr; + } + asn1_context* app_ctx = new asn1_context(p_, length); + app_ctx->app_type_ = type & kMaskAppType; + return app_ctx; } -bool asn1_constructed_skip_all(asn1_context_t* ctx) { - int byte = peek_byte(ctx); - while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) { - skip_bytes(ctx, 1); - size_t length; - if (!decode_length(ctx, &length) || !skip_bytes(ctx, length)) { - return false; - } - byte = peek_byte(ctx); +bool asn1_context::asn1_constructed_skip_all() { + int byte = peek_byte(); + while (byte != -1 && (byte & kMaskConstructed) == kTagConstructed) { + skip_bytes(1); + size_t length; + if (!decode_length(&length) || !skip_bytes(length)) { + return false; } - return byte != -1; + byte = peek_byte(); + } + return byte != -1; } -int asn1_constructed_type(asn1_context_t* ctx) { - return ctx->app_type; +int asn1_context::asn1_constructed_type() const { + return app_type_; } -asn1_context_t* asn1_sequence_get(asn1_context_t* ctx) { - if ((get_byte(ctx) & kMaskTag) != kTagSequence) { - return NULL; - } - size_t length; - if (!decode_length(ctx, &length) || length > ctx->length) { - return NULL; - } - return asn1_context_new(ctx->p, length); +asn1_context* asn1_context::asn1_sequence_get() { + if ((get_byte() & kMaskTag) != kTagSequence) { + return nullptr; + } + size_t length; + if (!decode_length(&length) || length > length_) { + return nullptr; + } + return new asn1_context(p_, length); } -asn1_context_t* asn1_set_get(asn1_context_t* ctx) { - if ((get_byte(ctx) & kMaskTag) != kTagSet) { - return NULL; - } - size_t length; - if (!decode_length(ctx, &length) || length > ctx->length) { - return NULL; - } - return asn1_context_new(ctx->p, length); +asn1_context* asn1_context::asn1_set_get() { + if ((get_byte() & kMaskTag) != kTagSet) { + return nullptr; + } + size_t length; + if (!decode_length(&length) || length > length_) { + return nullptr; + } + return new asn1_context(p_, length); } -bool asn1_sequence_next(asn1_context_t* ctx) { - size_t length; - if (get_byte(ctx) == -1 || !decode_length(ctx, &length) || !skip_bytes(ctx, length)) { - return false; - } - return true; +bool asn1_context::asn1_sequence_next() { + size_t length; + if (get_byte() == -1 || !decode_length(&length) || !skip_bytes(length)) { + return false; + } + return true; } -bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length) { - if (get_byte(ctx) != kTagOid) { - return false; - } - if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) { - return false; - } - *oid = ctx->p; - return true; +bool asn1_context::asn1_oid_get(const uint8_t** oid, size_t* length) { + if (get_byte() != kTagOid) { + return false; + } + if (!decode_length(length) || *length == 0 || *length > length_) { + return false; + } + *oid = p_; + return true; } -bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length) { - if (get_byte(ctx) != kTagOctetString) { - return false; - } - if (!decode_length(ctx, length) || *length == 0 || *length > ctx->length) { - return false; - } - *octet_string = ctx->p; - return true; +bool asn1_context::asn1_octet_string_get(const uint8_t** octet_string, size_t* length) { + if (get_byte() != kTagOctetString) { + return false; + } + if (!decode_length(length) || *length == 0 || *length > length_) { + return false; + } + *octet_string = p_; + return true; } diff --git a/asn1_decoder.h b/asn1_decoder.h index b17141c44..3e992115a 100644 --- a/asn1_decoder.h +++ b/asn1_decoder.h @@ -14,23 +14,42 @@ * limitations under the License. */ - #ifndef ASN1_DECODER_H_ #define ASN1_DECODER_H_ #include <stdint.h> -typedef struct asn1_context asn1_context_t; - -asn1_context_t* asn1_context_new(uint8_t* buffer, size_t length); -void asn1_context_free(asn1_context_t* ctx); -asn1_context_t* asn1_constructed_get(asn1_context_t* ctx); -bool asn1_constructed_skip_all(asn1_context_t* ctx); -int asn1_constructed_type(asn1_context_t* ctx); -asn1_context_t* asn1_sequence_get(asn1_context_t* ctx); -asn1_context_t* asn1_set_get(asn1_context_t* ctx); -bool asn1_sequence_next(asn1_context_t* seq); -bool asn1_oid_get(asn1_context_t* ctx, uint8_t** oid, size_t* length); -bool asn1_octet_string_get(asn1_context_t* ctx, uint8_t** octet_string, size_t* length); +class asn1_context { + public: + asn1_context(const uint8_t* buffer, size_t length) : p_(buffer), length_(length), app_type_(0) {} + int asn1_constructed_type() const; + asn1_context* asn1_constructed_get(); + bool asn1_constructed_skip_all(); + asn1_context* asn1_sequence_get(); + asn1_context* asn1_set_get(); + bool asn1_sequence_next(); + bool asn1_oid_get(const uint8_t** oid, size_t* length); + bool asn1_octet_string_get(const uint8_t** octet_string, size_t* length); + + private: + static constexpr int kMaskConstructed = 0xE0; + static constexpr int kMaskTag = 0x7F; + static constexpr int kMaskAppType = 0x1F; + + static constexpr int kTagOctetString = 0x04; + static constexpr int kTagOid = 0x06; + static constexpr int kTagSequence = 0x30; + static constexpr int kTagSet = 0x31; + static constexpr int kTagConstructed = 0xA0; + + int peek_byte() const; + int get_byte(); + bool skip_bytes(size_t num_skip); + bool decode_length(size_t* out_len); + + const uint8_t* p_; + size_t length_; + int app_type_; +}; #endif /* ASN1_DECODER_H_ */ diff --git a/install.cpp b/install.cpp index 9123fc276..db8fb97db 100644 --- a/install.cpp +++ b/install.cpp @@ -589,7 +589,7 @@ bool verify_package(const unsigned char* package_data, size_t package_size) { // Verify package. ui->Print("Verifying update package...\n"); auto t0 = std::chrono::system_clock::now(); - int err = verify_file(const_cast<unsigned char*>(package_data), package_size, loadedKeys, + int err = verify_file(package_data, package_size, loadedKeys, std::bind(&RecoveryUI::SetProgress, ui, std::placeholders::_1)); std::chrono::duration<double> duration = std::chrono::system_clock::now() - t0; ui->Print("Update package verification took %.1f s (result %d).\n", duration.count(), err); diff --git a/tests/unit/asn1_decoder_test.cpp b/tests/unit/asn1_decoder_test.cpp index af96d87d2..b334a655b 100644 --- a/tests/unit/asn1_decoder_test.cpp +++ b/tests/unit/asn1_decoder_test.cpp @@ -14,225 +14,188 @@ * limitations under the License. */ -#define LOG_TAG "asn1_decoder_test" +#include <stdint.h> + +#include <memory> -#include <cutils/log.h> #include <gtest/gtest.h> -#include <stdint.h> -#include <unistd.h> #include "asn1_decoder.h" -namespace android { - -class Asn1DecoderTest : public testing::Test { -}; - -TEST_F(Asn1DecoderTest, Empty_Failure) { - uint8_t empty[] = { }; - asn1_context_t* ctx = asn1_context_new(empty, sizeof(empty)); - - EXPECT_EQ(NULL, asn1_constructed_get(ctx)); - EXPECT_FALSE(asn1_constructed_skip_all(ctx)); - EXPECT_EQ(0, asn1_constructed_type(ctx)); - EXPECT_EQ(NULL, asn1_sequence_get(ctx)); - EXPECT_EQ(NULL, asn1_set_get(ctx)); - EXPECT_FALSE(asn1_sequence_next(ctx)); - - uint8_t* junk; - size_t length; - EXPECT_FALSE(asn1_oid_get(ctx, &junk, &length)); - EXPECT_FALSE(asn1_octet_string_get(ctx, &junk, &length)); - - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, ConstructedGet_TruncatedLength_Failure) { - uint8_t truncated[] = { 0xA0, 0x82, }; - asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); - EXPECT_EQ(NULL, asn1_constructed_get(ctx)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, ConstructedGet_LengthTooBig_Failure) { - uint8_t truncated[] = { 0xA0, 0x8a, 0xA5, 0x5A, 0xA5, 0x5A, - 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, }; - asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); - EXPECT_EQ(NULL, asn1_constructed_get(ctx)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, ConstructedGet_TooSmallForChild_Failure) { - uint8_t data[] = { 0xA5, 0x02, 0x06, 0x01, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - asn1_context_t* ptr = asn1_constructed_get(ctx); - ASSERT_NE((asn1_context_t*)NULL, ptr); - EXPECT_EQ(5, asn1_constructed_type(ptr)); - uint8_t* oid; - size_t length; - EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length)); - asn1_context_free(ptr); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, ConstructedGet_Success) { - uint8_t data[] = { 0xA5, 0x03, 0x06, 0x01, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - asn1_context_t* ptr = asn1_constructed_get(ctx); - ASSERT_NE((asn1_context_t*)NULL, ptr); - EXPECT_EQ(5, asn1_constructed_type(ptr)); - uint8_t* oid; - size_t length; - ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length)); - EXPECT_EQ(1U, length); - EXPECT_EQ(0x01U, *oid); - asn1_context_free(ptr); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, ConstructedSkipAll_TruncatedLength_Failure) { - uint8_t truncated[] = { 0xA2, 0x82, }; - asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); - EXPECT_FALSE(asn1_constructed_skip_all(ctx)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, ConstructedSkipAll_Success) { - uint8_t data[] = { 0xA0, 0x03, 0x02, 0x01, 0x01, - 0xA1, 0x03, 0x02, 0x01, 0x01, - 0x06, 0x01, 0xA5, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - ASSERT_TRUE(asn1_constructed_skip_all(ctx)); - uint8_t* oid; - size_t length; - ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length)); - EXPECT_EQ(1U, length); - EXPECT_EQ(0xA5U, *oid); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, SequenceGet_TruncatedLength_Failure) { - uint8_t truncated[] = { 0x30, 0x82, }; - asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); - EXPECT_EQ(NULL, asn1_sequence_get(ctx)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, SequenceGet_TooSmallForChild_Failure) { - uint8_t data[] = { 0x30, 0x02, 0x06, 0x01, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - asn1_context_t* ptr = asn1_sequence_get(ctx); - ASSERT_NE((asn1_context_t*)NULL, ptr); - uint8_t* oid; - size_t length; - EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length)); - asn1_context_free(ptr); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, SequenceGet_Success) { - uint8_t data[] = { 0x30, 0x03, 0x06, 0x01, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - asn1_context_t* ptr = asn1_sequence_get(ctx); - ASSERT_NE((asn1_context_t*)NULL, ptr); - uint8_t* oid; - size_t length; - ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length)); - EXPECT_EQ(1U, length); - EXPECT_EQ(0x01U, *oid); - asn1_context_free(ptr); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, SetGet_TruncatedLength_Failure) { - uint8_t truncated[] = { 0x31, 0x82, }; - asn1_context_t* ctx = asn1_context_new(truncated, sizeof(truncated)); - EXPECT_EQ(NULL, asn1_set_get(ctx)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, SetGet_TooSmallForChild_Failure) { - uint8_t data[] = { 0x31, 0x02, 0x06, 0x01, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - asn1_context_t* ptr = asn1_set_get(ctx); - ASSERT_NE((asn1_context_t*)NULL, ptr); - uint8_t* oid; - size_t length; - EXPECT_FALSE(asn1_oid_get(ptr, &oid, &length)); - asn1_context_free(ptr); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, SetGet_Success) { - uint8_t data[] = { 0x31, 0x03, 0x06, 0x01, 0xBA, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - asn1_context_t* ptr = asn1_set_get(ctx); - ASSERT_NE((asn1_context_t*)NULL, ptr); - uint8_t* oid; - size_t length; - ASSERT_TRUE(asn1_oid_get(ptr, &oid, &length)); - EXPECT_EQ(1U, length); - EXPECT_EQ(0xBAU, *oid); - asn1_context_free(ptr); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, OidGet_LengthZero_Failure) { - uint8_t data[] = { 0x06, 0x00, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - uint8_t* oid; - size_t length; - EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, OidGet_TooSmall_Failure) { - uint8_t data[] = { 0x06, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - uint8_t* oid; - size_t length; - EXPECT_FALSE(asn1_oid_get(ctx, &oid, &length)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, OidGet_Success) { - uint8_t data[] = { 0x06, 0x01, 0x99, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - uint8_t* oid; - size_t length; - ASSERT_TRUE(asn1_oid_get(ctx, &oid, &length)); - EXPECT_EQ(1U, length); - EXPECT_EQ(0x99U, *oid); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, OctetStringGet_LengthZero_Failure) { - uint8_t data[] = { 0x04, 0x00, 0x55, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - uint8_t* string; - size_t length; - ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, OctetStringGet_TooSmall_Failure) { - uint8_t data[] = { 0x04, 0x01, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - uint8_t* string; - size_t length; - ASSERT_FALSE(asn1_octet_string_get(ctx, &string, &length)); - asn1_context_free(ctx); -} - -TEST_F(Asn1DecoderTest, OctetStringGet_Success) { - uint8_t data[] = { 0x04, 0x01, 0xAA, }; - asn1_context_t* ctx = asn1_context_new(data, sizeof(data)); - uint8_t* string; - size_t length; - ASSERT_TRUE(asn1_octet_string_get(ctx, &string, &length)); - EXPECT_EQ(1U, length); - EXPECT_EQ(0xAAU, *string); - asn1_context_free(ctx); -} - -} // namespace android +TEST(Asn1DecoderTest, Empty_Failure) { + uint8_t empty[] = {}; + asn1_context ctx(empty, sizeof(empty)); + + ASSERT_EQ(nullptr, ctx.asn1_constructed_get()); + ASSERT_FALSE(ctx.asn1_constructed_skip_all()); + ASSERT_EQ(0, ctx.asn1_constructed_type()); + ASSERT_EQ(nullptr, ctx.asn1_sequence_get()); + ASSERT_EQ(nullptr, ctx.asn1_set_get()); + ASSERT_FALSE(ctx.asn1_sequence_next()); + + const uint8_t* junk; + size_t length; + ASSERT_FALSE(ctx.asn1_oid_get(&junk, &length)); + ASSERT_FALSE(ctx.asn1_octet_string_get(&junk, &length)); +} + +TEST(Asn1DecoderTest, ConstructedGet_TruncatedLength_Failure) { + uint8_t truncated[] = { 0xA0, 0x82 }; + asn1_context ctx(truncated, sizeof(truncated)); + ASSERT_EQ(nullptr, ctx.asn1_constructed_get()); +} + +TEST(Asn1DecoderTest, ConstructedGet_LengthTooBig_Failure) { + uint8_t truncated[] = { 0xA0, 0x8a, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A, 0xA5, 0x5A }; + asn1_context ctx(truncated, sizeof(truncated)); + ASSERT_EQ(nullptr, ctx.asn1_constructed_get()); +} + +TEST(Asn1DecoderTest, ConstructedGet_TooSmallForChild_Failure) { + uint8_t data[] = { 0xA5, 0x02, 0x06, 0x01, 0x01 }; + asn1_context ctx(data, sizeof(data)); + std::unique_ptr<asn1_context> ptr(ctx.asn1_constructed_get()); + ASSERT_NE(nullptr, ptr); + ASSERT_EQ(5, ptr->asn1_constructed_type()); + const uint8_t* oid; + size_t length; + ASSERT_FALSE(ptr->asn1_oid_get(&oid, &length)); +} + +TEST(Asn1DecoderTest, ConstructedGet_Success) { + uint8_t data[] = { 0xA5, 0x03, 0x06, 0x01, 0x01 }; + asn1_context ctx(data, sizeof(data)); + std::unique_ptr<asn1_context> ptr(ctx.asn1_constructed_get()); + ASSERT_NE(nullptr, ptr); + ASSERT_EQ(5, ptr->asn1_constructed_type()); + const uint8_t* oid; + size_t length; + ASSERT_TRUE(ptr->asn1_oid_get(&oid, &length)); + ASSERT_EQ(1U, length); + ASSERT_EQ(0x01U, *oid); +} + +TEST(Asn1DecoderTest, ConstructedSkipAll_TruncatedLength_Failure) { + uint8_t truncated[] = { 0xA2, 0x82 }; + asn1_context ctx(truncated, sizeof(truncated)); + ASSERT_FALSE(ctx.asn1_constructed_skip_all()); +} + +TEST(Asn1DecoderTest, ConstructedSkipAll_Success) { + uint8_t data[] = { 0xA0, 0x03, 0x02, 0x01, 0x01, 0xA1, 0x03, 0x02, 0x01, 0x01, 0x06, 0x01, 0xA5 }; + asn1_context ctx(data, sizeof(data)); + ASSERT_TRUE(ctx.asn1_constructed_skip_all()); + const uint8_t* oid; + size_t length; + ASSERT_TRUE(ctx.asn1_oid_get(&oid, &length)); + ASSERT_EQ(1U, length); + ASSERT_EQ(0xA5U, *oid); +} + +TEST(Asn1DecoderTest, SequenceGet_TruncatedLength_Failure) { + uint8_t truncated[] = { 0x30, 0x82 }; + asn1_context ctx(truncated, sizeof(truncated)); + ASSERT_EQ(nullptr, ctx.asn1_sequence_get()); +} + +TEST(Asn1DecoderTest, SequenceGet_TooSmallForChild_Failure) { + uint8_t data[] = { 0x30, 0x02, 0x06, 0x01, 0x01 }; + asn1_context ctx(data, sizeof(data)); + std::unique_ptr<asn1_context> ptr(ctx.asn1_sequence_get()); + ASSERT_NE(nullptr, ptr); + const uint8_t* oid; + size_t length; + ASSERT_FALSE(ptr->asn1_oid_get(&oid, &length)); +} + +TEST(Asn1DecoderTest, SequenceGet_Success) { + uint8_t data[] = { 0x30, 0x03, 0x06, 0x01, 0x01 }; + asn1_context ctx(data, sizeof(data)); + std::unique_ptr<asn1_context> ptr(ctx.asn1_sequence_get()); + ASSERT_NE(nullptr, ptr); + const uint8_t* oid; + size_t length; + ASSERT_TRUE(ptr->asn1_oid_get(&oid, &length)); + ASSERT_EQ(1U, length); + ASSERT_EQ(0x01U, *oid); +} + +TEST(Asn1DecoderTest, SetGet_TruncatedLength_Failure) { + uint8_t truncated[] = { 0x31, 0x82 }; + asn1_context ctx(truncated, sizeof(truncated)); + ASSERT_EQ(nullptr, ctx.asn1_set_get()); +} + +TEST(Asn1DecoderTest, SetGet_TooSmallForChild_Failure) { + uint8_t data[] = { 0x31, 0x02, 0x06, 0x01, 0x01 }; + asn1_context ctx(data, sizeof(data)); + std::unique_ptr<asn1_context> ptr(ctx.asn1_set_get()); + ASSERT_NE(nullptr, ptr); + const uint8_t* oid; + size_t length; + ASSERT_FALSE(ptr->asn1_oid_get(&oid, &length)); +} + +TEST(Asn1DecoderTest, SetGet_Success) { + uint8_t data[] = { 0x31, 0x03, 0x06, 0x01, 0xBA }; + asn1_context ctx(data, sizeof(data)); + std::unique_ptr<asn1_context> ptr(ctx.asn1_set_get()); + ASSERT_NE(nullptr, ptr); + const uint8_t* oid; + size_t length; + ASSERT_TRUE(ptr->asn1_oid_get(&oid, &length)); + ASSERT_EQ(1U, length); + ASSERT_EQ(0xBAU, *oid); +} + +TEST(Asn1DecoderTest, OidGet_LengthZero_Failure) { + uint8_t data[] = { 0x06, 0x00, 0x01 }; + asn1_context ctx(data, sizeof(data)); + const uint8_t* oid; + size_t length; + ASSERT_FALSE(ctx.asn1_oid_get(&oid, &length)); +} + +TEST(Asn1DecoderTest, OidGet_TooSmall_Failure) { + uint8_t data[] = { 0x06, 0x01 }; + asn1_context ctx(data, sizeof(data)); + const uint8_t* oid; + size_t length; + ASSERT_FALSE(ctx.asn1_oid_get(&oid, &length)); +} + +TEST(Asn1DecoderTest, OidGet_Success) { + uint8_t data[] = { 0x06, 0x01, 0x99 }; + asn1_context ctx(data, sizeof(data)); + const uint8_t* oid; + size_t length; + ASSERT_TRUE(ctx.asn1_oid_get(&oid, &length)); + ASSERT_EQ(1U, length); + ASSERT_EQ(0x99U, *oid); +} + +TEST(Asn1DecoderTest, OctetStringGet_LengthZero_Failure) { + uint8_t data[] = { 0x04, 0x00, 0x55 }; + asn1_context ctx(data, sizeof(data)); + const uint8_t* string; + size_t length; + ASSERT_FALSE(ctx.asn1_octet_string_get(&string, &length)); +} + +TEST(Asn1DecoderTest, OctetStringGet_TooSmall_Failure) { + uint8_t data[] = { 0x04, 0x01 }; + asn1_context ctx(data, sizeof(data)); + const uint8_t* string; + size_t length; + ASSERT_FALSE(ctx.asn1_octet_string_get(&string, &length)); +} + +TEST(Asn1DecoderTest, OctetStringGet_Success) { + uint8_t data[] = { 0x04, 0x01, 0xAA }; + asn1_context ctx(data, sizeof(data)); + const uint8_t* string; + size_t length; + ASSERT_TRUE(ctx.asn1_octet_string_get(&string, &length)); + ASSERT_EQ(1U, length); + ASSERT_EQ(0xAAU, *string); +} diff --git a/updater/Android.mk b/updater/Android.mk index 3a47dacd5..a113fe86c 100644 --- a/updater/Android.mk +++ b/updater/Android.mk @@ -110,21 +110,11 @@ LOCAL_STATIC_LIBRARIES := \ # any subsidiary static libraries required for your registered # extension libs. -inc := $(call intermediates-dir-for,PACKAGING,updater_extensions)/register.inc - -# Encode the value of TARGET_RECOVERY_UPDATER_LIBS into the filename of the dependency. -# So if TARGET_RECOVERY_UPDATER_LIBS is changed, a new dependency file will be generated. -# Note that we have to remove any existing depency files before creating new one, -# so no obsolete dependecy file gets used if you switch back to an old value. -inc_dep_file := $(inc).dep.$(subst $(space),-,$(sort $(TARGET_RECOVERY_UPDATER_LIBS))) -$(inc_dep_file): stem := $(inc).dep -$(inc_dep_file) : - $(hide) mkdir -p $(dir $@) - $(hide) rm -f $(stem).* - $(hide) touch $@ +LOCAL_MODULE_CLASS := EXECUTABLES +inc := $(call local-generated-sources-dir)/register.inc $(inc) : libs := $(TARGET_RECOVERY_UPDATER_LIBS) -$(inc) : $(inc_dep_file) +$(inc) : $(hide) mkdir -p $(dir $@) $(hide) echo "" > $@ $(hide) $(foreach lib,$(libs),echo "extern void Register_$(lib)(void);" >> $@;) @@ -132,11 +122,9 @@ $(inc) : $(inc_dep_file) $(hide) $(foreach lib,$(libs),echo " Register_$(lib)();" >> $@;) $(hide) echo "}" >> $@ -$(call intermediates-dir-for,EXECUTABLES,updater,,,$(TARGET_PREFER_32_BIT))/updater.o : $(inc) -LOCAL_C_INCLUDES += $(dir $(inc)) +LOCAL_GENERATED_SOURCES := $(inc) inc := -inc_dep_file := LOCAL_FORCE_STATIC_EXECUTABLE := true diff --git a/verifier.cpp b/verifier.cpp index 3beaa6e02..e9d540cdb 100644 --- a/verifier.cpp +++ b/verifier.cpp @@ -24,6 +24,7 @@ #include <algorithm> #include <functional> #include <memory> +#include <vector> #include <android-base/logging.h> #include <openssl/bn.h> @@ -60,51 +61,55 @@ static constexpr size_t MiB = 1024 * 1024; * SEQUENCE (SignatureAlgorithmIdentifier) * OCTET STRING (SignatureValue) */ -static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_der, - size_t* sig_der_length) { - asn1_context_t* ctx = asn1_context_new(pkcs7_der, pkcs7_der_len); - if (ctx == NULL) { - return false; - } +static bool read_pkcs7(const uint8_t* pkcs7_der, size_t pkcs7_der_len, + std::vector<uint8_t>* sig_der) { + CHECK(sig_der != nullptr); + sig_der->clear(); - asn1_context_t* pkcs7_seq = asn1_sequence_get(ctx); - if (pkcs7_seq != NULL && asn1_sequence_next(pkcs7_seq)) { - asn1_context_t *signed_data_app = asn1_constructed_get(pkcs7_seq); - if (signed_data_app != NULL) { - asn1_context_t* signed_data_seq = asn1_sequence_get(signed_data_app); - if (signed_data_seq != NULL - && asn1_sequence_next(signed_data_seq) - && asn1_sequence_next(signed_data_seq) - && asn1_sequence_next(signed_data_seq) - && asn1_constructed_skip_all(signed_data_seq)) { - asn1_context_t *sig_set = asn1_set_get(signed_data_seq); - if (sig_set != NULL) { - asn1_context_t* sig_seq = asn1_sequence_get(sig_set); - if (sig_seq != NULL - && asn1_sequence_next(sig_seq) - && asn1_sequence_next(sig_seq) - && asn1_sequence_next(sig_seq) - && asn1_sequence_next(sig_seq)) { - uint8_t* sig_der_ptr; - if (asn1_octet_string_get(sig_seq, &sig_der_ptr, sig_der_length)) { - *sig_der = (uint8_t*) malloc(*sig_der_length); - if (*sig_der != NULL) { - memcpy(*sig_der, sig_der_ptr, *sig_der_length); - } - } - asn1_context_free(sig_seq); - } - asn1_context_free(sig_set); - } - asn1_context_free(signed_data_seq); - } - asn1_context_free(signed_data_app); - } - asn1_context_free(pkcs7_seq); - } - asn1_context_free(ctx); + asn1_context ctx(pkcs7_der, pkcs7_der_len); + + std::unique_ptr<asn1_context> pkcs7_seq(ctx.asn1_sequence_get()); + if (pkcs7_seq == nullptr || !pkcs7_seq->asn1_sequence_next()) { + return false; + } + + std::unique_ptr<asn1_context> signed_data_app(pkcs7_seq->asn1_constructed_get()); + if (signed_data_app == nullptr) { + return false; + } - return *sig_der != NULL; + std::unique_ptr<asn1_context> signed_data_seq(signed_data_app->asn1_sequence_get()); + if (signed_data_seq == nullptr || + !signed_data_seq->asn1_sequence_next() || + !signed_data_seq->asn1_sequence_next() || + !signed_data_seq->asn1_sequence_next() || + !signed_data_seq->asn1_constructed_skip_all()) { + return false; + } + + std::unique_ptr<asn1_context> sig_set(signed_data_seq->asn1_set_get()); + if (sig_set == nullptr) { + return false; + } + + std::unique_ptr<asn1_context> sig_seq(sig_set->asn1_sequence_get()); + if (sig_seq == nullptr || + !sig_seq->asn1_sequence_next() || + !sig_seq->asn1_sequence_next() || + !sig_seq->asn1_sequence_next() || + !sig_seq->asn1_sequence_next()) { + return false; + } + + const uint8_t* sig_der_ptr; + size_t sig_der_length; + if (!sig_seq->asn1_octet_string_get(&sig_der_ptr, &sig_der_length)) { + return false; + } + + sig_der->resize(sig_der_length); + std::copy(sig_der_ptr, sig_der_ptr + sig_der_length, sig_der->begin()); + return true; } /* @@ -115,7 +120,7 @@ static bool read_pkcs7(uint8_t* pkcs7_der, size_t pkcs7_der_len, uint8_t** sig_d * Returns VERIFY_SUCCESS or VERIFY_FAILURE (if any error is encountered or no key matches the * signature). */ -int verify_file(unsigned char* addr, size_t length, const std::vector<Certificate>& keys, +int verify_file(const unsigned char* addr, size_t length, const std::vector<Certificate>& keys, const std::function<void(float)>& set_progress) { if (set_progress) { set_progress(0.0); @@ -136,7 +141,7 @@ int verify_file(unsigned char* addr, size_t length, const std::vector<Certificat return VERIFY_FAILURE; } - unsigned char* footer = addr + length - FOOTER_SIZE; + const unsigned char* footer = addr + length - FOOTER_SIZE; if (footer[2] != 0xff || footer[3] != 0xff) { LOG(ERROR) << "footer is wrong"; @@ -168,7 +173,7 @@ int verify_file(unsigned char* addr, size_t length, const std::vector<Certificat // (2 bytes) and the comment data. size_t signed_len = length - eocd_size + EOCD_HEADER_SIZE - 2; - unsigned char* eocd = addr + length - eocd_size; + const unsigned char* eocd = addr + length - eocd_size; // If this is really is the EOCD record, it will begin with the magic number $50 $4b $05 $06. if (eocd[0] != 0x50 || eocd[1] != 0x4b || eocd[2] != 0x05 || eocd[3] != 0x06) { @@ -177,7 +182,7 @@ int verify_file(unsigned char* addr, size_t length, const std::vector<Certificat } for (size_t i = 4; i < eocd_size-3; ++i) { - if (eocd[i ] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { + if (eocd[i] == 0x50 && eocd[i+1] == 0x4b && eocd[i+2] == 0x05 && eocd[i+3] == 0x06) { // If the sequence $50 $4b $05 $06 appears anywhere after the real one, libziparchive will // find the later (wrong) one, which could be exploitable. Fail the verification if this // sequence occurs anywhere after the real one. @@ -226,16 +231,14 @@ int verify_file(unsigned char* addr, size_t length, const std::vector<Certificat uint8_t sha256[SHA256_DIGEST_LENGTH]; SHA256_Final(sha256, &sha256_ctx); - uint8_t* sig_der = nullptr; - size_t sig_der_length = 0; - - uint8_t* signature = eocd + eocd_size - signature_start; + const uint8_t* signature = eocd + eocd_size - signature_start; size_t signature_size = signature_start - FOOTER_SIZE; LOG(INFO) << "signature (offset: " << std::hex << (length - signature_start) << ", length: " << signature_size << "): " << print_hex(signature, signature_size); - if (!read_pkcs7(signature, signature_size, &sig_der, &sig_der_length)) { + std::vector<uint8_t> sig_der; + if (!read_pkcs7(signature, signature_size, &sig_der)) { LOG(ERROR) << "Could not find signature DER block"; return VERIFY_FAILURE; } @@ -262,22 +265,21 @@ int verify_file(unsigned char* addr, size_t length, const std::vector<Certificat // The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that the signing tool appends // after the signature itself. if (key.key_type == Certificate::KEY_TYPE_RSA) { - if (!RSA_verify(hash_nid, hash, key.hash_len, sig_der, sig_der_length, key.rsa.get())) { + if (!RSA_verify(hash_nid, hash, key.hash_len, sig_der.data(), sig_der.size(), + key.rsa.get())) { LOG(INFO) << "failed to verify against RSA key " << i; continue; } LOG(INFO) << "whole-file signature verified against RSA key " << i; - free(sig_der); return VERIFY_SUCCESS; } else if (key.key_type == Certificate::KEY_TYPE_EC && key.hash_len == SHA256_DIGEST_LENGTH) { - if (!ECDSA_verify(0, hash, key.hash_len, sig_der, sig_der_length, key.ec.get())) { + if (!ECDSA_verify(0, hash, key.hash_len, sig_der.data(), sig_der.size(), key.ec.get())) { LOG(INFO) << "failed to verify against EC key " << i; continue; } LOG(INFO) << "whole-file signature verified against EC key " << i; - free(sig_der); return VERIFY_SUCCESS; } else { LOG(INFO) << "Unknown key type " << key.key_type; @@ -291,7 +293,6 @@ int verify_file(unsigned char* addr, size_t length, const std::vector<Certificat if (need_sha256) { LOG(INFO) << "SHA-256 digest: " << print_hex(sha256, SHA256_DIGEST_LENGTH); } - free(sig_der); LOG(ERROR) << "failed to verify whole-file signature"; return VERIFY_FAILURE; } diff --git a/verifier.h b/verifier.h index 067dab554..6bee74947 100644 --- a/verifier.h +++ b/verifier.h @@ -65,7 +65,7 @@ struct Certificate { * given keys. It optionally accepts a callback function for posting the progress to. Returns one * of the constants of VERIFY_SUCCESS and VERIFY_FAILURE. */ -int verify_file(unsigned char* addr, size_t length, const std::vector<Certificate>& keys, +int verify_file(const unsigned char* addr, size_t length, const std::vector<Certificate>& keys, const std::function<void(float)>& set_progress = nullptr); bool load_keys(const char* filename, std::vector<Certificate>& certs); |