blob: 9fe82a23284cf1bb81e5ab7b1274d6e58d83b26b [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
9#include <base/eintr_wrapper.h>
10#include <base/logging.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 }
42 private:
43 DISALLOW_COPY_AND_ASSIGN(ScopedBioHandle);
44 BIO* bio_;
45
46 void FreeCurrentBio() {
47 if (bio_) {
48 BIO_free_all(bio_);
49 bio_ = NULL;
50 }
51 }
52};
53
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070054OmahaHashCalculator::OmahaHashCalculator() : valid_(false) {
Darin Petkovd22cb292010-09-29 10:02:29 -070055 valid_ = (SHA256_Init(&ctx_) == 1);
56 LOG_IF(ERROR, !valid_) << "SHA256_Init failed";
rspangler@google.com49fdf182009-10-10 00:57:34 +000057}
58
59// Update is called with all of the data that should be hashed in order.
Darin Petkovd22cb292010-09-29 10:02:29 -070060// Mostly just passes the data through to OpenSSL's SHA256_Update()
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070061bool OmahaHashCalculator::Update(const char* data, size_t length) {
62 TEST_AND_RETURN_FALSE(valid_);
63 TEST_AND_RETURN_FALSE(hash_.empty());
rspangler@google.com49fdf182009-10-10 00:57:34 +000064 COMPILE_ASSERT(sizeof(size_t) <= sizeof(unsigned long),
Darin Petkovd22cb292010-09-29 10:02:29 -070065 length_param_may_be_truncated_in_SHA256_Update);
66 TEST_AND_RETURN_FALSE(SHA256_Update(&ctx_, data, length) == 1);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -070067 return true;
rspangler@google.com49fdf182009-10-10 00:57:34 +000068}
69
Darin Petkov36a58222010-10-07 22:00:09 -070070off_t OmahaHashCalculator::UpdateFile(const string& name, off_t length) {
71 int fd = HANDLE_EINTR(open(name.c_str(), O_RDONLY));
72 if (fd < 0) {
73 return -1;
74 }
75
76 const int kBufferSize = 128 * 1024; // 128 KiB
77 vector<char> buffer(kBufferSize);
78 off_t bytes_processed = 0;
79 while (length < 0 || bytes_processed < length) {
80 off_t bytes_to_read = buffer.size();
81 if (length >= 0 && bytes_to_read > length - bytes_processed) {
82 bytes_to_read = length - bytes_processed;
83 }
84 ssize_t rc = HANDLE_EINTR(read(fd, buffer.data(), bytes_to_read));
85 if (rc == 0) { // EOF
86 break;
87 }
88 if (rc < 0 || !Update(buffer.data(), rc)) {
89 bytes_processed = -1;
90 break;
91 }
92 bytes_processed += rc;
93 }
94 HANDLE_EINTR(close(fd));
95 return bytes_processed;
96}
97
Andrew de los Reyes89f17be2010-10-22 13:39:09 -070098bool OmahaHashCalculator::Base64Encode(const void* data,
99 size_t size,
100 string* out) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700101 bool success = true;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000102 BIO *b64 = BIO_new(BIO_f_base64());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700103 if (!b64)
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700104 LOG(ERROR) << "BIO_new(BIO_f_base64()) failed";
rspangler@google.com49fdf182009-10-10 00:57:34 +0000105 BIO *bmem = BIO_new(BIO_s_mem());
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700106 if (!bmem)
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700107 LOG(ERROR) << "BIO_new(BIO_s_mem()) failed";
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700108 if (b64 && bmem) {
109 b64 = BIO_push(b64, bmem);
Darin Petkovd7061ab2010-10-06 14:37:09 -0700110 success =
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700111 (BIO_write(b64, data, size) == static_cast<int>(size));
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700112 if (success)
113 success = (BIO_flush(b64) == 1);
rspangler@google.com49fdf182009-10-10 00:57:34 +0000114
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700115 BUF_MEM *bptr = NULL;
116 BIO_get_mem_ptr(b64, &bptr);
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700117 out->assign(bptr->data, bptr->length - 1);
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700118 }
119 if (b64) {
120 BIO_free_all(b64);
121 b64 = NULL;
122 }
123 return success;
rspangler@google.com49fdf182009-10-10 00:57:34 +0000124}
125
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700126bool OmahaHashCalculator::Base64Decode(const string& in,
127 vector<char>* out) {
128 ScopedBioHandle b64(BIO_new(BIO_f_base64()));
129 if (!b64.bio()) {
130 LOG(ERROR) << "Unable to create BIO object to decode base64 hash.";
131 return false;
132 }
133
134 BIO *bmem = BIO_new_mem_buf(const_cast<char*>(in.c_str()), in.size());
135 if (!bmem) {
136 LOG(ERROR) << "Unable to get BIO buffer to decode base64 hash.";
137 return false;
138 }
139
140 b64.set_bio(BIO_push(b64.bio(), bmem));
141 const int kOutBufferSize = 1024;
142 char out_buffer[kOutBufferSize];
143 int num_bytes_read = 1; // any non-zero value is fine to enter the loop.
144 while (num_bytes_read > 0) {
145 num_bytes_read = BIO_read(b64.bio(), &out_buffer, kOutBufferSize);
146 for (int i = 0; i < num_bytes_read; i++)
147 out->push_back(out_buffer[i]);
148 }
149
150 return true;
151}
152
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700153// Call Finalize() when all data has been passed in. This mostly just
154// calls OpenSSL's SHA256_Final() and then base64 encodes the hash.
155bool OmahaHashCalculator::Finalize() {
156 TEST_AND_RETURN_FALSE(hash_.empty());
157 TEST_AND_RETURN_FALSE(raw_hash_.empty());
158 raw_hash_.resize(SHA256_DIGEST_LENGTH);
159 TEST_AND_RETURN_FALSE(
160 SHA256_Final(reinterpret_cast<unsigned char*>(&raw_hash_[0]),
161 &ctx_) == 1);
162
163 // Convert raw_hash_ to base64 encoding and store it in hash_.
164 return Base64Encode(&raw_hash_[0], raw_hash_.size(), &hash_);;
165}
166
Darin Petkovadb3cef2011-01-13 16:16:08 -0800167bool OmahaHashCalculator::RawHashOfBytes(const char* data,
168 size_t length,
169 vector<char>* out_hash) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700170 OmahaHashCalculator calc;
Darin Petkovadb3cef2011-01-13 16:16:08 -0800171 TEST_AND_RETURN_FALSE(calc.Update(data, length));
172 TEST_AND_RETURN_FALSE(calc.Finalize());
173 *out_hash = calc.raw_hash();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700174 return true;
175}
176
Darin Petkovadb3cef2011-01-13 16:16:08 -0800177bool OmahaHashCalculator::RawHashOfData(const vector<char>& data,
178 vector<char>* out_hash) {
179 return RawHashOfBytes(data.data(), data.size(), out_hash);
180}
181
182off_t OmahaHashCalculator::RawHashOfFile(const string& name, off_t length,
183 vector<char>* out_hash) {
Darin Petkov698d0412010-10-13 10:59:44 -0700184 OmahaHashCalculator calc;
185 off_t res = calc.UpdateFile(name, length);
186 if (res < 0) {
187 return res;
188 }
189 if (!calc.Finalize()) {
190 return -1;
191 }
192 *out_hash = calc.raw_hash();
193 return res;
194}
195
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700196string OmahaHashCalculator::OmahaHashOfBytes(
rspangler@google.com49fdf182009-10-10 00:57:34 +0000197 const void* data, size_t length) {
198 OmahaHashCalculator calc;
199 calc.Update(reinterpret_cast<const char*>(data), length);
200 calc.Finalize();
201 return calc.hash();
202}
203
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700204string OmahaHashCalculator::OmahaHashOfString(const string& str) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000205 return OmahaHashOfBytes(str.data(), str.size());
206}
207
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700208string OmahaHashCalculator::OmahaHashOfData(const vector<char>& data) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000209 return OmahaHashOfBytes(&data[0], data.size());
210}
211
Darin Petkov73058b42010-10-06 16:32:19 -0700212string OmahaHashCalculator::GetContext() const {
213 return string(reinterpret_cast<const char*>(&ctx_), sizeof(ctx_));
214}
215
216bool OmahaHashCalculator::SetContext(const std::string& context) {
217 TEST_AND_RETURN_FALSE(context.size() == sizeof(ctx_));
218 memcpy(&ctx_, context.data(), sizeof(ctx_));
219 return true;
220}
221
rspangler@google.com49fdf182009-10-10 00:57:34 +0000222} // namespace chromeos_update_engine