blob: 9899d594f3a65fa0d8d5bac5acd7ccea8a7d04c5 [file] [log] [blame]
rspangler@google.com49fdf182009-10-10 00:57:34 +00001// Copyright (c) 2009 The Chromium OS Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -07005#include "update_engine/omaha_hash_calculator.h"
6
Darin Petkov36a58222010-10-07 22:00:09 -07007#include <fcntl.h>
8
Darin Petkov36a58222010-10-07 22:00:09 -07009#include <base/logging.h>
Chris Sosafc661a12013-02-26 14:43:21 -080010#include <base/posix/eintr_wrapper.h>
rspangler@google.com49fdf182009-10-10 00:57:34 +000011#include <openssl/bio.h>
12#include <openssl/buffer.h>
13#include <openssl/evp.h>
Darin Petkov36a58222010-10-07 22:00:09 -070014
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070015#include "update_engine/utils.h"
16
17using std::string;
18using std::vector;
rspangler@google.com49fdf182009-10-10 00:57:34 +000019
20namespace chromeos_update_engine {
21
Jay Srinivasan51dcf262012-09-13 17:24:32 -070022// Helper class to free a BIO structure when a method goes out of scope.
23class ScopedBioHandle {
24 public:
25 explicit ScopedBioHandle(BIO* bio) : bio_(bio) {}
26 ~ScopedBioHandle() {
27 FreeCurrentBio();
28 }
29
30 void set_bio(BIO* bio) {
31 if (bio_ != bio) {
32 // Free the current bio, but only if the caller is not trying to set
33 // the same bio object again, so that the operation can be idempotent.
34 FreeCurrentBio();
35 }
36 bio_ = bio;
37 }
38
39 BIO* bio() {
40 return bio_;
41 }
Alex Vakulenkod2779df2014-06-16 13:19:00 -070042
Jay Srinivasan51dcf262012-09-13 17:24:32 -070043 private:
Jay Srinivasan51dcf262012-09-13 17:24:32 -070044 BIO* bio_;
45
46 void FreeCurrentBio() {
47 if (bio_) {
48 BIO_free_all(bio_);
Alex Vakulenko88b591f2014-08-28 16:48:57 -070049 bio_ = nullptr;
Jay Srinivasan51dcf262012-09-13 17:24:32 -070050 }
51 }
Alex Vakulenkod2779df2014-06-16 13:19:00 -070052
53 DISALLOW_COPY_AND_ASSIGN(ScopedBioHandle);
Jay Srinivasan51dcf262012-09-13 17:24:32 -070054};
55
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070056OmahaHashCalculator::OmahaHashCalculator() : valid_(false) {
Darin Petkovd22cb292010-09-29 10:02:29 -070057 valid_ = (SHA256_Init(&ctx_) == 1);
58 LOG_IF(ERROR, !valid_) << "SHA256_Init failed";
rspangler@google.com49fdf182009-10-10 00:57:34 +000059}
60
61// Update is called with all of the data that should be hashed in order.
Darin Petkovd22cb292010-09-29 10:02:29 -070062// Mostly just passes the data through to OpenSSL's SHA256_Update()
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070063bool OmahaHashCalculator::Update(const char* data, size_t length) {
64 TEST_AND_RETURN_FALSE(valid_);
65 TEST_AND_RETURN_FALSE(hash_.empty());
Alex Vakulenkod2779df2014-06-16 13:19:00 -070066 static_assert(sizeof(size_t) <= sizeof(unsigned long), // NOLINT(runtime/int)
67 "length param may be truncated in SHA256_Update");
Darin Petkovd22cb292010-09-29 10:02:29 -070068 TEST_AND_RETURN_FALSE(SHA256_Update(&ctx_, data, length) == 1);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070069 return true;
rspangler@google.com49fdf182009-10-10 00:57:34 +000070}
71
Darin Petkov36a58222010-10-07 22:00:09 -070072off_t OmahaHashCalculator::UpdateFile(const string& name, off_t length) {
73 int fd = HANDLE_EINTR(open(name.c_str(), O_RDONLY));
74 if (fd < 0) {
75 return -1;
76 }
77
78 const int kBufferSize = 128 * 1024; // 128 KiB
79 vector<char> buffer(kBufferSize);
80 off_t bytes_processed = 0;
81 while (length < 0 || bytes_processed < length) {
82 off_t bytes_to_read = buffer.size();
83 if (length >= 0 && bytes_to_read > length - bytes_processed) {
84 bytes_to_read = length - bytes_processed;
85 }
86 ssize_t rc = HANDLE_EINTR(read(fd, buffer.data(), bytes_to_read));
87 if (rc == 0) { // EOF
88 break;
89 }
90 if (rc < 0 || !Update(buffer.data(), rc)) {
91 bytes_processed = -1;
92 break;
93 }
94 bytes_processed += rc;
95 }
Mike Frysingerbcee2ca2014-05-14 16:28:23 -040096 IGNORE_EINTR(close(fd));
Darin Petkov36a58222010-10-07 22:00:09 -070097 return bytes_processed;
98}
99
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700100bool OmahaHashCalculator::Base64Encode(const void* data,
101 size_t size,
102 string* out) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700103 bool success = true;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000104 BIO *b64 = BIO_new(BIO_f_base64());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700105 if (!b64)
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700106 LOG(ERROR) << "BIO_new(BIO_f_base64()) failed";
rspangler@google.com49fdf182009-10-10 00:57:34 +0000107 BIO *bmem = BIO_new(BIO_s_mem());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700108 if (!bmem)
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700109 LOG(ERROR) << "BIO_new(BIO_s_mem()) failed";
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700110 if (b64 && bmem) {
111 b64 = BIO_push(b64, bmem);
Darin Petkovd7061ab2010-10-06 14:37:09 -0700112 success =
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700113 (BIO_write(b64, data, size) == static_cast<int>(size));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700114 if (success)
115 success = (BIO_flush(b64) == 1);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000116
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700117 BUF_MEM *bptr = nullptr;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700118 BIO_get_mem_ptr(b64, &bptr);
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700119 out->assign(bptr->data, bptr->length - 1);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700120 }
121 if (b64) {
122 BIO_free_all(b64);
Alex Vakulenko88b591f2014-08-28 16:48:57 -0700123 b64 = nullptr;
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700124 }
125 return success;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000126}
127
Jay Srinivasane56c8732012-10-17 12:19:27 -0700128bool OmahaHashCalculator::Base64Decode(const string& raw_in,
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700129 vector<char>* out) {
Jay Srinivasane56c8732012-10-17 12:19:27 -0700130 out->clear();
131
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700132 ScopedBioHandle b64(BIO_new(BIO_f_base64()));
133 if (!b64.bio()) {
Jay Srinivasane56c8732012-10-17 12:19:27 -0700134 LOG(ERROR) << "Unable to create BIO object to decode base64 hash";
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700135 return false;
136 }
137
Jay Srinivasane56c8732012-10-17 12:19:27 -0700138 // Canonicalize the raw input to get rid of all newlines in the string
139 // and set the NO_NL flag so that BIO_read decodes properly. Otherwise
140 // BIO_read would just return 0 without decode anything.
141 string in;
142 for (size_t i = 0; i < raw_in.size(); i++)
143 if (raw_in[i] != '\n')
144 in.push_back(raw_in[i]);
145
146 BIO_set_flags(b64.bio(), BIO_FLAGS_BASE64_NO_NL);
147
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700148 BIO *bmem = BIO_new_mem_buf(const_cast<char*>(in.c_str()), in.size());
149 if (!bmem) {
Jay Srinivasane56c8732012-10-17 12:19:27 -0700150 LOG(ERROR) << "Unable to get BIO buffer to decode base64 hash";
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700151 return false;
152 }
153
154 b64.set_bio(BIO_push(b64.bio(), bmem));
Jay Srinivasane56c8732012-10-17 12:19:27 -0700155
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700156 const int kOutBufferSize = 1024;
157 char out_buffer[kOutBufferSize];
Alex Vakulenkod2779df2014-06-16 13:19:00 -0700158 int num_bytes_read = 1; // any non-zero value is fine to enter the loop.
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700159 while (num_bytes_read > 0) {
160 num_bytes_read = BIO_read(b64.bio(), &out_buffer, kOutBufferSize);
161 for (int i = 0; i < num_bytes_read; i++)
162 out->push_back(out_buffer[i]);
163 }
164
Jay Srinivasane56c8732012-10-17 12:19:27 -0700165 LOG(INFO) << "Decoded " << out->size()
166 << " bytes from " << in.size() << " base64-encoded bytes";
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700167 return true;
168}
169
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700170// Call Finalize() when all data has been passed in. This mostly just
171// calls OpenSSL's SHA256_Final() and then base64 encodes the hash.
172bool OmahaHashCalculator::Finalize() {
173 TEST_AND_RETURN_FALSE(hash_.empty());
174 TEST_AND_RETURN_FALSE(raw_hash_.empty());
175 raw_hash_.resize(SHA256_DIGEST_LENGTH);
176 TEST_AND_RETURN_FALSE(
177 SHA256_Final(reinterpret_cast<unsigned char*>(&raw_hash_[0]),
178 &ctx_) == 1);
179
180 // Convert raw_hash_ to base64 encoding and store it in hash_.
Jay Srinivasan55f50c22013-01-10 19:24:35 -0800181 return Base64Encode(&raw_hash_[0], raw_hash_.size(), &hash_);
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700182}
183
Darin Petkovadb3cef2011-01-13 16:16:08 -0800184bool OmahaHashCalculator::RawHashOfBytes(const char* data,
185 size_t length,
186 vector<char>* out_hash) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700187 OmahaHashCalculator calc;
Darin Petkovadb3cef2011-01-13 16:16:08 -0800188 TEST_AND_RETURN_FALSE(calc.Update(data, length));
189 TEST_AND_RETURN_FALSE(calc.Finalize());
190 *out_hash = calc.raw_hash();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700191 return true;
192}
193
Darin Petkovadb3cef2011-01-13 16:16:08 -0800194bool OmahaHashCalculator::RawHashOfData(const vector<char>& data,
195 vector<char>* out_hash) {
196 return RawHashOfBytes(data.data(), data.size(), out_hash);
197}
198
199off_t OmahaHashCalculator::RawHashOfFile(const string& name, off_t length,
200 vector<char>* out_hash) {
Darin Petkov698d0412010-10-13 10:59:44 -0700201 OmahaHashCalculator calc;
202 off_t res = calc.UpdateFile(name, length);
203 if (res < 0) {
204 return res;
205 }
206 if (!calc.Finalize()) {
207 return -1;
208 }
209 *out_hash = calc.raw_hash();
210 return res;
211}
212
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700213string OmahaHashCalculator::OmahaHashOfBytes(
rspangler@google.com49fdf182009-10-10 00:57:34 +0000214 const void* data, size_t length) {
215 OmahaHashCalculator calc;
216 calc.Update(reinterpret_cast<const char*>(data), length);
217 calc.Finalize();
218 return calc.hash();
219}
220
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700221string OmahaHashCalculator::OmahaHashOfString(const string& str) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000222 return OmahaHashOfBytes(str.data(), str.size());
223}
224
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700225string OmahaHashCalculator::OmahaHashOfData(const vector<char>& data) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000226 return OmahaHashOfBytes(&data[0], data.size());
227}
228
Darin Petkov73058b42010-10-06 16:32:19 -0700229string OmahaHashCalculator::GetContext() const {
230 return string(reinterpret_cast<const char*>(&ctx_), sizeof(ctx_));
231}
232
Alex Deymof329b932014-10-30 01:37:48 -0700233bool OmahaHashCalculator::SetContext(const string& context) {
Darin Petkov73058b42010-10-06 16:32:19 -0700234 TEST_AND_RETURN_FALSE(context.size() == sizeof(ctx_));
235 memcpy(&ctx_, context.data(), sizeof(ctx_));
236 return true;
237}
238
rspangler@google.com49fdf182009-10-10 00:57:34 +0000239} // namespace chromeos_update_engine