Add support for ECDSA signatures
This adds support for key version 5 which is an EC key using the NIST P-256 curve parameters. OTAs may be signed with these keys using the ECDSA signature algorithm with SHA-256. Change-Id: Id88672a3deb70681c78d5ea0d739e10f839e4567
This commit is contained in:
parent
58c60900ac
commit
7a4adb5268
12 changed files with 823 additions and 61 deletions
11
Android.mk
11
Android.mk
|
@ -24,6 +24,7 @@ LOCAL_SRC_FILES := \
|
|||
roots.cpp \
|
||||
ui.cpp \
|
||||
screen_ui.cpp \
|
||||
asn1_decoder.cpp \
|
||||
verifier.cpp \
|
||||
adb_install.cpp
|
||||
|
||||
|
@ -76,7 +77,13 @@ LOCAL_C_INCLUDES += system/extras/ext4_utils
|
|||
|
||||
include $(BUILD_EXECUTABLE)
|
||||
|
||||
|
||||
# All the APIs for testing
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libverifier
|
||||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := \
|
||||
asn1_decoder.cpp
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := verifier_test
|
||||
|
@ -84,6 +91,7 @@ LOCAL_FORCE_STATIC_EXECUTABLE := true
|
|||
LOCAL_MODULE_TAGS := tests
|
||||
LOCAL_SRC_FILES := \
|
||||
verifier_test.cpp \
|
||||
asn1_decoder.cpp \
|
||||
verifier.cpp \
|
||||
ui.cpp
|
||||
LOCAL_STATIC_LIBRARIES := \
|
||||
|
@ -100,6 +108,7 @@ include $(LOCAL_PATH)/minui/Android.mk \
|
|||
$(LOCAL_PATH)/minzip/Android.mk \
|
||||
$(LOCAL_PATH)/minadbd/Android.mk \
|
||||
$(LOCAL_PATH)/mtdutils/Android.mk \
|
||||
$(LOCAL_PATH)/tests/Android.mk \
|
||||
$(LOCAL_PATH)/tools/Android.mk \
|
||||
$(LOCAL_PATH)/edify/Android.mk \
|
||||
$(LOCAL_PATH)/updater/Android.mk \
|
||||
|
|
190
asn1_decoder.cpp
Normal file
190
asn1_decoder.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "asn1_decoder.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);
|
||||
}
|
||||
|
||||
static inline int peek_byte(asn1_context_t* ctx) {
|
||||
if (ctx->length <= 0) {
|
||||
return -1;
|
||||
}
|
||||
return *ctx->p;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
return byte != -1;
|
||||
}
|
||||
|
||||
int asn1_constructed_type(asn1_context_t* ctx) {
|
||||
return ctx->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_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);
|
||||
}
|
||||
|
||||
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_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_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;
|
||||
}
|
36
asn1_decoder.h
Normal file
36
asn1_decoder.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* 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);
|
||||
|
||||
#endif /* ASN1_DECODER_H_ */
|
BIN
testdata/otasigned_ecdsa_sha256.zip
vendored
Normal file
BIN
testdata/otasigned_ecdsa_sha256.zip
vendored
Normal file
Binary file not shown.
BIN
testdata/testkey_ecdsa.pk8
vendored
Normal file
BIN
testdata/testkey_ecdsa.pk8
vendored
Normal file
Binary file not shown.
10
testdata/testkey_ecdsa.x509.pem
vendored
Normal file
10
testdata/testkey_ecdsa.x509.pem
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIBezCCASACCQC4g5wurPSmtzAKBggqhkjOPQQDAjBFMQswCQYDVQQGEwJBVTET
|
||||
MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ
|
||||
dHkgTHRkMB4XDTEzMTAwODIxMTAxM1oXDTE0MTAwODIxMTAxM1owRTELMAkGA1UE
|
||||
BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp
|
||||
ZGdpdHMgUHR5IEx0ZDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGcO1QDowF2E
|
||||
RboWVmAYI2oXTr5MHAJ4xpMUFsrWVvoktYSN2RhNuOl5jZGvSBsQII9p/4qfjLmS
|
||||
TBaCfQ0Xmt4wCgYIKoZIzj0EAwIDSQAwRgIhAIJjWmZAwngc2VcHUhYp2oSLoCQ+
|
||||
P+7AtbAn5242AqfOAiEAghO0t6jTKs0LUhLJrQwbOkHyZMVdZaG2vcwV9y9H5Qc=
|
||||
-----END CERTIFICATE-----
|
26
tests/Android.mk
Normal file
26
tests/Android.mk
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Build the unit tests.
|
||||
LOCAL_PATH := $(call my-dir)
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
# Build the unit tests.
|
||||
test_src_files := \
|
||||
asn1_decoder_test.cpp
|
||||
|
||||
shared_libraries := \
|
||||
liblog \
|
||||
libcutils
|
||||
|
||||
static_libraries := \
|
||||
libgtest \
|
||||
libgtest_main \
|
||||
libverifier
|
||||
|
||||
$(foreach file,$(test_src_files), \
|
||||
$(eval include $(CLEAR_VARS)) \
|
||||
$(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \
|
||||
$(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \
|
||||
$(eval LOCAL_SRC_FILES := $(file)) \
|
||||
$(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \
|
||||
$(eval LOCAL_C_INCLUDES := $(LOCAL_PATH)/..) \
|
||||
$(eval include $(BUILD_NATIVE_TEST)) \
|
||||
)
|
238
tests/asn1_decoder_test.cpp
Normal file
238
tests/asn1_decoder_test.cpp
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright (C) 2013 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "asn1_decoder_test"
|
||||
|
||||
#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
|
230
verifier.cpp
230
verifier.cpp
|
@ -14,10 +14,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "asn1_decoder.h"
|
||||
#include "common.h"
|
||||
#include "verifier.h"
|
||||
#include "ui.h"
|
||||
#include "verifier.h"
|
||||
|
||||
#include "mincrypt/dsa_sig.h"
|
||||
#include "mincrypt/p256.h"
|
||||
#include "mincrypt/p256_ecdsa.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
#include "mincrypt/sha.h"
|
||||
#include "mincrypt/sha256.h"
|
||||
|
@ -28,6 +32,78 @@
|
|||
|
||||
extern RecoveryUI* ui;
|
||||
|
||||
/*
|
||||
* Simple version of PKCS#7 SignedData extraction. This extracts the
|
||||
* signature OCTET STRING to be used for signature verification.
|
||||
*
|
||||
* For full details, see http://www.ietf.org/rfc/rfc3852.txt
|
||||
*
|
||||
* The PKCS#7 structure looks like:
|
||||
*
|
||||
* SEQUENCE (ContentInfo)
|
||||
* OID (ContentType)
|
||||
* [0] (content)
|
||||
* SEQUENCE (SignedData)
|
||||
* INTEGER (version CMSVersion)
|
||||
* SET (DigestAlgorithmIdentifiers)
|
||||
* SEQUENCE (EncapsulatedContentInfo)
|
||||
* [0] (CertificateSet OPTIONAL)
|
||||
* [1] (RevocationInfoChoices OPTIONAL)
|
||||
* SET (SignerInfos)
|
||||
* SEQUENCE (SignerInfo)
|
||||
* INTEGER (CMSVersion)
|
||||
* SEQUENCE (SignerIdentifier)
|
||||
* SEQUENCE (DigestAlgorithmIdentifier)
|
||||
* 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;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return *sig_der != NULL;
|
||||
}
|
||||
|
||||
// Look for an RSA signature embedded in the .ZIP file comment given
|
||||
// the path to the zip. Verify it matches one of the given public
|
||||
// keys.
|
||||
|
@ -79,9 +155,8 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
|
|||
LOGI("comment is %d bytes; signature %d bytes from end\n",
|
||||
comment_size, signature_start);
|
||||
|
||||
if (signature_start - FOOTER_SIZE < RSANUMBYTES) {
|
||||
// "signature" block isn't big enough to contain an RSA block.
|
||||
LOGE("signature is too short\n");
|
||||
if (signature_start <= FOOTER_SIZE) {
|
||||
LOGE("Signature start is in the footer");
|
||||
fclose(f);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
@ -187,6 +262,23 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
|
|||
const uint8_t* sha1 = SHA_final(&sha1_ctx);
|
||||
const uint8_t* sha256 = SHA256_final(&sha256_ctx);
|
||||
|
||||
uint8_t* sig_der = NULL;
|
||||
size_t sig_der_length = 0;
|
||||
|
||||
size_t signature_size = signature_start - FOOTER_SIZE;
|
||||
if (!read_pkcs7(eocd + eocd_size - signature_start, signature_size, &sig_der,
|
||||
&sig_der_length)) {
|
||||
LOGE("Could not find signature DER block\n");
|
||||
free(eocd);
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
free(eocd);
|
||||
|
||||
/*
|
||||
* Check to make sure at least one of the keys matches the signature. Since
|
||||
* any key can match, we need to try each before determining a verification
|
||||
* failure has happened.
|
||||
*/
|
||||
for (i = 0; i < numKeys; ++i) {
|
||||
const uint8_t* hash;
|
||||
switch (pKeys[i].hash_len) {
|
||||
|
@ -197,16 +289,46 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
|
|||
|
||||
// The 6 bytes is the "(signature_start) $ff $ff (comment_size)" that
|
||||
// the signing tool appends after the signature itself.
|
||||
if (RSA_verify(pKeys[i].public_key, eocd + eocd_size - 6 - RSANUMBYTES,
|
||||
RSANUMBYTES, hash, pKeys[i].hash_len)) {
|
||||
LOGI("whole-file signature verified against key %d\n", i);
|
||||
free(eocd);
|
||||
if (pKeys[i].key_type == Certificate::RSA) {
|
||||
if (sig_der_length < RSANUMBYTES) {
|
||||
// "signature" block isn't big enough to contain an RSA block.
|
||||
LOGI("signature is too short for RSA key %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!RSA_verify(pKeys[i].rsa, sig_der, RSANUMBYTES,
|
||||
hash, pKeys[i].hash_len)) {
|
||||
LOGI("failed to verify against RSA key %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGI("whole-file signature verified against RSA key %d\n", i);
|
||||
free(sig_der);
|
||||
return VERIFY_SUCCESS;
|
||||
} else if (pKeys[i].key_type == Certificate::EC
|
||||
&& pKeys[i].hash_len == SHA256_DIGEST_SIZE) {
|
||||
p256_int r, s;
|
||||
if (!dsa_sig_unpack(sig_der, sig_der_length, &r, &s)) {
|
||||
LOGI("Not a DSA signature block for EC key %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
p256_int p256_hash;
|
||||
p256_from_bin(hash, &p256_hash);
|
||||
if (!p256_ecdsa_verify(&(pKeys[i].ec->x), &(pKeys[i].ec->y),
|
||||
&p256_hash, &r, &s)) {
|
||||
LOGI("failed to verify against EC key %d\n", i);
|
||||
continue;
|
||||
}
|
||||
|
||||
LOGI("whole-file signature verified against EC key %d\n", i);
|
||||
free(sig_der);
|
||||
return VERIFY_SUCCESS;
|
||||
} else {
|
||||
LOGI("failed to verify against key %d\n", i);
|
||||
LOGI("Unknown key type %d\n", pKeys[i].key_type);
|
||||
}
|
||||
}
|
||||
free(eocd);
|
||||
free(sig_der);
|
||||
LOGE("failed to verify whole-file signature\n");
|
||||
return VERIFY_FAILURE;
|
||||
}
|
||||
|
@ -238,6 +360,7 @@ int verify_file(const char* path, const Certificate* pKeys, unsigned int numKeys
|
|||
// 2: 2048-bit RSA key with e=65537 and SHA-1 hash
|
||||
// 3: 2048-bit RSA key with e=3 and SHA-256 hash
|
||||
// 4: 2048-bit RSA key with e=65537 and SHA-256 hash
|
||||
// 5: 256-bit EC key using the NIST P-256 curve parameters and SHA-256 hash
|
||||
//
|
||||
// Returns NULL if the file failed to parse, or if it contain zero keys.
|
||||
Certificate*
|
||||
|
@ -258,28 +381,41 @@ load_keys(const char* filename, int* numKeys) {
|
|||
++*numKeys;
|
||||
out = (Certificate*)realloc(out, *numKeys * sizeof(Certificate));
|
||||
Certificate* cert = out + (*numKeys - 1);
|
||||
cert->public_key = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
|
||||
memset(cert, '\0', sizeof(Certificate));
|
||||
|
||||
char start_char;
|
||||
if (fscanf(f, " %c", &start_char) != 1) goto exit;
|
||||
if (start_char == '{') {
|
||||
// a version 1 key has no version specifier.
|
||||
cert->public_key->exponent = 3;
|
||||
cert->key_type = Certificate::RSA;
|
||||
cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
|
||||
cert->rsa->exponent = 3;
|
||||
cert->hash_len = SHA_DIGEST_SIZE;
|
||||
} else if (start_char == 'v') {
|
||||
int version;
|
||||
if (fscanf(f, "%d {", &version) != 1) goto exit;
|
||||
switch (version) {
|
||||
case 2:
|
||||
cert->public_key->exponent = 65537;
|
||||
cert->key_type = Certificate::RSA;
|
||||
cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
|
||||
cert->rsa->exponent = 65537;
|
||||
cert->hash_len = SHA_DIGEST_SIZE;
|
||||
break;
|
||||
case 3:
|
||||
cert->public_key->exponent = 3;
|
||||
cert->key_type = Certificate::RSA;
|
||||
cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
|
||||
cert->rsa->exponent = 3;
|
||||
cert->hash_len = SHA256_DIGEST_SIZE;
|
||||
break;
|
||||
case 4:
|
||||
cert->public_key->exponent = 65537;
|
||||
cert->key_type = Certificate::RSA;
|
||||
cert->rsa = (RSAPublicKey*)malloc(sizeof(RSAPublicKey));
|
||||
cert->rsa->exponent = 65537;
|
||||
cert->hash_len = SHA256_DIGEST_SIZE;
|
||||
break;
|
||||
case 5:
|
||||
cert->key_type = Certificate::EC;
|
||||
cert->ec = (ECPublicKey*)calloc(1, sizeof(ECPublicKey));
|
||||
cert->hash_len = SHA256_DIGEST_SIZE;
|
||||
break;
|
||||
default:
|
||||
|
@ -287,23 +423,55 @@ load_keys(const char* filename, int* numKeys) {
|
|||
}
|
||||
}
|
||||
|
||||
RSAPublicKey* key = cert->public_key;
|
||||
if (fscanf(f, " %i , 0x%x , { %u",
|
||||
&(key->len), &(key->n0inv), &(key->n[0])) != 3) {
|
||||
if (cert->key_type == Certificate::RSA) {
|
||||
RSAPublicKey* key = cert->rsa;
|
||||
if (fscanf(f, " %i , 0x%x , { %u",
|
||||
&(key->len), &(key->n0inv), &(key->n[0])) != 3) {
|
||||
goto exit;
|
||||
}
|
||||
if (key->len != RSANUMWORDS) {
|
||||
LOGE("key length (%d) does not match expected size\n", key->len);
|
||||
goto exit;
|
||||
}
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
|
||||
}
|
||||
if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
|
||||
}
|
||||
fscanf(f, " } } ");
|
||||
|
||||
LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
|
||||
} else if (cert->key_type == Certificate::EC) {
|
||||
ECPublicKey* key = cert->ec;
|
||||
int key_len;
|
||||
unsigned int byte;
|
||||
uint8_t x_bytes[P256_NBYTES];
|
||||
uint8_t y_bytes[P256_NBYTES];
|
||||
if (fscanf(f, " %i , { %u", &key_len, &byte) != 2) goto exit;
|
||||
if (key_len != P256_NBYTES) {
|
||||
LOGE("Key length (%d) does not match expected size %d\n", key_len, P256_NBYTES);
|
||||
goto exit;
|
||||
}
|
||||
x_bytes[P256_NBYTES - 1] = byte;
|
||||
for (i = P256_NBYTES - 2; i >= 0; --i) {
|
||||
if (fscanf(f, " , %u", &byte) != 1) goto exit;
|
||||
x_bytes[i] = byte;
|
||||
}
|
||||
if (fscanf(f, " } , { %u", &byte) != 1) goto exit;
|
||||
y_bytes[P256_NBYTES - 1] = byte;
|
||||
for (i = P256_NBYTES - 2; i >= 0; --i) {
|
||||
if (fscanf(f, " , %u", &byte) != 1) goto exit;
|
||||
y_bytes[i] = byte;
|
||||
}
|
||||
fscanf(f, " } } ");
|
||||
p256_from_bin(x_bytes, &key->x);
|
||||
p256_from_bin(y_bytes, &key->y);
|
||||
} else {
|
||||
LOGE("Unknown key type %d\n", cert->key_type);
|
||||
goto exit;
|
||||
}
|
||||
if (key->len != RSANUMWORDS) {
|
||||
LOGE("key length (%d) does not match expected size\n", key->len);
|
||||
goto exit;
|
||||
}
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %u", &(key->n[i])) != 1) goto exit;
|
||||
}
|
||||
if (fscanf(f, " } , { %u", &(key->rr[0])) != 1) goto exit;
|
||||
for (i = 1; i < key->len; ++i) {
|
||||
if (fscanf(f, " , %u", &(key->rr[i])) != 1) goto exit;
|
||||
}
|
||||
fscanf(f, " } } ");
|
||||
|
||||
// if the line ends in a comma, this file has more keys.
|
||||
switch (fgetc(f)) {
|
||||
|
@ -319,8 +487,6 @@ load_keys(const char* filename, int* numKeys) {
|
|||
LOGE("unexpected character between keys\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
LOGI("read key e=%d hash=%d\n", key->exponent, cert->hash_len);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
17
verifier.h
17
verifier.h
|
@ -17,11 +17,24 @@
|
|||
#ifndef _RECOVERY_VERIFIER_H
|
||||
#define _RECOVERY_VERIFIER_H
|
||||
|
||||
#include "mincrypt/p256.h"
|
||||
#include "mincrypt/rsa.h"
|
||||
|
||||
typedef struct Certificate {
|
||||
typedef struct {
|
||||
p256_int x;
|
||||
p256_int y;
|
||||
} ECPublicKey;
|
||||
|
||||
typedef struct {
|
||||
typedef enum {
|
||||
RSA,
|
||||
EC,
|
||||
} KeyType;
|
||||
|
||||
int hash_len; // SHA_DIGEST_SIZE (SHA-1) or SHA256_DIGEST_SIZE (SHA-256)
|
||||
RSAPublicKey* public_key;
|
||||
KeyType key_type;
|
||||
RSAPublicKey* rsa;
|
||||
ECPublicKey* ec;
|
||||
} Certificate;
|
||||
|
||||
/* Look in the file for a signature footer, and verify that it
|
||||
|
|
|
@ -100,6 +100,18 @@ RSAPublicKey test_f4_key =
|
|||
65537
|
||||
};
|
||||
|
||||
ECPublicKey test_ec_key =
|
||||
{
|
||||
{
|
||||
{0xd656fa24u, 0x931416cau, 0x1c0278c6u, 0x174ebe4cu,
|
||||
0x6018236au, 0x45ba1656u, 0xe8c05d84u, 0x670ed500u}
|
||||
},
|
||||
{
|
||||
{0x0d179adeu, 0x4c16827du, 0x9f8cb992u, 0x8f69ff8au,
|
||||
0x481b1020u, 0x798d91afu, 0x184db8e9u, 0xb5848dd9u}
|
||||
}
|
||||
};
|
||||
|
||||
RecoveryUI* ui = NULL;
|
||||
|
||||
// verifier expects to find a UI object; we provide one that does
|
||||
|
@ -136,34 +148,86 @@ ui_print(const char* format, ...) {
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
static Certificate* add_certificate(Certificate** certsp, int* num_keys,
|
||||
Certificate::KeyType key_type) {
|
||||
int i = *num_keys;
|
||||
*num_keys = *num_keys + 1;
|
||||
*certsp = (Certificate*) realloc(*certsp, *num_keys * sizeof(Certificate));
|
||||
Certificate* certs = *certsp;
|
||||
certs[i].rsa = NULL;
|
||||
certs[i].ec = NULL;
|
||||
certs[i].key_type = key_type;
|
||||
certs[i].hash_len = SHA_DIGEST_SIZE;
|
||||
return &certs[i];
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2 || argc > 4) {
|
||||
fprintf(stderr, "Usage: %s [-sha256] [-f4 | -file <keys>] <package>\n", argv[0]);
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Usage: %s [-sha256] [-ec | -f4 | -file <keys>] <package>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
Certificate* certs = NULL;
|
||||
int num_keys = 0;
|
||||
|
||||
int argn = 1;
|
||||
while (argn < argc) {
|
||||
if (strcmp(argv[argn], "-sha256") == 0) {
|
||||
if (num_keys == 0) {
|
||||
fprintf(stderr, "May only specify -sha256 after key type\n");
|
||||
return 2;
|
||||
}
|
||||
++argn;
|
||||
Certificate* cert = &certs[num_keys - 1];
|
||||
cert->hash_len = SHA256_DIGEST_SIZE;
|
||||
} else if (strcmp(argv[argn], "-ec") == 0) {
|
||||
++argn;
|
||||
Certificate* cert = add_certificate(&certs, &num_keys, Certificate::EC);
|
||||
cert->ec = &test_ec_key;
|
||||
} else if (strcmp(argv[argn], "-e3") == 0) {
|
||||
++argn;
|
||||
Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA);
|
||||
cert->rsa = &test_key;
|
||||
} else if (strcmp(argv[argn], "-f4") == 0) {
|
||||
++argn;
|
||||
Certificate* cert = add_certificate(&certs, &num_keys, Certificate::RSA);
|
||||
cert->rsa = &test_f4_key;
|
||||
} else if (strcmp(argv[argn], "-file") == 0) {
|
||||
if (certs != NULL) {
|
||||
fprintf(stderr, "Cannot specify -file with other certs specified\n");
|
||||
return 2;
|
||||
}
|
||||
++argn;
|
||||
certs = load_keys(argv[argn], &num_keys);
|
||||
++argn;
|
||||
} else if (argv[argn][0] == '-') {
|
||||
fprintf(stderr, "Unknown argument %s\n", argv[argn]);
|
||||
return 2;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (argn == argc) {
|
||||
fprintf(stderr, "Must specify package to verify\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
Certificate default_cert;
|
||||
Certificate* cert = &default_cert;
|
||||
cert->public_key = &test_key;
|
||||
cert->hash_len = SHA_DIGEST_SIZE;
|
||||
int num_keys = 1;
|
||||
++argv;
|
||||
if (strcmp(argv[0], "-sha256") == 0) {
|
||||
++argv;
|
||||
cert->hash_len = SHA256_DIGEST_SIZE;
|
||||
}
|
||||
if (strcmp(argv[0], "-f4") == 0) {
|
||||
++argv;
|
||||
cert->public_key = &test_f4_key;
|
||||
} else if (strcmp(argv[0], "-file") == 0) {
|
||||
++argv;
|
||||
cert = load_keys(argv[0], &num_keys);
|
||||
++argv;
|
||||
if (num_keys == 0) {
|
||||
certs = (Certificate*) calloc(1, sizeof(Certificate));
|
||||
if (certs == NULL) {
|
||||
fprintf(stderr, "Failure allocating memory for default certificate\n");
|
||||
return 1;
|
||||
}
|
||||
certs->key_type = Certificate::RSA;
|
||||
certs->rsa = &test_key;
|
||||
certs->ec = NULL;
|
||||
certs->hash_len = SHA_DIGEST_SIZE;
|
||||
num_keys = 1;
|
||||
}
|
||||
|
||||
ui = new FakeUI();
|
||||
|
||||
int result = verify_file(*argv, cert, num_keys);
|
||||
int result = verify_file(argv[argn], certs, num_keys);
|
||||
if (result == VERIFY_SUCCESS) {
|
||||
printf("VERIFIED\n");
|
||||
return 0;
|
||||
|
|
|
@ -81,20 +81,30 @@ expect_fail unsigned.zip
|
|||
expect_fail jarsigned.zip
|
||||
|
||||
# success cases
|
||||
expect_succeed otasigned.zip
|
||||
expect_succeed otasigned.zip -e3
|
||||
expect_succeed otasigned_f4.zip -f4
|
||||
expect_succeed otasigned_sha256.zip -sha256
|
||||
expect_succeed otasigned_f4_sha256.zip -sha256 -f4
|
||||
expect_succeed otasigned_sha256.zip -e3 -sha256
|
||||
expect_succeed otasigned_f4_sha256.zip -f4 -sha256
|
||||
expect_succeed otasigned_ecdsa_sha256.zip -ec -sha256
|
||||
|
||||
# success with multiple keys
|
||||
expect_succeed otasigned.zip -f4 -e3
|
||||
expect_succeed otasigned_f4.zip -ec -f4
|
||||
expect_succeed otasigned_sha256.zip -ec -e3 -e3 -sha256
|
||||
expect_succeed otasigned_f4_sha256.zip -ec -sha256 -e3 -f4 -sha256
|
||||
expect_succeed otasigned_ecdsa_sha256.zip -f4 -sha256 -e3 -ec -sha256
|
||||
|
||||
# verified against different key
|
||||
expect_fail otasigned.zip -f4
|
||||
expect_fail otasigned_f4.zip
|
||||
expect_fail otasigned_f4.zip -e3
|
||||
expect_fail otasigned_ecdsa_sha256.zip -e3 -sha256
|
||||
|
||||
# verified against right key but wrong hash algorithm
|
||||
expect_fail otasigned.zip -sha256
|
||||
expect_fail otasigned_f4.zip -sha256 -f4
|
||||
expect_fail otasigned.zip -e3 -sha256
|
||||
expect_fail otasigned_f4.zip -f4 -sha256
|
||||
expect_fail otasigned_sha256.zip
|
||||
expect_fail otasigned_f4_sha256.zip -f4
|
||||
expect_fail otasigned_ecdsa_sha256.zip
|
||||
|
||||
# various other cases
|
||||
expect_fail random.zip
|
||||
|
|
Loading…
Reference in a new issue