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