payload_signer.cc revision aef1c6f0b5a0059a103251a9a93bd0be43bd50a2
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_size| 73// generates an updated payload that includes a dummy signature op in its 74// manifest. 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. Returns 77// true on success, false otherwise. 78bool AddSignatureOpToPayload(const string& payload_path, 79 uint64_t signature_blob_size, 80 brillo::Blob* out_payload, 81 uint64_t* out_metadata_size, 82 uint64_t* out_signatures_offset) { 83 const int kProtobufOffset = 20; 84 const int kProtobufSizeOffset = 12; 85 86 // Loads the payload. 87 brillo::Blob payload; 88 DeltaArchiveManifest manifest; 89 uint64_t metadata_size, major_version; 90 uint32_t metadata_signature_size; 91 TEST_AND_RETURN_FALSE(PayloadSigner::LoadPayload(payload_path, &payload, 92 &manifest, &major_version, &metadata_size, &metadata_signature_size)); 93 94 // Is there already a signature op in place? 95 if (manifest.has_signatures_size()) { 96 // The signature op is tied to the size of the signature blob, but not it's 97 // contents. We don't allow the manifest to change if there is already an op 98 // present, because that might invalidate previously generated 99 // hashes/signatures. 100 if (manifest.signatures_size() != signature_blob_size) { 101 LOG(ERROR) << "Attempt to insert different signature sized blob. " 102 << "(current:" << manifest.signatures_size() 103 << "new:" << signature_blob_size << ")"; 104 return false; 105 } 106 107 LOG(INFO) << "Matching signature sizes already present."; 108 } else { 109 // Updates the manifest to include the signature operation. 110 PayloadSigner::AddSignatureOp(payload.size() - metadata_size, 111 signature_blob_size, 112 &manifest); 113 114 // Updates the payload to include the new manifest. 115 string serialized_manifest; 116 TEST_AND_RETURN_FALSE(manifest.AppendToString(&serialized_manifest)); 117 LOG(INFO) << "Updated protobuf size: " << serialized_manifest.size(); 118 payload.erase(payload.begin() + kProtobufOffset, 119 payload.begin() + metadata_size); 120 payload.insert(payload.begin() + kProtobufOffset, 121 serialized_manifest.begin(), 122 serialized_manifest.end()); 123 124 // Updates the protobuf size. 125 uint64_t size_be = htobe64(serialized_manifest.size()); 126 memcpy(&payload[kProtobufSizeOffset], &size_be, sizeof(size_be)); 127 metadata_size = serialized_manifest.size() + kProtobufOffset; 128 129 LOG(INFO) << "Updated payload size: " << payload.size(); 130 LOG(INFO) << "Updated metadata size: " << metadata_size; 131 } 132 133 out_payload->swap(payload); 134 *out_metadata_size = metadata_size; 135 *out_signatures_offset = metadata_size + manifest.signatures_offset(); 136 LOG(INFO) << "Signature Blob Offset: " << *out_signatures_offset; 137 return true; 138} 139} // namespace 140 141void PayloadSigner::AddSignatureOp(uint64_t signature_blob_offset, 142 uint64_t signature_blob_length, 143 DeltaArchiveManifest* manifest) { 144 LOG(INFO) << "Making room for signature in file"; 145 manifest->set_signatures_offset(signature_blob_offset); 146 LOG(INFO) << "set? " << manifest->has_signatures_offset(); 147 // Add a dummy op at the end to appease older clients 148 InstallOperation* dummy_op = manifest->add_kernel_install_operations(); 149 dummy_op->set_type(InstallOperation::REPLACE); 150 dummy_op->set_data_offset(signature_blob_offset); 151 manifest->set_signatures_offset(signature_blob_offset); 152 dummy_op->set_data_length(signature_blob_length); 153 manifest->set_signatures_size(signature_blob_length); 154 Extent* dummy_extent = dummy_op->add_dst_extents(); 155 // Tell the dummy op to write this data to a big sparse hole 156 dummy_extent->set_start_block(kSparseHole); 157 dummy_extent->set_num_blocks((signature_blob_length + kBlockSize - 1) / 158 kBlockSize); 159} 160 161bool PayloadSigner::LoadPayload(const std::string& payload_path, 162 brillo::Blob* out_payload, 163 DeltaArchiveManifest* out_manifest, 164 uint64_t* out_major_version, 165 uint64_t* out_metadata_size, 166 uint32_t* out_metadata_signature_size) { 167 brillo::Blob payload; 168 TEST_AND_RETURN_FALSE(utils::ReadFile(payload_path, &payload)); 169 TEST_AND_RETURN_FALSE(payload.size() >= 170 DeltaPerformer::kMaxPayloadHeaderSize); 171 const uint8_t* read_pointer = payload.data(); 172 TEST_AND_RETURN_FALSE( 173 memcmp(read_pointer, kDeltaMagic, sizeof(kDeltaMagic)) == 0); 174 read_pointer += sizeof(kDeltaMagic); 175 176 uint64_t major_version; 177 memcpy(&major_version, read_pointer, sizeof(major_version)); 178 read_pointer += sizeof(major_version); 179 major_version = be64toh(major_version); 180 TEST_AND_RETURN_FALSE(major_version == kChromeOSMajorPayloadVersion || 181 major_version == kBrilloMajorPayloadVersion); 182 if (out_major_version) 183 *out_major_version = major_version; 184 185 uint64_t manifest_size = 0; 186 memcpy(&manifest_size, read_pointer, sizeof(manifest_size)); 187 read_pointer += sizeof(manifest_size); 188 manifest_size = be64toh(manifest_size); 189 190 uint32_t metadata_signature_size = 0; 191 if (major_version == kBrilloMajorPayloadVersion) { 192 memcpy(&metadata_signature_size, read_pointer, 193 sizeof(metadata_signature_size)); 194 read_pointer += sizeof(metadata_signature_size); 195 metadata_signature_size = be32toh(metadata_signature_size); 196 } 197 if (out_metadata_signature_size) 198 *out_metadata_signature_size = metadata_signature_size; 199 200 *out_metadata_size = read_pointer - payload.data() + manifest_size; 201 TEST_AND_RETURN_FALSE(payload.size() >= *out_metadata_size); 202 if (out_manifest) 203 TEST_AND_RETURN_FALSE( 204 out_manifest->ParseFromArray(read_pointer, manifest_size)); 205 *out_payload = std::move(payload); 206 return true; 207} 208 209bool PayloadSigner::VerifySignedPayload(const string& payload_path, 210 const string& public_key_path) { 211 brillo::Blob payload; 212 DeltaArchiveManifest manifest; 213 uint64_t metadata_size, major_version; 214 uint32_t metadata_signature_size; 215 TEST_AND_RETURN_FALSE(LoadPayload(payload_path, &payload, &manifest, 216 &major_version, &metadata_size, &metadata_signature_size)); 217 TEST_AND_RETURN_FALSE(manifest.has_signatures_offset() && 218 manifest.has_signatures_size()); 219 CHECK_EQ(payload.size(), 220 metadata_size + manifest.signatures_offset() + 221 manifest.signatures_size()); 222 brillo::Blob signature_blob( 223 payload.begin() + metadata_size + manifest.signatures_offset(), 224 payload.end()); 225 brillo::Blob hash; 226 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes( 227 payload.data(), metadata_size + manifest.signatures_offset(), &hash)); 228 TEST_AND_RETURN_FALSE(PayloadVerifier::PadRSA2048SHA256Hash(&hash)); 229 TEST_AND_RETURN_FALSE(PayloadVerifier::VerifySignature( 230 signature_blob, public_key_path, hash)); 231 return true; 232} 233 234bool PayloadSigner::SignHash(const brillo::Blob& hash, 235 const string& private_key_path, 236 brillo::Blob* out_signature) { 237 LOG(INFO) << "Signing hash with private key: " << private_key_path; 238 string sig_path; 239 TEST_AND_RETURN_FALSE( 240 utils::MakeTempFile("signature.XXXXXX", &sig_path, nullptr)); 241 ScopedPathUnlinker sig_path_unlinker(sig_path); 242 243 string hash_path; 244 TEST_AND_RETURN_FALSE( 245 utils::MakeTempFile("hash.XXXXXX", &hash_path, nullptr)); 246 ScopedPathUnlinker hash_path_unlinker(hash_path); 247 // We expect unpadded SHA256 hash coming in 248 TEST_AND_RETURN_FALSE(hash.size() == 32); 249 brillo::Blob padded_hash(hash); 250 PayloadVerifier::PadRSA2048SHA256Hash(&padded_hash); 251 TEST_AND_RETURN_FALSE(utils::WriteFile(hash_path.c_str(), 252 padded_hash.data(), 253 padded_hash.size())); 254 255 // This runs on the server, so it's okay to copy out and call openssl 256 // executable rather than properly use the library. 257 vector<string> cmd = {"openssl", "rsautl", "-raw", "-sign", "-inkey", 258 private_key_path, "-in", hash_path, "-out", sig_path}; 259 int return_code = 0; 260 TEST_AND_RETURN_FALSE(Subprocess::SynchronousExec(cmd, &return_code, 261 nullptr)); 262 TEST_AND_RETURN_FALSE(return_code == 0); 263 264 brillo::Blob signature; 265 TEST_AND_RETURN_FALSE(utils::ReadFile(sig_path, &signature)); 266 out_signature->swap(signature); 267 return true; 268} 269 270bool PayloadSigner::SignPayload(const string& unsigned_payload_path, 271 const vector<string>& private_key_paths, 272 brillo::Blob* out_signature_blob) { 273 brillo::Blob hash_data; 274 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfFile( 275 unsigned_payload_path, -1, &hash_data) == 276 utils::FileSize(unsigned_payload_path)); 277 278 vector<brillo::Blob> signatures; 279 for (const string& path : private_key_paths) { 280 brillo::Blob signature; 281 TEST_AND_RETURN_FALSE(SignHash(hash_data, path, &signature)); 282 signatures.push_back(signature); 283 } 284 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 285 out_signature_blob)); 286 return true; 287} 288 289bool PayloadSigner::SignatureBlobLength(const vector<string>& private_key_paths, 290 uint64_t* out_length) { 291 DCHECK(out_length); 292 293 string x_path; 294 TEST_AND_RETURN_FALSE( 295 utils::MakeTempFile("signed_data.XXXXXX", &x_path, nullptr)); 296 ScopedPathUnlinker x_path_unlinker(x_path); 297 TEST_AND_RETURN_FALSE(utils::WriteFile(x_path.c_str(), "x", 1)); 298 299 brillo::Blob sig_blob; 300 TEST_AND_RETURN_FALSE(PayloadSigner::SignPayload(x_path, 301 private_key_paths, 302 &sig_blob)); 303 *out_length = sig_blob.size(); 304 return true; 305} 306 307bool PayloadSigner::PrepPayloadForHashing( 308 const string& payload_path, 309 const vector<int>& signature_sizes, 310 brillo::Blob* payload_out, 311 uint64_t* metadata_size_out, 312 uint64_t* signatures_offset_out) { 313 // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory. 314 315 // Loads the payload and adds the signature op to it. 316 vector<brillo::Blob> signatures; 317 for (int signature_size : signature_sizes) { 318 signatures.emplace_back(signature_size, 0); 319 } 320 brillo::Blob signature_blob; 321 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 322 &signature_blob)); 323 TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path, 324 signature_blob.size(), 325 payload_out, 326 metadata_size_out, 327 signatures_offset_out)); 328 329 return true; 330} 331 332bool PayloadSigner::HashPayloadForSigning(const string& payload_path, 333 const vector<int>& signature_sizes, 334 brillo::Blob* out_hash_data) { 335 brillo::Blob payload; 336 uint64_t metadata_size; 337 uint64_t signatures_offset; 338 339 TEST_AND_RETURN_FALSE(PrepPayloadForHashing(payload_path, 340 signature_sizes, 341 &payload, 342 &metadata_size, 343 &signatures_offset)); 344 345 // Calculates the hash on the updated payload. Note that we stop calculating 346 // before we reach the signature information. 347 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(payload.data(), 348 signatures_offset, 349 out_hash_data)); 350 return true; 351} 352 353bool PayloadSigner::HashMetadataForSigning(const string& payload_path, 354 const vector<int>& signature_sizes, 355 brillo::Blob* out_metadata_hash) { 356 brillo::Blob payload; 357 uint64_t metadata_size; 358 uint64_t signatures_offset; 359 360 TEST_AND_RETURN_FALSE(PrepPayloadForHashing(payload_path, 361 signature_sizes, 362 &payload, 363 &metadata_size, 364 &signatures_offset)); 365 366 // Calculates the hash on the manifest. 367 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(payload.data(), 368 metadata_size, 369 out_metadata_hash)); 370 return true; 371} 372 373bool PayloadSigner::AddSignatureToPayload( 374 const string& payload_path, 375 const vector<brillo::Blob>& signatures, 376 const string& signed_payload_path, 377 uint64_t *out_metadata_size) { 378 // TODO(petkov): Reduce memory usage -- the payload is manipulated in memory. 379 380 // Loads the payload and adds the signature op to it. 381 brillo::Blob signature_blob; 382 TEST_AND_RETURN_FALSE(ConvertSignatureToProtobufBlob(signatures, 383 &signature_blob)); 384 brillo::Blob payload; 385 uint64_t signatures_offset; 386 TEST_AND_RETURN_FALSE(AddSignatureOpToPayload(payload_path, 387 signature_blob.size(), 388 &payload, 389 out_metadata_size, 390 &signatures_offset)); 391 // Appends the signature blob to the end of the payload and writes the new 392 // payload. 393 LOG(INFO) << "Payload size before signatures: " << payload.size(); 394 payload.resize(signatures_offset); 395 payload.insert(payload.begin() + signatures_offset, 396 signature_blob.begin(), 397 signature_blob.end()); 398 LOG(INFO) << "Signed payload size: " << payload.size(); 399 TEST_AND_RETURN_FALSE(utils::WriteFile(signed_payload_path.c_str(), 400 payload.data(), 401 payload.size())); 402 return true; 403} 404 405bool PayloadSigner::GetMetadataSignature(const void* const metadata, 406 size_t metadata_size, 407 const string& private_key_path, 408 string* out_signature) { 409 // Calculates the hash on the updated payload. Note that the payload includes 410 // the signature op but doesn't include the signature blob at the end. 411 brillo::Blob metadata_hash; 412 TEST_AND_RETURN_FALSE(OmahaHashCalculator::RawHashOfBytes(metadata, 413 metadata_size, 414 &metadata_hash)); 415 416 brillo::Blob signature; 417 TEST_AND_RETURN_FALSE(SignHash(metadata_hash, 418 private_key_path, 419 &signature)); 420 421 *out_signature = brillo::data_encoding::Base64Encode(signature); 422 return true; 423} 424 425 426} // namespace chromeos_update_engine 427