blob: f713fb12d4bae8ca437790402f90345f1beabe02 [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
Alex Deymo923d8fa2014-07-15 17:58:51 -070031namespace {
32
33// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
34// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
35// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
36// padded hash will look as follows:
37//
38// 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH
39// |--------------205-----------||----19----||----32----|
40//
41// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
42// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
43// packed as follows:
44//
45// SEQUENCE(2+49) {
46// SEQUENCE(2+13) {
47// OBJECT(2+9) id-sha256
48// NULL(2+0)
49// }
50// OCTET STRING(2+32) <actual signature bytes...>
51// }
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080052const uint8_t kRSA2048SHA256Padding[] = {
Alex Deymo923d8fa2014-07-15 17:58:51 -070053 // PKCS1-v1_5 padding
54 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
55 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
56 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
57 0xff, 0xff, 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, 0x00,
80 // ASN.1 header
81 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
82 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
83 0x00, 0x04, 0x20,
84};
85
86} // namespace
87
88bool PayloadVerifier::LoadPayload(const string& payload_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080089 chromeos::Blob* out_payload,
Alex Deymo923d8fa2014-07-15 17:58:51 -070090 DeltaArchiveManifest* out_manifest,
91 uint64_t* out_metadata_size) {
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080092 chromeos::Blob payload;
Alex Deymo923d8fa2014-07-15 17:58:51 -070093 // Loads the payload and parses the manifest.
94 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
95 LOG(INFO) << "Payload size: " << payload.size();
96 ErrorCode error = ErrorCode::kSuccess;
97 InstallPlan install_plan;
Alex Vakulenko88b591f2014-08-28 16:48:57 -070098 DeltaPerformer delta_performer(nullptr, nullptr, &install_plan);
Alex Deymo923d8fa2014-07-15 17:58:51 -070099 TEST_AND_RETURN_FALSE(
100 delta_performer.ParsePayloadMetadata(payload, &error) ==
101 DeltaPerformer::kMetadataParseSuccess);
102 TEST_AND_RETURN_FALSE(delta_performer.GetManifest(out_manifest));
103 *out_metadata_size = delta_performer.GetMetadataSize();
104 LOG(INFO) << "Metadata size: " << *out_metadata_size;
105 out_payload->swap(payload);
106 return true;
107}
108
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800109bool PayloadVerifier::VerifySignature(const chromeos::Blob& signature_blob,
Alex Deymof329b932014-10-30 01:37:48 -0700110 const string& public_key_path,
Alex Deymob552a682015-09-30 09:36:49 -0700111 const chromeos::Blob& hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700112 TEST_AND_RETURN_FALSE(!public_key_path.empty());
113
114 Signatures signatures;
Alex Deymob552a682015-09-30 09:36:49 -0700115 LOG(INFO) << "signature blob size = " << signature_blob.size();
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800116 TEST_AND_RETURN_FALSE(signatures.ParseFromArray(signature_blob.data(),
Alex Deymo923d8fa2014-07-15 17:58:51 -0700117 signature_blob.size()));
118
Alex Deymob552a682015-09-30 09:36:49 -0700119 if (!signatures.signatures_size()) {
120 LOG(ERROR) << "No signatures stored in the blob.";
121 return false;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700122 }
Alex Deymo923d8fa2014-07-15 17:58:51 -0700123
Alex Deymob552a682015-09-30 09:36:49 -0700124 std::vector<chromeos::Blob> tested_hashes;
125 // Tries every signature in the signature blob.
126 for (int i = 0; i < signatures.signatures_size(); i++) {
127 const Signatures_Signature& signature = signatures.signatures(i);
128 chromeos::Blob sig_data(signature.data().begin(), signature.data().end());
129 chromeos::Blob sig_hash_data;
130 if (!GetRawHashFromSignature(sig_data, public_key_path, &sig_hash_data))
131 continue;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700132
Alex Deymob552a682015-09-30 09:36:49 -0700133 if (hash_data == sig_hash_data) {
134 LOG(INFO) << "Verified correct signature " << i + 1 << " out of "
135 << signatures.signatures_size() << " signatures.";
136 return true;
137 }
138 tested_hashes.push_back(sig_hash_data);
139 }
140 LOG(ERROR) << "None of the " << signatures.signatures_size()
141 << " signatures is correct. Expected:";
142 utils::HexDumpVector(hash_data);
143 LOG(ERROR) << "But found decrypted hashes:";
144 for (const auto& sig_hash_data : tested_hashes) {
145 utils::HexDumpVector(sig_hash_data);
146 }
147 return false;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700148}
149
150
151bool PayloadVerifier::GetRawHashFromSignature(
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800152 const chromeos::Blob& sig_data,
Alex Deymof329b932014-10-30 01:37:48 -0700153 const string& public_key_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800154 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700155 TEST_AND_RETURN_FALSE(!public_key_path.empty());
156
157 // The code below executes the equivalent of:
158 //
159 // openssl rsautl -verify -pubin -inkey |public_key_path|
160 // -in |sig_data| -out |out_hash_data|
161
162 // Loads the public key.
163 FILE* fpubkey = fopen(public_key_path.c_str(), "rb");
164 if (!fpubkey) {
165 LOG(ERROR) << "Unable to open public key file: " << public_key_path;
166 return false;
167 }
168
169 char dummy_password[] = { ' ', 0 }; // Ensure no password is read from stdin.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700170 RSA* rsa = PEM_read_RSA_PUBKEY(fpubkey, nullptr, nullptr, dummy_password);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700171 fclose(fpubkey);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700172 TEST_AND_RETURN_FALSE(rsa != nullptr);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700173 unsigned int keysize = RSA_size(rsa);
174 if (sig_data.size() > 2 * keysize) {
175 LOG(ERROR) << "Signature size is too big for public key size.";
176 RSA_free(rsa);
177 return false;
178 }
179
180 // Decrypts the signature.
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800181 chromeos::Blob hash_data(keysize);
182 int decrypt_size = RSA_public_decrypt(sig_data.size(),
183 sig_data.data(),
184 hash_data.data(),
185 rsa,
186 RSA_NO_PADDING);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700187 RSA_free(rsa);
188 TEST_AND_RETURN_FALSE(decrypt_size > 0 &&
189 decrypt_size <= static_cast<int>(hash_data.size()));
190 hash_data.resize(decrypt_size);
191 out_hash_data->swap(hash_data);
192 return true;
193}
194
Alex Deymof329b932014-10-30 01:37:48 -0700195bool PayloadVerifier::VerifySignedPayload(const string& payload_path,
Alex Deymob552a682015-09-30 09:36:49 -0700196 const string& public_key_path) {
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800197 chromeos::Blob payload;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700198 DeltaArchiveManifest manifest;
199 uint64_t metadata_size;
200 TEST_AND_RETURN_FALSE(LoadPayload(
201 payload_path, &payload, &manifest, &metadata_size));
202 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
203 manifest.has_signatures_size());
204 CHECK_EQ(payload.size(),
205 metadata_size + manifest.signatures_offset() +
206 manifest.signatures_size());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800207 chromeos::Blob signature_blob(
Alex Deymo923d8fa2014-07-15 17:58:51 -0700208 payload.begin() + metadata_size + manifest.signatures_offset(),
209 payload.end());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800210 chromeos::Blob hash;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700211 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(
212 payload.data(), metadata_size + manifest.signatures_offset(), &hash));
Alex Deymob552a682015-09-30 09:36:49 -0700213 TEST_AND_RETURN_FALSE(PadRSA2048SHA256Hash(&hash));
214 TEST_AND_RETURN_FALSE(VerifySignature(
215 signature_blob, public_key_path, hash));
Alex Deymo923d8fa2014-07-15 17:58:51 -0700216 return true;
217}
218
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800219bool PayloadVerifier::PadRSA2048SHA256Hash(chromeos::Blob* hash) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700220 TEST_AND_RETURN_FALSE(hash->size() == 32);
221 hash->insert(hash->begin(),
222 reinterpret_cast<const char*>(kRSA2048SHA256Padding),
223 reinterpret_cast<const char*>(kRSA2048SHA256Padding +
224 sizeof(kRSA2048SHA256Padding)));
225 TEST_AND_RETURN_FALSE(hash->size() == 256);
226 return true;
227}
228
229} // namespace chromeos_update_engine