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