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 <memory> 22 #include <utility> 23 24 #include <base/logging.h> 25 #include <base/strings/string_number_conversions.h> 26 #include <base/strings/string_split.h> 27 #include <base/strings/string_util.h> 28 #include <brillo/data_encoding.h> 29 #include <openssl/err.h> 30 #include <openssl/pem.h> 31 32 #include "update_engine/common/constants.h" 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_metadata.h" 39 #include "update_engine/payload_consumer/payload_verifier.h" 40 #include "update_engine/payload_generator/delta_diff_generator.h" 41 #include "update_engine/payload_generator/payload_file.h" 42 #include "update_engine/update_metadata.pb.h" 43 44 using std::string; 45 using std::vector; 46 47 namespace chromeos_update_engine { 48 49 namespace { 50 // Given raw |signatures|, packs them into a protobuf and serializes it into a 51 // string. Returns true on success, false otherwise. 52 bool ConvertSignaturesToProtobuf(const vector<brillo::Blob>& signatures, 53 const vector<size_t>& padded_signature_sizes, 54 string* out_serialized_signature) { 55 TEST_AND_RETURN_FALSE(signatures.size() == padded_signature_sizes.size()); 56 // Pack it into a protobuf 57 Signatures out_message; 58 for (size_t i = 0; i < signatures.size(); i++) { 59 const auto& signature = signatures[i]; 60 const auto& padded_signature_size = padded_signature_sizes[i]; 61 TEST_AND_RETURN_FALSE(padded_signature_size >= signature.size()); 62 Signatures::Signature* sig_message = out_message.add_signatures(); 63 // Skip assigning the same version number because we don't need to be 64 // compatible with old major version 1 client anymore. 65 66 // TODO(Xunchang) don't need to set the unpadded_signature_size field for 67 // RSA key signed signatures. 68 sig_message->set_unpadded_signature_size(signature.size()); 69 brillo::Blob padded_signature = signature; 70 padded_signature.insert( 71 padded_signature.end(), padded_signature_size - signature.size(), 0); 72 sig_message->set_data(padded_signature.data(), padded_signature.size()); 73 } 74 75 // Serialize protobuf 76 TEST_AND_RETURN_FALSE( 77 out_message.SerializeToString(out_serialized_signature)); 78 LOG(INFO) << "Signature blob size: " << out_serialized_signature->size(); 79 return true; 80 } 81 82 // Given an unsigned payload under |payload_path| and the |payload_signature| 83 // and |metadata_signature| generates an updated payload that includes the 84 // signatures. It populates |out_metadata_size| with the size of the final 85 // manifest after adding the dummy signature operation, and 86 // |out_signatures_offset| with the expected offset for the new blob, and 87 // |out_metadata_signature_size| which will be size of |metadata_signature| 88 // if the payload major version supports metadata signature, 0 otherwise. 89 // Returns true on success, false otherwise. 90 bool AddSignatureBlobToPayload(const string& payload_path, 91 const string& payload_signature, 92 const string& metadata_signature, 93 brillo::Blob* out_payload, 94 uint64_t* out_metadata_size, 95 uint32_t* out_metadata_signature_size, 96 uint64_t* out_signatures_offset) { 97 uint64_t manifest_offset = 20; 98 const int kProtobufSizeOffset = 12; 99 100 brillo::Blob payload; 101 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); 102 PayloadMetadata payload_metadata; 103 TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload)); 104 uint64_t metadata_size = payload_metadata.GetMetadataSize(); 105 uint32_t metadata_signature_size = 106 payload_metadata.GetMetadataSignatureSize(); 107 if (payload_metadata.GetMajorVersion() == kBrilloMajorPayloadVersion) { 108 // Write metadata signature size in header. 109 uint32_t metadata_signature_size_be = htobe32(metadata_signature.size()); 110 memcpy(payload.data() + manifest_offset, 111 &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.begin(), 119 metadata_signature.end()); 120 metadata_signature_size = metadata_signature.size(); 121 LOG(INFO) << "Metadata signature size: " << metadata_signature_size; 122 } 123 124 DeltaArchiveManifest manifest; 125 TEST_AND_RETURN_FALSE(payload_metadata.GetManifest(payload, &manifest)); 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() != payload_signature.size()) { 134 LOG(ERROR) << "Attempt to insert different signature sized blob. " 135 << "(current:" << manifest.signatures_size() 136 << "new:" << payload_signature.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 payload_signature.size(), 146 payload_metadata.GetMajorVersion() == 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 = 168 metadata_size + metadata_signature_size + manifest.signatures_offset(); 169 LOG(INFO) << "Signature Blob Offset: " << signatures_offset; 170 payload.resize(signatures_offset); 171 payload.insert(payload.begin() + signatures_offset, 172 payload_signature.begin(), 173 payload_signature.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(HashCalculator::RawHashOfBytes( 195 payload.data(), metadata_size, out_metadata_hash)); 196 } 197 if (out_hash_data) { 198 // Calculates the hash on the updated payload. Note that we skip metadata 199 // signature and payload signature. 200 HashCalculator calc; 201 TEST_AND_RETURN_FALSE(calc.Update(payload.data(), metadata_size)); 202 TEST_AND_RETURN_FALSE(signatures_offset >= 203 metadata_size + metadata_signature_size); 204 TEST_AND_RETURN_FALSE(calc.Update( 205 payload.data() + metadata_size + metadata_signature_size, 206 signatures_offset - metadata_size - metadata_signature_size)); 207 TEST_AND_RETURN_FALSE(calc.Finalize()); 208 *out_hash_data = calc.raw_hash(); 209 } 210 return true; 211 } 212 213 std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)> CreatePrivateKeyFromPath( 214 const string& private_key_path) { 215 FILE* fprikey = fopen(private_key_path.c_str(), "rb"); 216 if (!fprikey) { 217 PLOG(ERROR) << "Failed to read " << private_key_path; 218 return {nullptr, nullptr}; 219 } 220 221 auto private_key = std::unique_ptr<EVP_PKEY, decltype(&EVP_PKEY_free)>( 222 PEM_read_PrivateKey(fprikey, nullptr, nullptr, nullptr), EVP_PKEY_free); 223 fclose(fprikey); 224 return private_key; 225 } 226 227 } // namespace 228 229 bool PayloadSigner::GetMaximumSignatureSize(const string& private_key_path, 230 size_t* signature_size) { 231 *signature_size = 0; 232 auto private_key = CreatePrivateKeyFromPath(private_key_path); 233 if (!private_key) { 234 LOG(ERROR) << "Failed to create private key from " << private_key_path; 235 return false; 236 } 237 238 *signature_size = EVP_PKEY_size(private_key.get()); 239 return true; 240 } 241 242 void PayloadSigner::AddSignatureToManifest(uint64_t signature_blob_offset, 243 uint64_t signature_blob_length, 244 bool add_dummy_op, 245 DeltaArchiveManifest* manifest) { 246 LOG(INFO) << "Making room for signature in file"; 247 manifest->set_signatures_offset(signature_blob_offset); 248 LOG(INFO) << "set? " << manifest->has_signatures_offset(); 249 manifest->set_signatures_offset(signature_blob_offset); 250 manifest->set_signatures_size(signature_blob_length); 251 // Add a dummy op at the end to appease older clients 252 if (add_dummy_op) { 253 InstallOperation* dummy_op = manifest->add_kernel_install_operations(); 254 dummy_op->set_type(InstallOperation::REPLACE); 255 dummy_op->set_data_offset(signature_blob_offset); 256 dummy_op->set_data_length(signature_blob_length); 257 Extent* dummy_extent = dummy_op->add_dst_extents(); 258 // Tell the dummy op to write this data to a big sparse hole 259 dummy_extent->set_start_block(kSparseHole); 260 dummy_extent->set_num_blocks( 261 utils::DivRoundUp(signature_blob_length, kBlockSize)); 262 } 263 } 264 265 bool PayloadSigner::VerifySignedPayload(const string& payload_path, 266 const string& public_key_path) { 267 brillo::Blob payload; 268 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); 269 PayloadMetadata payload_metadata; 270 TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload)); 271 DeltaArchiveManifest manifest; 272 TEST_AND_RETURN_FALSE(payload_metadata.GetManifest(payload, &manifest)); 273 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() && 274 manifest.has_signatures_size()); 275 uint64_t metadata_size = payload_metadata.GetMetadataSize(); 276 uint32_t metadata_signature_size = 277 payload_metadata.GetMetadataSignatureSize(); 278 uint64_t signatures_offset = 279 metadata_size + metadata_signature_size + manifest.signatures_offset(); 280 CHECK_EQ(payload.size(), signatures_offset + manifest.signatures_size()); 281 brillo::Blob payload_hash, metadata_hash; 282 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 283 metadata_size, 284 metadata_signature_size, 285 signatures_offset, 286 &payload_hash, 287 &metadata_hash)); 288 string signature(payload.begin() + signatures_offset, payload.end()); 289 string public_key; 290 TEST_AND_RETURN_FALSE(utils::ReadFile(public_key_path, &public_key)); 291 TEST_AND_RETURN_FALSE(payload_hash.size() == kSHA256Size); 292 293 auto payload_verifier = PayloadVerifier::CreateInstance(public_key); 294 TEST_AND_RETURN_FALSE(payload_verifier != nullptr); 295 296 TEST_AND_RETURN_FALSE( 297 payload_verifier->VerifySignature(signature, payload_hash)); 298 if (metadata_signature_size) { 299 signature.assign(payload.begin() + metadata_size, 300 payload.begin() + metadata_size + metadata_signature_size); 301 TEST_AND_RETURN_FALSE(metadata_hash.size() == kSHA256Size); 302 TEST_AND_RETURN_FALSE( 303 payload_verifier->VerifySignature(signature, metadata_hash)); 304 } 305 return true; 306 } 307 308 bool PayloadSigner::SignHash(const brillo::Blob& hash, 309 const string& private_key_path, 310 brillo::Blob* out_signature) { 311 LOG(INFO) << "Signing hash with private key: " << private_key_path; 312 // We expect unpadded SHA256 hash coming in 313 TEST_AND_RETURN_FALSE(hash.size() == kSHA256Size); 314 // The code below executes the equivalent of: 315 // 316 // openssl rsautl -raw -sign -inkey |private_key_path| 317 // -in |padded_hash| -out |out_signature| 318 319 auto private_key = CreatePrivateKeyFromPath(private_key_path); 320 if (!private_key) { 321 LOG(ERROR) << "Failed to create private key from " << private_key_path; 322 return false; 323 } 324 325 int key_type = EVP_PKEY_id(private_key.get()); 326 brillo::Blob signature; 327 if (key_type == EVP_PKEY_RSA) { 328 RSA* rsa = EVP_PKEY_get0_RSA(private_key.get()); 329 TEST_AND_RETURN_FALSE(rsa != nullptr); 330 331 brillo::Blob padded_hash = hash; 332 PayloadVerifier::PadRSASHA256Hash(&padded_hash, RSA_size(rsa)); 333 334 signature.resize(RSA_size(rsa)); 335 ssize_t signature_size = RSA_private_encrypt(padded_hash.size(), 336 padded_hash.data(), 337 signature.data(), 338 rsa, 339 RSA_NO_PADDING); 340 341 if (signature_size < 0) { 342 LOG(ERROR) << "Signing hash failed: " 343 << ERR_error_string(ERR_get_error(), nullptr); 344 return false; 345 } 346 TEST_AND_RETURN_FALSE(static_cast<size_t>(signature_size) == 347 signature.size()); 348 } else if (key_type == EVP_PKEY_EC) { 349 EC_KEY* ec_key = EVP_PKEY_get0_EC_KEY(private_key.get()); 350 TEST_AND_RETURN_FALSE(ec_key != nullptr); 351 352 signature.resize(ECDSA_size(ec_key)); 353 unsigned int signature_size; 354 if (ECDSA_sign(0, 355 hash.data(), 356 hash.size(), 357 signature.data(), 358 &signature_size, 359 ec_key) != 1) { 360 LOG(ERROR) << "Signing hash failed: " 361 << ERR_error_string(ERR_get_error(), nullptr); 362 return false; 363 } 364 365 // NIST P-256 366 LOG(ERROR) << "signature max size " << signature.size() << " size " 367 << signature_size; 368 TEST_AND_RETURN_FALSE(signature.size() >= signature_size); 369 signature.resize(signature_size); 370 } else { 371 LOG(ERROR) << "key_type " << key_type << " isn't supported for signing"; 372 return false; 373 } 374 out_signature->swap(signature); 375 return true; 376 } 377 378 bool PayloadSigner::SignHashWithKeys(const brillo::Blob& hash_data, 379 const vector<string>& private_key_paths, 380 string* out_serialized_signature) { 381 vector<brillo::Blob> signatures; 382 vector<size_t> padded_signature_sizes; 383 for (const string& path : private_key_paths) { 384 brillo::Blob signature; 385 TEST_AND_RETURN_FALSE(SignHash(hash_data, path, &signature)); 386 signatures.push_back(signature); 387 388 size_t padded_signature_size; 389 TEST_AND_RETURN_FALSE( 390 GetMaximumSignatureSize(path, &padded_signature_size)); 391 padded_signature_sizes.push_back(padded_signature_size); 392 } 393 TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf( 394 signatures, padded_signature_sizes, out_serialized_signature)); 395 return true; 396 } 397 398 bool PayloadSigner::SignPayload(const string& unsigned_payload_path, 399 const vector<string>& private_key_paths, 400 const uint64_t metadata_size, 401 const uint32_t metadata_signature_size, 402 const uint64_t signatures_offset, 403 string* out_serialized_signature) { 404 brillo::Blob payload; 405 TEST_AND_RETURN_FALSE(utils::ReadFile(unsigned_payload_path, &payload)); 406 brillo::Blob hash_data; 407 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 408 metadata_size, 409 metadata_signature_size, 410 signatures_offset, 411 &hash_data, 412 nullptr)); 413 TEST_AND_RETURN_FALSE( 414 SignHashWithKeys(hash_data, private_key_paths, out_serialized_signature)); 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 hash_blob; 422 TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfData({'x'}, &hash_blob)); 423 string sig_blob; 424 TEST_AND_RETURN_FALSE( 425 SignHashWithKeys(hash_blob, private_key_paths, &sig_blob)); 426 *out_length = sig_blob.size(); 427 return true; 428 } 429 430 bool PayloadSigner::HashPayloadForSigning(const string& payload_path, 431 const vector<size_t>& signature_sizes, 432 brillo::Blob* out_payload_hash_data, 433 brillo::Blob* out_metadata_hash) { 434 // Create a signature blob with signatures filled with 0. 435 // Will be used for both payload signature and metadata signature. 436 vector<brillo::Blob> signatures; 437 for (int signature_size : signature_sizes) { 438 signatures.emplace_back(signature_size, 0); 439 } 440 string signature; 441 TEST_AND_RETURN_FALSE( 442 ConvertSignaturesToProtobuf(signatures, signature_sizes, &signature)); 443 444 brillo::Blob payload; 445 uint64_t metadata_size, signatures_offset; 446 uint32_t metadata_signature_size; 447 // Prepare payload for hashing. 448 TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path, 449 signature, 450 signature, 451 &payload, 452 &metadata_size, 453 &metadata_signature_size, 454 &signatures_offset)); 455 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 456 metadata_size, 457 metadata_signature_size, 458 signatures_offset, 459 out_payload_hash_data, 460 out_metadata_hash)); 461 return true; 462 } 463 464 bool PayloadSigner::AddSignatureToPayload( 465 const string& payload_path, 466 const vector<size_t>& padded_signature_sizes, 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 string payload_signature, metadata_signature; 475 TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf( 476 payload_signatures, padded_signature_sizes, &payload_signature)); 477 if (!metadata_signatures.empty()) { 478 TEST_AND_RETURN_FALSE(ConvertSignaturesToProtobuf( 479 metadata_signatures, padded_signature_sizes, &metadata_signature)); 480 } 481 brillo::Blob payload; 482 uint64_t signatures_offset; 483 uint32_t metadata_signature_size; 484 TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path, 485 payload_signature, 486 metadata_signature, 487 &payload, 488 out_metadata_size, 489 &metadata_signature_size, 490 &signatures_offset)); 491 492 LOG(INFO) << "Signed payload size: " << payload.size(); 493 TEST_AND_RETURN_FALSE(utils::WriteFile( 494 signed_payload_path.c_str(), payload.data(), payload.size())); 495 return true; 496 } 497 498 bool 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( 506 HashCalculator::RawHashOfBytes(metadata, metadata_size, &metadata_hash)); 507 508 brillo::Blob signature; 509 TEST_AND_RETURN_FALSE(SignHash(metadata_hash, private_key_path, &signature)); 510 511 *out_signature = brillo::data_encoding::Base64Encode(signature); 512 return true; 513 } 514 515 bool PayloadSigner::ExtractPayloadProperties( 516 const string& payload_path, brillo::KeyValueStore* properties) { 517 brillo::Blob payload; 518 TEST_AND_RETURN_FALSE( 519 utils::ReadFileChunk(payload_path, 0, kMaxPayloadHeaderSize, &payload)); 520 521 PayloadMetadata payload_metadata; 522 TEST_AND_RETURN_FALSE(payload_metadata.ParsePayloadHeader(payload)); 523 uint64_t metadata_size = payload_metadata.GetMetadataSize(); 524 525 uint64_t file_size = utils::FileSize(payload_path); 526 properties->SetString(kPayloadPropertyFileSize, std::to_string(file_size)); 527 properties->SetString(kPayloadPropertyMetadataSize, 528 std::to_string(metadata_size)); 529 530 brillo::Blob file_hash, metadata_hash; 531 TEST_AND_RETURN_FALSE( 532 HashCalculator::RawHashOfFile(payload_path, file_size, &file_hash) == 533 static_cast<off_t>(file_size)); 534 535 TEST_AND_RETURN_FALSE(HashCalculator::RawHashOfFile( 536 payload_path, metadata_size, &metadata_hash) == 537 static_cast<off_t>(metadata_size)); 538 539 properties->SetString(kPayloadPropertyFileHash, 540 brillo::data_encoding::Base64Encode(file_hash)); 541 properties->SetString(kPayloadPropertyMetadataHash, 542 brillo::data_encoding::Base64Encode(metadata_hash)); 543 return true; 544 } 545 546 } // namespace chromeos_update_engine 547