payload_signer.cc revision 644f618b8bbaa096c1fcdcf7d6ed06e2538ea83c
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::AddSignatureOp(payload.size() - metadata_size - 132 metadata_signature_size, 133 signature_blob.size(), 134 &manifest); 135 136 // Updates the payload to include the new manifest. 137 string serialized_manifest; 138 TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest)); 139 LOG(INFO) << "Updated protobuf size: " << serialized_manifest.size(); 140 payload.erase(payload.begin() + manifest_offset, 141 payload.begin() + metadata_size); 142 payload.insert(payload.begin() + manifest_offset, 143 serialized_manifest.begin(), 144 serialized_manifest.end()); 145 146 // Updates the protobuf size. 147 uint64_t size_be = htobe64(serialized_manifest.size()); 148 memcpy(&payload[kProtobufSizeOffset], &size_be, sizeof(size_be)); 149 metadata_size = serialized_manifest.size() + manifest_offset; 150 151 LOG(INFO) << "Updated payload size: " << payload.size(); 152 LOG(INFO) << "Updated metadata size: " << metadata_size; 153 } 154 uint64_t signatures_offset = metadata_size + metadata_signature_size + 155 manifest.signatures_offset(); 156 LOG(INFO) << "Signature Blob Offset: " << signatures_offset; 157 payload.resize(signatures_offset); 158 payload.insert(payload.begin() + signatures_offset, 159 signature_blob.begin(), 160 signature_blob.end()); 161 162 *out_payload = std::move(payload); 163 *out_metadata_size = metadata_size; 164 *out_metadata_signature_size = metadata_signature_size; 165 *out_signatures_offset = signatures_offset; 166 return true; 167} 168 169// Given a |payload| with correct signature op and metadata signature size in 170// header and |metadata_size|, |metadata_signature_size|, |signatures_offset|, 171// calculate hash for payload and metadata, save it to |out_hash_data| and 172// |out_metadata_hash|. 173bool CalculateHashFromPayload(const brillo::Blob& payload, 174 const uint64_t metadata_size, 175 const uint32_t metadata_signature_size, 176 const uint64_t signatures_offset, 177 brillo::Blob* out_hash_data, 178 brillo::Blob* out_metadata_hash) { 179 if (out_metadata_hash) { 180 // Calculates the hash on the manifest. 181 TEST_AND_RETURN_FALSE( 182 OmahaHashCalculator::RawHashOfBytes(payload.data(), metadata_size, 183 out_metadata_hash)); 184 } 185 if (out_hash_data) { 186 // Calculates the hash on the updated payload. Note that we skip metadata 187 // signature and payload signature. 188 OmahaHashCalculator calc; 189 TEST_AND_RETURN_FALSE(calc.Update(payload.data(), metadata_size)); 190 TEST_AND_RETURN_FALSE(calc.Update( 191 payload.data() + metadata_size + metadata_signature_size, 192 signatures_offset - metadata_size - metadata_signature_size)); 193 TEST_AND_RETURN_FALSE(calc.Finalize()); 194 *out_hash_data = calc.raw_hash(); 195 } 196 return true; 197} 198 199} // namespace 200 201void PayloadSigner::AddSignatureOp(uint64_t signature_blob_offset, 202 uint64_t signature_blob_length, 203 DeltaArchiveManifest* manifest) { 204 LOG(INFO) << "Making room for signature in file"; 205 manifest->set_signatures_offset(signature_blob_offset); 206 LOG(INFO) << "set? " << manifest->has_signatures_offset(); 207 // Add a dummy op at the end to appease older clients 208 InstallOperation* dummy_op = manifest->add_kernel_install_operations(); 209 dummy_op->set_type(InstallOperation::REPLACE); 210 dummy_op->set_data_offset(signature_blob_offset); 211 manifest->set_signatures_offset(signature_blob_offset); 212 dummy_op->set_data_length(signature_blob_length); 213 manifest->set_signatures_size(signature_blob_length); 214 Extent* dummy_extent = dummy_op->add_dst_extents(); 215 // Tell the dummy op to write this data to a big sparse hole 216 dummy_extent->set_start_block(kSparseHole); 217 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) / 218 kBlockSize); 219} 220 221bool PayloadSigner::LoadPayload(const std::string& payload_path, 222 brillo::Blob* out_payload, 223 DeltaArchiveManifest* out_manifest, 224 uint64_t* out_major_version, 225 uint64_t* out_metadata_size, 226 uint32_t* out_metadata_signature_size) { 227 brillo::Blob payload; 228 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); 229 TEST_AND_RETURN_FALSE(payload.size() >= 230 DeltaPerformer::kMaxPayloadHeaderSize); 231 const uint8_t* read_pointer = payload.data(); 232 TEST_AND_RETURN_FALSE( 233 memcmp(read_pointer, kDeltaMagic, sizeof(kDeltaMagic)) == 0); 234 read_pointer += sizeof(kDeltaMagic); 235 236 uint64_t major_version; 237 memcpy(&major_version, read_pointer, sizeof(major_version)); 238 read_pointer += sizeof(major_version); 239 major_version = be64toh(major_version); 240 TEST_AND_RETURN_FALSE(major_version == kChromeOSMajorPayloadVersion || 241 major_version == kBrilloMajorPayloadVersion); 242 if (out_major_version) 243 *out_major_version = major_version; 244 245 uint64_t manifest_size = 0; 246 memcpy(&manifest_size, read_pointer, sizeof(manifest_size)); 247 read_pointer += sizeof(manifest_size); 248 manifest_size = be64toh(manifest_size); 249 250 uint32_t metadata_signature_size = 0; 251 if (major_version == kBrilloMajorPayloadVersion) { 252 memcpy(&metadata_signature_size, read_pointer, 253 sizeof(metadata_signature_size)); 254 read_pointer += sizeof(metadata_signature_size); 255 metadata_signature_size = be32toh(metadata_signature_size); 256 } 257 if (out_metadata_signature_size) 258 *out_metadata_signature_size = metadata_signature_size; 259 260 *out_metadata_size = read_pointer - payload.data() + manifest_size; 261 TEST_AND_RETURN_FALSE(payload.size() >= *out_metadata_size); 262 if (out_manifest) 263 TEST_AND_RETURN_FALSE( 264 out_manifest->ParseFromArray(read_pointer, manifest_size)); 265 *out_payload = std::move(payload); 266 return true; 267} 268 269bool PayloadSigner::VerifySignedPayload(const string& payload_path, 270 const string& public_key_path) { 271 brillo::Blob payload; 272 DeltaArchiveManifest manifest; 273 uint64_t metadata_size; 274 uint32_t metadata_signature_size; 275 TEST_AND_RETURN_FALSE(LoadPayload(payload_path, &payload, &manifest, nullptr, 276 &metadata_size, &metadata_signature_size)); 277 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() && 278 manifest.has_signatures_size()); 279 uint64_t signatures_offset = metadata_size + metadata_signature_size + 280 manifest.signatures_offset(); 281 CHECK_EQ(payload.size(), signatures_offset + manifest.signatures_size()); 282 brillo::Blob payload_hash, metadata_hash; 283 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 284 metadata_size, 285 metadata_signature_size, 286 signatures_offset, 287 &payload_hash, 288 &metadata_hash)); 289 brillo::Blob signature_blob(payload.begin() + signatures_offset, 290 payload.end()); 291 TEST_AND_RETURN_FALSE(PayloadVerifier::PadRSA2048SHA256Hash(&payload_hash)); 292 TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature( 293 signature_blob, public_key_path, payload_hash)); 294 if (metadata_signature_size) { 295 signature_blob.assign(payload.begin() + metadata_size, 296 payload.begin() + metadata_size + 297 metadata_signature_size); 298 TEST_AND_RETURN_FALSE( 299 PayloadVerifier::PadRSA2048SHA256Hash(&metadata_hash)); 300 TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature( 301 signature_blob, public_key_path, metadata_hash)); 302 } 303 return true; 304} 305 306bool PayloadSigner::SignHash(const brillo::Blob& hash, 307 const string& private_key_path, 308 brillo::Blob* out_signature) { 309 LOG(INFO) << "Signing hash with private key: " << private_key_path; 310 string sig_path; 311 TEST_AND_RETURN_FALSE( 312 utils::MakeTempFile("signature.XXXXXX", &sig_path, nullptr)); 313 ScopedPathUnlinker sig_path_unlinker(sig_path); 314 315 string hash_path; 316 TEST_AND_RETURN_FALSE( 317 utils::MakeTempFile("hash.XXXXXX", &hash_path, nullptr)); 318 ScopedPathUnlinker hash_path_unlinker(hash_path); 319 // We expect unpadded SHA256 hash coming in 320 TEST_AND_RETURN_FALSE(hash.size() == 32); 321 brillo::Blob padded_hash(hash); 322 PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash); 323 TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(), 324 padded_hash.data(), 325 padded_hash.size())); 326 327 // This runs on the server, so it's okay to copy out and call openssl 328 // executable rather than properly use the library. 329 vector<string> cmd = {"openssl", "rsautl", "-raw", "-sign", "-inkey", 330 private_key_path, "-in", hash_path, "-out", sig_path}; 331 int return_code = 0; 332 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code, 333 nullptr)); 334 TEST_AND_RETURN_FALSE(return_code == 0); 335 336 brillo::Blob signature; 337 TEST_AND_RETURN_FALSE(utils::ReadFile(sig_path, &signature)); 338 out_signature->swap(signature); 339 return true; 340} 341 342bool PayloadSigner::SignHashWithKeys(const brillo::Blob& hash_data, 343 const vector<string>& private_key_paths, 344 brillo::Blob* out_signature_blob) { 345 vector<brillo::Blob> signatures; 346 for (const string& path : private_key_paths) { 347 brillo::Blob signature; 348 TEST_AND_RETURN_FALSE(SignHash(hash_data, path, &signature)); 349 signatures.push_back(signature); 350 } 351 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 352 out_signature_blob)); 353 return true; 354} 355 356bool PayloadSigner::SignPayload(const string& unsigned_payload_path, 357 const vector<string>& private_key_paths, 358 const uint64_t metadata_size, 359 const uint32_t metadata_signature_size, 360 const uint64_t signatures_offset, 361 brillo::Blob* out_signature_blob) { 362 brillo::Blob payload; 363 TEST_AND_RETURN_FALSE(utils::ReadFile(unsigned_payload_path, &payload)); 364 brillo::Blob hash_data; 365 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 366 metadata_size, 367 metadata_signature_size, 368 signatures_offset, 369 &hash_data, 370 nullptr)); 371 TEST_AND_RETURN_FALSE(SignHashWithKeys(hash_data, 372 private_key_paths, 373 out_signature_blob)); 374 return true; 375} 376 377bool PayloadSigner::SignatureBlobLength(const vector<string>& private_key_paths, 378 uint64_t* out_length) { 379 DCHECK(out_length); 380 brillo::Blob x_blob(1, 'x'), hash_blob, sig_blob; 381 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(x_blob.data(), 382 x_blob.size(), 383 &hash_blob)); 384 TEST_AND_RETURN_FALSE( 385 SignHashWithKeys(hash_blob, private_key_paths, &sig_blob)); 386 *out_length = sig_blob.size(); 387 return true; 388} 389 390bool PayloadSigner::HashPayloadForSigning(const string& payload_path, 391 const vector<int>& signature_sizes, 392 brillo::Blob* out_payload_hash_data, 393 brillo::Blob* out_metadata_hash) { 394 // Create a signature blob with signatures filled with 0. 395 // Will be used for both payload signature and metadata signature. 396 vector<brillo::Blob> signatures; 397 for (int signature_size : signature_sizes) { 398 signatures.emplace_back(signature_size, 0); 399 } 400 brillo::Blob signature_blob; 401 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 402 &signature_blob)); 403 404 brillo::Blob payload; 405 uint64_t metadata_size, signatures_offset; 406 uint32_t metadata_signature_size; 407 // Prepare payload for hashing. 408 TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path, 409 signature_blob, 410 signature_blob, 411 &payload, 412 &metadata_size, 413 &metadata_signature_size, 414 &signatures_offset)); 415 TEST_AND_RETURN_FALSE(CalculateHashFromPayload(payload, 416 metadata_size, 417 metadata_signature_size, 418 signatures_offset, 419 out_payload_hash_data, 420 out_metadata_hash)); 421 return true; 422} 423 424bool PayloadSigner::AddSignatureToPayload( 425 const string& payload_path, 426 const vector<brillo::Blob>& payload_signatures, 427 const vector<brillo::Blob>& metadata_signatures, 428 const string& signed_payload_path, 429 uint64_t *out_metadata_size) { 430 // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory. 431 432 // Loads the payload and adds the signature op to it. 433 brillo::Blob signature_blob, metadata_signature_blob; 434 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(payload_signatures, 435 &signature_blob)); 436 if (!metadata_signatures.empty()) { 437 TEST_AND_RETURN_FALSE( 438 ConvertSignatureToProtobufBlob(metadata_signatures, 439 &metadata_signature_blob)); 440 } 441 brillo::Blob payload; 442 uint64_t signatures_offset; 443 uint32_t metadata_signature_size; 444 TEST_AND_RETURN_FALSE(AddSignatureBlobToPayload(payload_path, 445 signature_blob, 446 metadata_signature_blob, 447 &payload, 448 out_metadata_size, 449 &metadata_signature_size, 450 &signatures_offset)); 451 452 LOG(INFO) << "Signed payload size: " << payload.size(); 453 TEST_AND_RETURN_FALSE(utils::WriteFile(signed_payload_path.c_str(), 454 payload.data(), 455 payload.size())); 456 return true; 457} 458 459bool PayloadSigner::GetMetadataSignature(const void* const metadata, 460 size_t metadata_size, 461 const string& private_key_path, 462 string* out_signature) { 463 // Calculates the hash on the updated payload. Note that the payload includes 464 // the signature op but doesn't include the signature blob at the end. 465 brillo::Blob metadata_hash; 466 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(metadata, 467 metadata_size, 468 &metadata_hash)); 469 470 brillo::Blob signature; 471 TEST_AND_RETURN_FALSE(SignHash(metadata_hash, 472 private_key_path, 473 &signature)); 474 475 *out_signature = brillo::data_encoding::Base64Encode(signature); 476 return true; 477} 478 479 480} // namespace chromeos_update_engine 481