blob: c3dc134fcbcfb9102e934ad353cf2da9bf601d12 [file] [log] [blame]
Alex Deymoaea4c1c2015-08-19 20:24:43 -07001//
2// Copyright (C) 2014 The Android Open Source Project
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
Alex Deymo923d8fa2014-07-15 17:58:51 -070016
17#include "update_engine/payload_verifier.h"
18
19#include <base/logging.h>
20#include <openssl/pem.h>
21
22#include "update_engine/delta_performer.h"
23#include "update_engine/omaha_hash_calculator.h"
Alex Deymo923d8fa2014-07-15 17:58:51 -070024#include "update_engine/update_metadata.pb.h"
25#include "update_engine/utils.h"
26
27using std::string;
Alex Deymo923d8fa2014-07-15 17:58:51 -070028
29namespace chromeos_update_engine {
30
31const uint32_t kSignatureMessageOriginalVersion = 1;
32const uint32_t kSignatureMessageCurrentVersion = 1;
33
34namespace {
35
36// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
37// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
38// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
39// padded hash will look as follows:
40//
41// 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH
42// |--------------205-----------||----19----||----32----|
43//
44// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
45// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
46// packed as follows:
47//
48// SEQUENCE(2+49) {
49// SEQUENCE(2+13) {
50// OBJECT(2+9) id-sha256
51// NULL(2+0)
52// }
53// OCTET STRING(2+32) <actual signature bytes...>
54// }
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080055const uint8_t kRSA2048SHA256Padding[] = {
Alex Deymo923d8fa2014-07-15 17:58:51 -070056 // PKCS1-v1_5 padding
57 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
58 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
59 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
60 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
61 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
62 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
63 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
64 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
65 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
66 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
67 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
68 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
69 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
70 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
71 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
72 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
73 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
74 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
75 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
76 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
77 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
78 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
79 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
80 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
81 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
82 0xff, 0xff, 0xff, 0xff, 0x00,
83 // ASN.1 header
84 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
85 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
86 0x00, 0x04, 0x20,
87};
88
89} // namespace
90
91bool PayloadVerifier::LoadPayload(const string& payload_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080092 chromeos::Blob* out_payload,
Alex Deymo923d8fa2014-07-15 17:58:51 -070093 DeltaArchiveManifest* out_manifest,
94 uint64_t* out_metadata_size) {
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080095 chromeos::Blob payload;
Alex Deymo923d8fa2014-07-15 17:58:51 -070096 // Loads the payload and parses the manifest.
97 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
98 LOG(INFO) << "Payload size: " << payload.size();
99 ErrorCode error = ErrorCode::kSuccess;
100 InstallPlan install_plan;
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700101 DeltaPerformer delta_performer(nullptr, nullptr, &install_plan);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700102 TEST_AND_RETURN_FALSE(
103 delta_performer.ParsePayloadMetadata(payload, &error) ==
104 DeltaPerformer::kMetadataParseSuccess);
105 TEST_AND_RETURN_FALSE(delta_performer.GetManifest(out_manifest));
106 *out_metadata_size = delta_performer.GetMetadataSize();
107 LOG(INFO) << "Metadata size: " << *out_metadata_size;
108 out_payload->swap(payload);
109 return true;
110}
111
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800112bool PayloadVerifier::VerifySignature(const chromeos::Blob& signature_blob,
Alex Deymof329b932014-10-30 01:37:48 -0700113 const string& public_key_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800114 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700115 return VerifySignatureBlob(signature_blob, public_key_path,
116 kSignatureMessageCurrentVersion, out_hash_data);
117}
118
119bool PayloadVerifier::VerifySignatureBlob(
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800120 const chromeos::Blob& signature_blob,
Alex Deymof329b932014-10-30 01:37:48 -0700121 const string& public_key_path,
Alex Deymo923d8fa2014-07-15 17:58:51 -0700122 uint32_t client_version,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800123 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700124 TEST_AND_RETURN_FALSE(!public_key_path.empty());
125
126 Signatures signatures;
127 LOG(INFO) << "signature size = " << signature_blob.size();
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800128 TEST_AND_RETURN_FALSE(signatures.ParseFromArray(signature_blob.data(),
Alex Deymo923d8fa2014-07-15 17:58:51 -0700129 signature_blob.size()));
130
131 // Finds a signature that matches the current version.
132 int sig_index = 0;
133 for (; sig_index < signatures.signatures_size(); sig_index++) {
134 const Signatures_Signature& signature = signatures.signatures(sig_index);
135 if (signature.has_version() &&
136 signature.version() == client_version) {
137 break;
138 }
139 }
140 TEST_AND_RETURN_FALSE(sig_index < signatures.signatures_size());
141
142 const Signatures_Signature& signature = signatures.signatures(sig_index);
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800143 chromeos::Blob sig_data(signature.data().begin(), signature.data().end());
Alex Deymo923d8fa2014-07-15 17:58:51 -0700144
145 return GetRawHashFromSignature(sig_data, public_key_path, out_hash_data);
146}
147
148
149bool PayloadVerifier::GetRawHashFromSignature(
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800150 const chromeos::Blob& sig_data,
Alex Deymof329b932014-10-30 01:37:48 -0700151 const string& public_key_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800152 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700153 TEST_AND_RETURN_FALSE(!public_key_path.empty());
154
155 // The code below executes the equivalent of:
156 //
157 // openssl rsautl -verify -pubin -inkey |public_key_path|
158 // -in |sig_data| -out |out_hash_data|
159
160 // Loads the public key.
161 FILE* fpubkey = fopen(public_key_path.c_str(), "rb");
162 if (!fpubkey) {
163 LOG(ERROR) << "Unable to open public key file: " << public_key_path;
164 return false;
165 }
166
167 char dummy_password[] = { ' ', 0 }; // Ensure no password is read from stdin.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700168 RSA* rsa = PEM_read_RSA_PUBKEY(fpubkey, nullptr, nullptr, dummy_password);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700169 fclose(fpubkey);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700170 TEST_AND_RETURN_FALSE(rsa != nullptr);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700171 unsigned int keysize = RSA_size(rsa);
172 if (sig_data.size() > 2 * keysize) {
173 LOG(ERROR) << "Signature size is too big for public key size.";
174 RSA_free(rsa);
175 return false;
176 }
177
178 // Decrypts the signature.
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800179 chromeos::Blob hash_data(keysize);
180 int decrypt_size = RSA_public_decrypt(sig_data.size(),
181 sig_data.data(),
182 hash_data.data(),
183 rsa,
184 RSA_NO_PADDING);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700185 RSA_free(rsa);
186 TEST_AND_RETURN_FALSE(decrypt_size > 0 &&
187 decrypt_size <= static_cast<int>(hash_data.size()));
188 hash_data.resize(decrypt_size);
189 out_hash_data->swap(hash_data);
190 return true;
191}
192
Alex Deymof329b932014-10-30 01:37:48 -0700193bool PayloadVerifier::VerifySignedPayload(const string& payload_path,
194 const string& public_key_path,
Alex Deymo923d8fa2014-07-15 17:58:51 -0700195 uint32_t client_key_check_version) {
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800196 chromeos::Blob payload;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700197 DeltaArchiveManifest manifest;
198 uint64_t metadata_size;
199 TEST_AND_RETURN_FALSE(LoadPayload(
200 payload_path, &payload, &manifest, &metadata_size));
201 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
202 manifest.has_signatures_size());
203 CHECK_EQ(payload.size(),
204 metadata_size + manifest.signatures_offset() +
205 manifest.signatures_size());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800206 chromeos::Blob signature_blob(
Alex Deymo923d8fa2014-07-15 17:58:51 -0700207 payload.begin() + metadata_size + manifest.signatures_offset(),
208 payload.end());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800209 chromeos::Blob signed_hash;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700210 TEST_AND_RETURN_FALSE(VerifySignatureBlob(
211 signature_blob, public_key_path, client_key_check_version, &signed_hash));
212 TEST_AND_RETURN_FALSE(!signed_hash.empty());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800213 chromeos::Blob hash;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700214 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(
215 payload.data(), metadata_size + manifest.signatures_offset(), &hash));
216 PadRSA2048SHA256Hash(&hash);
217 TEST_AND_RETURN_FALSE(hash == signed_hash);
218 return true;
219}
220
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800221bool PayloadVerifier::PadRSA2048SHA256Hash(chromeos::Blob* hash) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700222 TEST_AND_RETURN_FALSE(hash->size() == 32);
223 hash->insert(hash->begin(),
224 reinterpret_cast<const char*>(kRSA2048SHA256Padding),
225 reinterpret_cast<const char*>(kRSA2048SHA256Padding +
226 sizeof(kRSA2048SHA256Padding)));
227 TEST_AND_RETURN_FALSE(hash->size() == 256);
228 return true;
229}
230
231} // namespace chromeos_update_engine