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