blob: 2159d281285828f61f0a574c617a46f024e9fe6c [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 }
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 Srinivasane56c8732012-10-17 12:19:27 -0700126bool OmahaHashCalculator::Base64Decode(const string& raw_in,
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700127 vector<char>* out) {
Jay Srinivasane56c8732012-10-17 12:19:27 -0700128 out->clear();
129
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700130 ScopedBioHandle b64(BIO_new(BIO_f_base64()));
131 if (!b64.bio()) {
Jay Srinivasane56c8732012-10-17 12:19:27 -0700132 LOG(ERROR) << "Unable to create BIO object to decode base64 hash";
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700133 return false;
134 }
135
Jay Srinivasane56c8732012-10-17 12:19:27 -0700136 // Canonicalize the raw input to get rid of all newlines in the string
137 // and set the NO_NL flag so that BIO_read decodes properly. Otherwise
138 // BIO_read would just return 0 without decode anything.
139 string in;
140 for (size_t i = 0; i < raw_in.size(); i++)
141 if (raw_in[i] != '\n')
142 in.push_back(raw_in[i]);
143
144 BIO_set_flags(b64.bio(), BIO_FLAGS_BASE64_NO_NL);
145
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700146 BIO *bmem = BIO_new_mem_buf(const_cast<char*>(in.c_str()), in.size());
147 if (!bmem) {
Jay Srinivasane56c8732012-10-17 12:19:27 -0700148 LOG(ERROR) << "Unable to get BIO buffer to decode base64 hash";
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700149 return false;
150 }
151
152 b64.set_bio(BIO_push(b64.bio(), bmem));
Jay Srinivasane56c8732012-10-17 12:19:27 -0700153
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700154 const int kOutBufferSize = 1024;
155 char out_buffer[kOutBufferSize];
156 int num_bytes_read = 1; // any non-zero value is fine to enter the loop.
157 while (num_bytes_read > 0) {
158 num_bytes_read = BIO_read(b64.bio(), &out_buffer, kOutBufferSize);
159 for (int i = 0; i < num_bytes_read; i++)
160 out->push_back(out_buffer[i]);
161 }
162
Jay Srinivasane56c8732012-10-17 12:19:27 -0700163 LOG(INFO) << "Decoded " << out->size()
164 << " bytes from " << in.size() << " base64-encoded bytes";
Jay Srinivasan51dcf262012-09-13 17:24:32 -0700165 return true;
166}
167
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700168// Call Finalize() when all data has been passed in. This mostly just
169// calls OpenSSL's SHA256_Final() and then base64 encodes the hash.
170bool OmahaHashCalculator::Finalize() {
171 TEST_AND_RETURN_FALSE(hash_.empty());
172 TEST_AND_RETURN_FALSE(raw_hash_.empty());
173 raw_hash_.resize(SHA256_DIGEST_LENGTH);
174 TEST_AND_RETURN_FALSE(
175 SHA256_Final(reinterpret_cast<unsigned char*>(&raw_hash_[0]),
176 &ctx_) == 1);
177
178 // Convert raw_hash_ to base64 encoding and store it in hash_.
Jay Srinivasan55f50c22013-01-10 19:24:35 -0800179 return Base64Encode(&raw_hash_[0], raw_hash_.size(), &hash_);
Andrew de los Reyes89f17be2010-10-22 13:39:09 -0700180}
181
Darin Petkovadb3cef2011-01-13 16:16:08 -0800182bool OmahaHashCalculator::RawHashOfBytes(const char* data,
183 size_t length,
184 vector<char>* out_hash) {
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700185 OmahaHashCalculator calc;
Darin Petkovadb3cef2011-01-13 16:16:08 -0800186 TEST_AND_RETURN_FALSE(calc.Update(data, length));
187 TEST_AND_RETURN_FALSE(calc.Finalize());
188 *out_hash = calc.raw_hash();
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700189 return true;
190}
191
Darin Petkovadb3cef2011-01-13 16:16:08 -0800192bool OmahaHashCalculator::RawHashOfData(const vector<char>& data,
193 vector<char>* out_hash) {
194 return RawHashOfBytes(data.data(), data.size(), out_hash);
195}
196
197off_t OmahaHashCalculator::RawHashOfFile(const string& name, off_t length,
198 vector<char>* out_hash) {
Darin Petkov698d0412010-10-13 10:59:44 -0700199 OmahaHashCalculator calc;
200 off_t res = calc.UpdateFile(name, length);
201 if (res < 0) {
202 return res;
203 }
204 if (!calc.Finalize()) {
205 return -1;
206 }
207 *out_hash = calc.raw_hash();
208 return res;
209}
210
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700211string OmahaHashCalculator::OmahaHashOfBytes(
rspangler@google.com49fdf182009-10-10 00:57:34 +0000212 const void* data, size_t length) {
213 OmahaHashCalculator calc;
214 calc.Update(reinterpret_cast<const char*>(data), length);
215 calc.Finalize();
216 return calc.hash();
217}
218
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700219string OmahaHashCalculator::OmahaHashOfString(const string& str) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000220 return OmahaHashOfBytes(str.data(), str.size());
221}
222
Andrew de los Reyes932bc4c2010-08-23 18:14:09 -0700223string OmahaHashCalculator::OmahaHashOfData(const vector<char>& data) {
rspangler@google.com49fdf182009-10-10 00:57:34 +0000224 return OmahaHashOfBytes(&data[0], data.size());
225}
226
Darin Petkov73058b42010-10-06 16:32:19 -0700227string OmahaHashCalculator::GetContext() const {
228 return string(reinterpret_cast<const char*>(&ctx_), sizeof(ctx_));
229}
230
231bool OmahaHashCalculator::SetContext(const std::string& context) {
232 TEST_AND_RETURN_FALSE(context.size() == sizeof(ctx_));
233 memcpy(&ctx_, context.data(), sizeof(ctx_));
234 return true;
235}
236
rspangler@google.com49fdf182009-10-10 00:57:34 +0000237} // namespace chromeos_update_engine