blob: fa23d4f6e007b29683e0b22f97a2cb42dd022a08 [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"
12#include "update_engine/payload_generator/delta_diff_generator.h"
13#include "update_engine/update_metadata.pb.h"
14#include "update_engine/utils.h"
15
16using std::string;
17using std::vector;
18
19namespace chromeos_update_engine {
20
21const uint32_t kSignatureMessageOriginalVersion = 1;
22const uint32_t kSignatureMessageCurrentVersion = 1;
23
24namespace {
25
26// The following is a standard PKCS1-v1_5 padding for SHA256 signatures, as
27// defined in RFC3447. It is prepended to the actual signature (32 bytes) to
28// form a sequence of 256 bytes (2048 bits) that is amenable to RSA signing. The
29// padded hash will look as follows:
30//
31// 0x00 0x01 0xff ... 0xff 0x00 ASN1HEADER SHA256HASH
32// |--------------205-----------||----19----||----32----|
33//
34// where ASN1HEADER is the ASN.1 description of the signed data. The complete 51
35// bytes of actual data (i.e. the ASN.1 header complete with the hash) are
36// packed as follows:
37//
38// SEQUENCE(2+49) {
39// SEQUENCE(2+13) {
40// OBJECT(2+9) id-sha256
41// NULL(2+0)
42// }
43// OCTET STRING(2+32) <actual signature bytes...>
44// }
45const unsigned char kRSA2048SHA256Padding[] = {
46 // PKCS1-v1_5 padding
47 0x00, 0x01, 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, 0xff, 0xff, 0xff, 0xff,
71 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
72 0xff, 0xff, 0xff, 0xff, 0x00,
73 // ASN.1 header
74 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
75 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
76 0x00, 0x04, 0x20,
77};
78
79} // namespace
80
81bool PayloadVerifier::LoadPayload(const string& payload_path,
82 vector<char>* out_payload,
83 DeltaArchiveManifest* out_manifest,
84 uint64_t* out_metadata_size) {
85 vector<char> payload;
86 // Loads the payload and parses the manifest.
87 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload));
88 LOG(INFO) << "Payload size: " << payload.size();
89 ErrorCode error = ErrorCode::kSuccess;
90 InstallPlan install_plan;
91 DeltaPerformer delta_performer(NULL, NULL, &install_plan);
92 TEST_AND_RETURN_FALSE(
93 delta_performer.ParsePayloadMetadata(payload, &error) ==
94 DeltaPerformer::kMetadataParseSuccess);
95 TEST_AND_RETURN_FALSE(delta_performer.GetManifest(out_manifest));
96 *out_metadata_size = delta_performer.GetMetadataSize();
97 LOG(INFO) << "Metadata size: " << *out_metadata_size;
98 out_payload->swap(payload);
99 return true;
100}
101
102bool PayloadVerifier::VerifySignature(const std::vector<char>& signature_blob,
103 const std::string& public_key_path,
104 std::vector<char>* out_hash_data) {
105 return VerifySignatureBlob(signature_blob, public_key_path,
106 kSignatureMessageCurrentVersion, out_hash_data);
107}
108
109bool PayloadVerifier::VerifySignatureBlob(
110 const std::vector<char>& signature_blob,
111 const std::string& public_key_path,
112 uint32_t client_version,
113 std::vector<char>* out_hash_data) {
114 TEST_AND_RETURN_FALSE(!public_key_path.empty());
115
116 Signatures signatures;
117 LOG(INFO) << "signature size = " << signature_blob.size();
118 TEST_AND_RETURN_FALSE(signatures.ParseFromArray(&signature_blob[0],
119 signature_blob.size()));
120
121 // Finds a signature that matches the current version.
122 int sig_index = 0;
123 for (; sig_index < signatures.signatures_size(); sig_index++) {
124 const Signatures_Signature& signature = signatures.signatures(sig_index);
125 if (signature.has_version() &&
126 signature.version() == client_version) {
127 break;
128 }
129 }
130 TEST_AND_RETURN_FALSE(sig_index < signatures.signatures_size());
131
132 const Signatures_Signature& signature = signatures.signatures(sig_index);
133 vector<char> sig_data(signature.data().begin(), signature.data().end());
134
135 return GetRawHashFromSignature(sig_data, public_key_path, out_hash_data);
136}
137
138
139bool PayloadVerifier::GetRawHashFromSignature(
140 const std::vector<char>& sig_data,
141 const std::string& public_key_path,
142 std::vector<char>* out_hash_data) {
143 TEST_AND_RETURN_FALSE(!public_key_path.empty());
144
145 // The code below executes the equivalent of:
146 //
147 // openssl rsautl -verify -pubin -inkey |public_key_path|
148 // -in |sig_data| -out |out_hash_data|
149
150 // Loads the public key.
151 FILE* fpubkey = fopen(public_key_path.c_str(), "rb");
152 if (!fpubkey) {
153 LOG(ERROR) << "Unable to open public key file: " << public_key_path;
154 return false;
155 }
156
157 char dummy_password[] = { ' ', 0 }; // Ensure no password is read from stdin.
158 RSA* rsa = PEM_read_RSA_PUBKEY(fpubkey, NULL, NULL, dummy_password);
159 fclose(fpubkey);
160 TEST_AND_RETURN_FALSE(rsa != NULL);
161 unsigned int keysize = RSA_size(rsa);
162 if (sig_data.size() > 2 * keysize) {
163 LOG(ERROR) << "Signature size is too big for public key size.";
164 RSA_free(rsa);
165 return false;
166 }
167
168 // Decrypts the signature.
169 vector<char> hash_data(keysize);
170 int decrypt_size = RSA_public_decrypt(
171 sig_data.size(),
172 reinterpret_cast<const unsigned char*>(sig_data.data()),
173 reinterpret_cast<unsigned char*>(hash_data.data()),
174 rsa,
175 RSA_NO_PADDING);
176 RSA_free(rsa);
177 TEST_AND_RETURN_FALSE(decrypt_size > 0 &&
178 decrypt_size <= static_cast<int>(hash_data.size()));
179 hash_data.resize(decrypt_size);
180 out_hash_data->swap(hash_data);
181 return true;
182}
183
184bool PayloadVerifier::VerifySignedPayload(const std::string& payload_path,
185 const std::string& public_key_path,
186 uint32_t client_key_check_version) {
187 vector<char> payload;
188 DeltaArchiveManifest manifest;
189 uint64_t metadata_size;
190 TEST_AND_RETURN_FALSE(LoadPayload(
191 payload_path, &payload, &manifest, &metadata_size));
192 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() &&
193 manifest.has_signatures_size());
194 CHECK_EQ(payload.size(),
195 metadata_size + manifest.signatures_offset() +
196 manifest.signatures_size());
197 vector<char> signature_blob(
198 payload.begin() + metadata_size + manifest.signatures_offset(),
199 payload.end());
200 vector<char> signed_hash;
201 TEST_AND_RETURN_FALSE(VerifySignatureBlob(
202 signature_blob, public_key_path, client_key_check_version, &signed_hash));
203 TEST_AND_RETURN_FALSE(!signed_hash.empty());
204 vector<char> hash;
205 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(
206 payload.data(), metadata_size + manifest.signatures_offset(), &hash));
207 PadRSA2048SHA256Hash(&hash);
208 TEST_AND_RETURN_FALSE(hash == signed_hash);
209 return true;
210}
211
212bool PayloadVerifier::PadRSA2048SHA256Hash(std::vector<char>* hash) {
213 TEST_AND_RETURN_FALSE(hash->size() == 32);
214 hash->insert(hash->begin(),
215 reinterpret_cast<const char*>(kRSA2048SHA256Padding),
216 reinterpret_cast<const char*>(kRSA2048SHA256Padding +
217 sizeof(kRSA2048SHA256Padding)));
218 TEST_AND_RETURN_FALSE(hash->size() == 256);
219 return true;
220}
221
222} // namespace chromeos_update_engine