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