blob: 2b96186dac69c97d92d261a2848c2284e33f7dba [file] [log] [blame]
Alex Deymo923d8fa2014-07-15 17:58:51 -07001// Copyright 2014 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "update_engine/payload_verifier.h"
6
7#include <base/logging.h>
8#include <openssl/pem.h>
9
10#include "update_engine/delta_performer.h"
11#include "update_engine/omaha_hash_calculator.h"
Alex Deymo923d8fa2014-07-15 17:58:51 -070012#include "update_engine/update_metadata.pb.h"
13#include "update_engine/utils.h"
14
15using std::string;
Alex Deymo923d8fa2014-07-15 17:58:51 -070016
17namespace chromeos_update_engine {
18
19const uint32_t kSignatureMessageOriginalVersion = 1;
20const uint32_t kSignatureMessageCurrentVersion = 1;
21
22namespace {
23
24// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
25// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
26// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
27// padded hash will look as follows:
28//
29// 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH
30// |--------------205-----------||----19----||----32----|
31//
32// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
33// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
34// packed as follows:
35//
36// SEQUENCE(2+49) {
37// SEQUENCE(2+13) {
38// OBJECT(2+9) id-sha256
39// NULL(2+0)
40// }
41// OCTET STRING(2+32) <actual signature bytes...>
42// }
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080043const uint8_t kRSA2048SHA256Padding[] = {
Alex Deymo923d8fa2014-07-15 17:58:51 -070044 // PKCS1-v1_5 padding
45 0x00, 0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
46 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
47 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
48 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
49 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
50 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
51 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
52 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
53 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
54 0xff, 0xff, 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, 0x00,
71 // ASN.1 header
72 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
73 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
74 0x00, 0x04, 0x20,
75};
76
77} // namespace
78
79bool PayloadVerifier::LoadPayload(const string& payload_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080080 chromeos::Blob* out_payload,
Alex Deymo923d8fa2014-07-15 17:58:51 -070081 DeltaArchiveManifest* out_manifest,
82 uint64_t* out_metadata_size) {
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -080083 chromeos::Blob payload;
Alex Deymo923d8fa2014-07-15 17:58:51 -070084 // Loads the payload and parses the manifest.
85 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
86 LOG(INFO) << "Payload size: " << payload.size();
87 ErrorCode error = ErrorCode::kSuccess;
88 InstallPlan install_plan;
Alex Vakulenko88b591f2014-08-28 16:48:57 -070089 DeltaPerformer delta_performer(nullptr, nullptr, &install_plan);
Alex Deymo923d8fa2014-07-15 17:58:51 -070090 TEST_AND_RETURN_FALSE(
91 delta_performer.ParsePayloadMetadata(payload, &error) ==
92 DeltaPerformer::kMetadataParseSuccess);
93 TEST_AND_RETURN_FALSE(delta_performer.GetManifest(out_manifest));
94 *out_metadata_size = delta_performer.GetMetadataSize();
95 LOG(INFO) << "Metadata size: " << *out_metadata_size;
96 out_payload->swap(payload);
97 return true;
98}
99
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800100bool PayloadVerifier::VerifySignature(const chromeos::Blob& signature_blob,
Alex Deymof329b932014-10-30 01:37:48 -0700101 const string& public_key_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800102 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700103 return VerifySignatureBlob(signature_blob, public_key_path,
104 kSignatureMessageCurrentVersion, out_hash_data);
105}
106
107bool PayloadVerifier::VerifySignatureBlob(
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800108 const chromeos::Blob& signature_blob,
Alex Deymof329b932014-10-30 01:37:48 -0700109 const string& public_key_path,
Alex Deymo923d8fa2014-07-15 17:58:51 -0700110 uint32_t client_version,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800111 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700112 TEST_AND_RETURN_FALSE(!public_key_path.empty());
113
114 Signatures signatures;
115 LOG(INFO) << "signature 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
119 // Finds a signature that matches the current version.
120 int sig_index = 0;
121 for (; sig_index < signatures.signatures_size(); sig_index++) {
122 const Signatures_Signature& signature = signatures.signatures(sig_index);
123 if (signature.has_version() &&
124 signature.version() == client_version) {
125 break;
126 }
127 }
128 TEST_AND_RETURN_FALSE(sig_index < signatures.signatures_size());
129
130 const Signatures_Signature& signature = signatures.signatures(sig_index);
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800131 chromeos::Blob sig_data(signature.data().begin(), signature.data().end());
Alex Deymo923d8fa2014-07-15 17:58:51 -0700132
133 return GetRawHashFromSignature(sig_data, public_key_path, out_hash_data);
134}
135
136
137bool PayloadVerifier::GetRawHashFromSignature(
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800138 const chromeos::Blob& sig_data,
Alex Deymof329b932014-10-30 01:37:48 -0700139 const string& public_key_path,
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800140 chromeos::Blob* out_hash_data) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700141 TEST_AND_RETURN_FALSE(!public_key_path.empty());
142
143 // The code below executes the equivalent of:
144 //
145 // openssl rsautl -verify -pubin -inkey |public_key_path|
146 // -in |sig_data| -out |out_hash_data|
147
148 // Loads the public key.
149 FILE* fpubkey = fopen(public_key_path.c_str(), "rb");
150 if (!fpubkey) {
151 LOG(ERROR) << "Unable to open public key file: " << public_key_path;
152 return false;
153 }
154
155 char dummy_password[] = { ' ', 0 }; // Ensure no password is read from stdin.
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700156 RSA* rsa = PEM_read_RSA_PUBKEY(fpubkey, nullptr, nullptr, dummy_password);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700157 fclose(fpubkey);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700158 TEST_AND_RETURN_FALSE(rsa != nullptr);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700159 unsigned int keysize = RSA_size(rsa);
160 if (sig_data.size() > 2 * keysize) {
161 LOG(ERROR) << "Signature size is too big for public key size.";
162 RSA_free(rsa);
163 return false;
164 }
165
166 // Decrypts the signature.
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800167 chromeos::Blob hash_data(keysize);
168 int decrypt_size = RSA_public_decrypt(sig_data.size(),
169 sig_data.data(),
170 hash_data.data(),
171 rsa,
172 RSA_NO_PADDING);
Alex Deymo923d8fa2014-07-15 17:58:51 -0700173 RSA_free(rsa);
174 TEST_AND_RETURN_FALSE(decrypt_size > 0 &&
175 decrypt_size <= static_cast<int>(hash_data.size()));
176 hash_data.resize(decrypt_size);
177 out_hash_data->swap(hash_data);
178 return true;
179}
180
Alex Deymof329b932014-10-30 01:37:48 -0700181bool PayloadVerifier::VerifySignedPayload(const string& payload_path,
182 const string& public_key_path,
Alex Deymo923d8fa2014-07-15 17:58:51 -0700183 uint32_t client_key_check_version) {
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800184 chromeos::Blob payload;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700185 DeltaArchiveManifest manifest;
186 uint64_t metadata_size;
187 TEST_AND_RETURN_FALSE(LoadPayload(
188 payload_path, &payload, &manifest, &metadata_size));
189 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
190 manifest.has_signatures_size());
191 CHECK_EQ(payload.size(),
192 metadata_size + manifest.signatures_offset() +
193 manifest.signatures_size());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800194 chromeos::Blob signature_blob(
Alex Deymo923d8fa2014-07-15 17:58:51 -0700195 payload.begin() + metadata_size + manifest.signatures_offset(),
196 payload.end());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800197 chromeos::Blob signed_hash;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700198 TEST_AND_RETURN_FALSE(VerifySignatureBlob(
199 signature_blob, public_key_path, client_key_check_version, &signed_hash));
200 TEST_AND_RETURN_FALSE(!signed_hash.empty());
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800201 chromeos::Blob hash;
Alex Deymo923d8fa2014-07-15 17:58:51 -0700202 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(
203 payload.data(), metadata_size + manifest.signatures_offset(), &hash));
204 PadRSA2048SHA256Hash(&hash);
205 TEST_AND_RETURN_FALSE(hash == signed_hash);
206 return true;
207}
208
Alex Vakulenkof68bbbc2015-02-09 12:53:18 -0800209bool PayloadVerifier::PadRSA2048SHA256Hash(chromeos::Blob* hash) {
Alex Deymo923d8fa2014-07-15 17:58:51 -0700210 TEST_AND_RETURN_FALSE(hash->size() == 32);
211 hash->insert(hash->begin(),
212 reinterpret_cast<const char*>(kRSA2048SHA256Padding),
213 reinterpret_cast<const char*>(kRSA2048SHA256Padding +
214 sizeof(kRSA2048SHA256Padding)));
215 TEST_AND_RETURN_FALSE(hash->size() == 256);
216 return true;
217}
218
219} // namespace chromeos_update_engine