payload_signer.cc revision 3e728feb92bfd5514bff55aa7dc455da80e18906
1// 2// Copyright (C) 2011 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// 16 17#include "update_engine/payload_generator/payload_signer.h" 18 19#include <endian.h> 20 21#include <base/logging.h> 22#include <base/strings/string_split.h> 23#include <base/strings/string_util.h> 24#include <brillo/data_encoding.h> 25#include <openssl/pem.h> 26 27#include "update_engine/delta_performer.h" 28#include "update_engine/omaha_hash_calculator.h" 29#include "update_engine/payload_constants.h" 30#include "update_engine/payload_generator/delta_diff_generator.h" 31#include "update_engine/payload_generator/payload_file.h" 32#include "update_engine/payload_verifier.h" 33#include "update_engine/subprocess.h" 34#include "update_engine/update_metadata.pb.h" 35#include "update_engine/utils.h" 36 37using std::string; 38using std::vector; 39 40namespace chromeos_update_engine { 41 42namespace { 43 44// The payload verifier will check all the signatures included in the payload 45// regardless of the version field. Old version of the verifier require the 46// version field to be included and be 1. 47const uint32_t kSignatureMessageLegacyVersion = 1; 48 49// Given raw |signatures|, packs them into a protobuf and serializes it into a 50// binary blob. Returns true on success, false otherwise. 51bool ConvertSignatureToProtobufBlob(const vector<brillo::Blob>& signatures, 52 brillo::Blob* out_signature_blob) { 53 // Pack it into a protobuf 54 Signatures out_message; 55 for (const brillo::Blob& signature : signatures) { 56 Signatures_Signature* sig_message = out_message.add_signatures(); 57 // Set all the signatures with the same version number. 58 sig_message->set_version(kSignatureMessageLegacyVersion); 59 sig_message->set_data(signature.data(), signature.size()); 60 } 61 62 // Serialize protobuf 63 string serialized; 64 TEST_AND_RETURN_FALSE(out_message.AppendToString(&serialized)); 65 out_signature_blob->insert(out_signature_blob->end(), 66 serialized.begin(), 67 serialized.end()); 68 LOG(INFO) << "Signature blob size: " << out_signature_blob->size(); 69 return true; 70} 71 72// Given an unsigned payload under |payload_path| and the |signature_blob| and 73// |metadata_signature_blob| generates an updated payload that includes the 74// signatures. It populates |out_metadata_size| with the size of the final 75// manifest after adding the dummy signature operation, and 76// |out_signatures_offset| with the expected offset for the new blob, and 77// |out_metadata_signature_size| which will be size of |metadata_signature_blob| 78// if the payload major version supports metadata signature, 0 otherwise. 79// Returns true on success, false otherwise. 80bool AddSignatureBlobToPayload(const string& payload_path, 81 const brillo::Blob& signature_blob, 82 const brillo::Blob& metadata_signature_blob, 83 brillo::Blob* out_payload, 84 uint64_t* out_metadata_size, 85 uint32_t* out_metadata_signature_size, 86 uint64_t* out_signatures_offset) { 87 uint64_t manifest_offset = 20; 88 const int kProtobufSizeOffset = 12; 89 90 // Loads the payload. 91 brillo::Blob payload; 92 DeltaArchiveManifest manifest; 93 uint64_t metadata_size, major_version; 94 uint32_t metadata_signature_size; 95 TEST_AND_RETURN_FALSE(PayloadSigner::LoadPayload(payload_path, &payload, 96 &manifest, &major_version, &metadata_size, &metadata_signature_size)); 97 98 if (major_version == kBrilloMajorPayloadVersion) { 99 // Write metadata signature size in header. 100 uint32_t metadata_signature_size_be = 101 htobe32(metadata_signature_blob.size()); 102 memcpy(payload.data() + manifest_offset, &metadata_signature_size_be, 103 sizeof(metadata_signature_size_be)); 104 manifest_offset += sizeof(metadata_signature_size_be); 105 // Replace metadata signature. 106 payload.erase(payload.begin() + metadata_size, 107 payload.begin() + metadata_size + metadata_signature_size); 108 payload.insert(payload.begin() + metadata_size, 109 metadata_signature_blob.begin(), 110 metadata_signature_blob.end()); 111 metadata_signature_size = metadata_signature_blob.size(); 112 LOG(INFO) << "Metadata signature size: " << metadata_signature_size; 113 } 114 115 // Is there already a signature op in place? 116 if (manifest.has_signatures_size()) { 117 // The signature op is tied to the size of the signature blob, but not it's 118 // contents. We don't allow the manifest to change if there is already an op 119 // present, because that might invalidate previously generated 120 // hashes/signatures. 121 if (manifest.signatures_size() != signature_blob.size()) { 122 LOG(ERROR) << "Attempt to insert different signature sized blob. " 123 << "(current:" << manifest.signatures_size() 124 << "new:" << signature_blob.size() << ")"; 125 return false; 126 } 127 128 LOG(INFO) << "Matching signature sizes already present."; 129 } else { 130 // Updates the manifest to include the signature operation. 131 PayloadSigner::AddSignatureToManifest( 132 payload.size() - metadata_size - metadata_signature_size, 133 signature_blob.size(), 134 major_version == kChromeOSMajorPayloadVersion, 135 &manifest); 136 137 // Updates the payload to include the new manifest. 138 string serialized_manifest; 139 TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest)); 140 LOG(INFO) << "Updated protobuf size: " << serialized_manifest.size(); 141 payload.erase(payload.begin() + manifest_offset, 142 payload.begin() + metadata_size); 143 payload.insert(payload.begin() + manifest_offset, 144 serialized_manifest.begin(), 145 serialized_manifest.end()); 146 147 // Updates the protobuf size. 148 uint64_t size_be = htobe64(serialized_manifest.size()); 149 memcpy(&payload[kProtobufSizeOffset], &size_be, sizeof(size_be)); 150 metadata_size = serialized_manifest.size() + manifest_offset; 151 152 LOG(INFO) << "Updated payload size: " << payload.size(); 153 LOG(INFO) << "Updated metadata size: " << metadata_size; 154 } 155 uint64_t signatures_offset = metadata_size + metadata_signature_size + 156 manifest.signatures_offset(); 157 LOG(INFO) << "Signature Blob Offset: " << signatures_offset; 158 payload.resize(signatures_offset); 159 payload.insert(payload.begin() + signatures_offset, 160 signature_blob.begin(), 161 signature_blob.end()); 162 163 *out_payload = std::move(payload); 164 *out_metadata_size = metadata_size; 165 *out_metadata_signature_size = metadata_signature_size; 166 *out_signatures_offset = signatures_offset; 167 return true; 168} 169 170// Given a |payload| with correct signature op and metadata signature size in 171// header and |metadata_size|, |metadata_signature_size|, |signatures_offset|, 172// calculate hash for payload and metadata, save it to |out_hash_data| and 173// |out_metadata_hash|. 174bool CalculateHashFromPayload(const brillo::Blob& payload, 175 const uint64_t metadata_size, 176 const uint32_t metadata_signature_size, 177 const uint64_t signatures_offset, 178 brillo::Blob* out_hash_data, 179 brillo::Blob* out_metadata_hash) { 180 if (out_metadata_hash) { 181 // Calculates the hash on the manifest. 182 TEST_AND_RETURN_FALSE( 183 OmahaHashCalculator::RawHashOfBytes(payload.data(), metadata_size, 184 out_metadata_hash)); 185 } 186 if (out_hash_data) { 187 // Calculates the hash on the updated payload. Note that we skip metadata 188 // signature and payload signature. 189 OmahaHashCalculator calc; 190 TEST_AND_RETURN_FALSE(calc.Update(payload.data(), metadata_size)); 191 TEST_AND_RETURN_FALSE(calc.Update( 192 payload.data() + metadata_size + metadata_signature_size, 193 signatures_offset - metadata_size - metadata_signature_size)); 194 TEST_AND_RETURN_FALSE(calc.Finalize()); 195 *out_hash_data = calc.raw_hash(); 196 } 197 return true; 198} 199 200} // namespace 201 202void PayloadSigner::AddSignatureToManifest(uint64_t signature_blob_offset, 203 uint64_t signature_blob_length, 204 bool add_dummy_op, 205 DeltaArchiveManifest* manifest) { 206 LOG(INFO) << "Making room for signature in file"; 207 manifest->set_signatures_offset(signature_blob_offset); 208 LOG(INFO) << "set? " << manifest->has_signatures_offset(); 209 manifest->set_signatures_offset(signature_blob_offset); 210 manifest->set_signatures_size(signature_blob_length); 211 // Add a dummy op at the end to appease older clients 212 if (add_dummy_op) { 213 InstallOperation* dummy_op = manifest->add_kernel_install_operations(); 214 dummy_op->set_type(InstallOperation::REPLACE); 215 dummy_op->set_data_offset(signature_blob_offset); 216 dummy_op->set_data_length(signature_blob_length); 217 Extent* dummy_extent = dummy_op->add_dst_extents(); 218 // Tell the dummy op to write this data to a big sparse hole 219 dummy_extent->set_start_block(kSparseHole); 220 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) / 221 kBlockSize); 222 } 223} 224 225bool PayloadSigner::LoadPayload(const std::string& payload_path, 226 brillo::Blob* out_payload, 227 DeltaArchiveManifest* out_manifest, 228 uint64_t* out_major_version, 229 uint64_t* out_metadata_size, 230 uint32_t* out_metadata_signature_size) { 231 brillo::Blob payload; 232 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); 233 TEST_AND_RETURN_FALSE(payload.size() >= 234 DeltaPerformer::kMaxPayloadHeaderSize); 235 const uint8_t* read_pointer = payload.data(); 236 TEST_AND_RETURN_FALSE( 237 memcmp(read_pointer, kDeltaMagic, sizeof(kDeltaMagic)) == 0); 238 read_pointer += sizeof(kDeltaMagic); 239 240 uint64_t major_version; 241 memcpy(&major_version, read_pointer, sizeof(major_version)); 242 read_pointer += sizeof(major_version); 243 major_version = be64toh(major_version); 244 TEST_AND_RETURN_FALSE(major_version == kChromeOSMajorPayloadVersion || 245 major_version == kBrilloMajorPayloadVersion); 246 if (out_major_version) 247 *out_major_version = major_version; 248 249 uint64_t manifest_size = 0; 250 memcpy(&manifest_size, read_pointer, sizeof(manifest_size)); 251 read_pointer += sizeof(manifest_size); 252 manifest_size = be64toh(manifest_size); 253 254 uint32_t metadata_signature_size = 0; 255 if (major_version == kBrilloMajorPayloadVersion) { 256 memcpy(&metadata_signature_size, read_pointer, 257 sizeof(metadata_signature_size)); 258 read_pointer += sizeof(metadata_signature_size); 259 metadata_signature_size = be32toh(metadata_signature_size); 260 } 261 if (out_metadata_signature_size) 262 *out_metadata_signature_size = metadata_signature_size; 263 264 *out_metadata_size = read_pointer - payload.data() + manifest_size; 265 TEST_AND_RETURN_FALSE(payload.size() >= *out_metadata_size); 266 if (out_manifest) 267 TEST_AND_RETURN_FALSE( 268 out_manifest->ParseFromArray(read_pointer, manifest_size)); 269 *out_payload = std::move(payload); 270 return true; 271} 272 273bool PayloadSigner::VerifySignedPayload(const string& payload_path, 274 const string& public_key_path) { 275 brillo::Blob payload; 276 DeltaArchiveManifest manifest; 277 uint64_t metadata_size; 278 uint32_t metadata_signature_size; 279 TEST_AND_RETURN_FALSE(LoadPayload(payload_path, &payload, &manifest, nullptr, 280 &metadata_size, &metadata_signature_size)); 281 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() && 282 manifest.has_signatures_size()); 283 uint64_t signatures_offset = metadata_size + metadata_signature_size + 284 manifest.signatures_offset(); 285 CHECK_EQ(payload.size(), signatures_offset + manifest.signatures_size()); 286 brillo::Blob payload_hash, metadata_hash; 287 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 288 metadata_size, 289 metadata_signature_size, 290 signatures_offset, 291 &payload_hash, 292 &metadata_hash)); 293 brillo::Blob signature_blob(payload.begin() + signatures_offset, 294 payload.end()); 295 TEST_AND_RETURN_FALSE(PayloadVerifier::PadRSA2048SHA256Hash(&payload_hash)); 296 TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature( 297 signature_blob, public_key_path, payload_hash)); 298 if (metadata_signature_size) { 299 signature_blob.assign(payload.begin() + metadata_size, 300 payload.begin() + metadata_size + 301 metadata_signature_size); 302 TEST_AND_RETURN_FALSE( 303 PayloadVerifier::PadRSA2048SHA256Hash(&metadata_hash)); 304 TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature( 305 signature_blob, public_key_path, metadata_hash)); 306 } 307 return true; 308} 309 310bool PayloadSigner::SignHash(const brillo::Blob& hash, 311 const string& private_key_path, 312 brillo::Blob* out_signature) { 313 LOG(INFO) << "Signing hash with private key: " << private_key_path; 314 string sig_path; 315 TEST_AND_RETURN_FALSE( 316 utils::MakeTempFile("signature.XXXXXX", &sig_path, nullptr)); 317 ScopedPathUnlinker sig_path_unlinker(sig_path); 318 319 string hash_path; 320 TEST_AND_RETURN_FALSE( 321 utils::MakeTempFile("hash.XXXXXX", &hash_path, nullptr)); 322 ScopedPathUnlinker hash_path_unlinker(hash_path); 323 // We expect unpadded SHA256 hash coming in 324 TEST_AND_RETURN_FALSE(hash.size() == 32); 325 brillo::Blob padded_hash(hash); 326 PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash); 327 TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(), 328 padded_hash.data(), 329 padded_hash.size())); 330 331 // This runs on the server, so it's okay to copy out and call openssl 332 // executable rather than properly use the library. 333 vector<string> cmd = {"openssl", "rsautl", "-raw", "-sign", "-inkey", 334 private_key_path, "-in", hash_path, "-out", sig_path}; 335 int return_code = 0; 336 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code, 337 nullptr)); 338 TEST_AND_RETURN_FALSE(return_code == 0); 339 340 brillo::Blob signature; 341 TEST_AND_RETURN_FALSE(utils::ReadFile(sig_path, &signature)); 342 out_signature->swap(signature); 343 return true; 344} 345 346bool PayloadSigner::SignHashWithKeys(const brillo::Blob& hash_data, 347 const vector<string>& private_key_paths, 348 brillo::Blob* out_signature_blob) { 349 vector<brillo::Blob> signatures; 350 for (const string& path : private_key_paths) { 351 brillo::Blob signature; 352 TEST_AND_RETURN_FALSE(SignHash(hash_data, path, &signature)); 353 signatures.push_back(signature); 354 } 355 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 356 out_signature_blob)); 357 return true; 358} 359 360bool PayloadSigner::SignPayload(const string& unsigned_payload_path, 361 const vector<string>& private_key_paths, 362 const uint64_t metadata_size, 363 const uint32_t metadata_signature_size, 364 const uint64_t signatures_offset, 365 brillo::Blob* out_signature_blob) { 366 brillo::Blob payload; 367 TEST_AND_RETURN_FALSE(utils::ReadFile(unsigned_payload_path, &payload)); 368 brillo::Blob hash_data; 369 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 370 metadata_size, 371 metadata_signature_size, 372 signatures_offset, 373 &hash_data, 374 nullptr)); 375 TEST_AND_RETURN_FALSE(SignHashWithKeys(hash_data, 376 private_key_paths, 377 out_signature_blob)); 378 return true; 379} 380 381bool PayloadSigner::SignatureBlobLength(const vector<string>& private_key_paths, 382 uint64_t* out_length) { 383 DCHECK(out_length); 384 brillo::Blob x_blob(1, 'x'), hash_blob, sig_blob; 385 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(x_blob.data(), 386 x_blob.size(), 387 &hash_blob)); 388 TEST_AND_RETURN_FALSE( 389 SignHashWithKeys(hash_blob, private_key_paths, &sig_blob)); 390 *out_length = sig_blob.size(); 391 return true; 392} 393 394bool PayloadSigner::HashPayloadForSigning(const string& payload_path, 395 const vector<int>& signature_sizes, 396 brillo::Blob* out_payload_hash_data, 397 brillo::Blob* out_metadata_hash) { 398 // Create a signature blob with signatures filled with 0. 399 // Will be used for both payload signature and metadata signature. 400 vector<brillo::Blob> signatures; 401 for (int signature_size : signature_sizes) { 402 signatures.emplace_back(signature_size, 0); 403 } 404 brillo::Blob signature_blob; 405 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 406 &signature_blob)); 407 408 brillo::Blob payload; 409 uint64_t metadata_size, signatures_offset; 410 uint32_t metadata_signature_size; 411 // Prepare payload for hashing. 412 TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path, 413 signature_blob, 414 signature_blob, 415 &payload, 416 &metadata_size, 417 &metadata_signature_size, 418 &signatures_offset)); 419 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 420 metadata_size, 421 metadata_signature_size, 422 signatures_offset, 423 out_payload_hash_data, 424 out_metadata_hash)); 425 return true; 426} 427 428bool PayloadSigner::AddSignatureToPayload( 429 const string& payload_path, 430 const vector<brillo::Blob>& payload_signatures, 431 const vector<brillo::Blob>& metadata_signatures, 432 const string& signed_payload_path, 433 uint64_t *out_metadata_size) { 434 // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory. 435 436 // Loads the payload and adds the signature op to it. 437 brillo::Blob signature_blob, metadata_signature_blob; 438 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(payload_signatures, 439 &signature_blob)); 440 if (!metadata_signatures.empty()) { 441 TEST_AND_RETURN_FALSE( 442 ConvertSignatureToProtobufBlob(metadata_signatures, 443 &metadata_signature_blob)); 444 } 445 brillo::Blob payload; 446 uint64_t signatures_offset; 447 uint32_t metadata_signature_size; 448 TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path, 449 signature_blob, 450 metadata_signature_blob, 451 &payload, 452 out_metadata_size, 453 &metadata_signature_size, 454 &signatures_offset)); 455 456 LOG(INFO) << "Signed payload size: " << payload.size(); 457 TEST_AND_RETURN_FALSE(utils::WriteFile(signed_payload_path.c_str(), 458 payload.data(), 459 payload.size())); 460 return true; 461} 462 463bool PayloadSigner::GetMetadataSignature(const void* const metadata, 464 size_t metadata_size, 465 const string& private_key_path, 466 string* out_signature) { 467 // Calculates the hash on the updated payload. Note that the payload includes 468 // the signature op but doesn't include the signature blob at the end. 469 brillo::Blob metadata_hash; 470 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(metadata, 471 metadata_size, 472 &metadata_hash)); 473 474 brillo::Blob signature; 475 TEST_AND_RETURN_FALSE(SignHash(metadata_hash, 476 private_key_path, 477 &signature)); 478 479 *out_signature = brillo::data_encoding::Base64Encode(signature); 480 return true; 481} 482 483 484} // namespace chromeos_update_engine 485