1 //
2 // Copyright (C) 2013 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 <unistd.h>
18
19 #include <limits>
20 #include <string>
21 #include <vector>
22
23 #include <base/command_line.h>
24 #include <base/logging.h>
25 #include <base/posix/eintr_wrapper.h>
26 #include <brillo/syslog_logging.h>
27 #include <openssl/bio.h>
28 #include <openssl/conf.h>
29 #include <openssl/err.h>
30 #include <openssl/evp.h>
31 #include <openssl/pem.h>
32 #include <openssl/rsa.h>
33 #include <openssl/sha.h>
34 #include <openssl/x509.h>
35
36 #include "shill/shims/protos/crypto_util.pb.h"
37
38 using shill_protos::EncryptDataMessage;
39 using shill_protos::EncryptDataResponse;
40 using shill_protos::VerifyCredentialsMessage;
41 using shill_protos::VerifyCredentialsResponse;
42 using std::numeric_limits;
43 using std::string;
44 using std::vector;
45
46 namespace {
47
48 const char kTrustedCAModulus[] =
49 "BC2280BD80F63A21003BAE765E357F3DC3645C559486342F058728CDF7698C17B350A7B8"
50 "82FADFC7432DD67EABA06FB7137280A44715C1209950CDEC1462095BA498CDD241B6364E"
51 "FFE82E32304A81A842A36C9B336ECAB2F55366E02753861A851EA7393F4A778EFB546666"
52 "FB5854C05E39C7F550060BE08AD4CEE16A551F8B1700E669A327E60825693C129D8D052C"
53 "D62EA231DEB45250D62049DE71A0F9AD204012F1DD25EBD5E6B836F4D68F7FCA43DCD710"
54 "5BE63F518A85B3F3FFF6032DCB234F9CAD18E793058CAC529AF74CE9997ABE6E7E4D0AE3"
55 "C61CA993FA3AA5915D1CBD66EBCC60DC8674CACFF8921C987D57FA61479EAB80B7E44880"
56 "2A92C51B";
57 const char kCommandVerify[] = "verify";
58 const char kCommandEncrypt[] = "encrypt";
59 const size_t kMacLength = 12;
60
61 // Encrypt |data| with |public_key|. |public_key| is the raw bytes of a key in
62 // RSAPublicKey format. |data| is some string of bytes smaller than the
63 // maximum length permissable for encryption with a key of |public_key| size.
64 // |rsa_ptr| should point to NULL (but should not be NULL). This function may
65 // set *|rsa_ptr| to an RSA object which should be freed in the caller.
66 // Returns the encrypted result in |encrypted_output| and returns true on
67 // success. Returns false on failure.
EncryptByteStringImpl(const string & public_key,const string & data,RSA ** rsa_ptr,string * encrypted_output)68 bool EncryptByteStringImpl(const string& public_key,
69 const string& data,
70 RSA** rsa_ptr,
71 string* encrypted_output) {
72 CHECK(rsa_ptr);
73 CHECK(!*rsa_ptr);
74 CHECK(encrypted_output);
75
76 // This pointer will be incremented internally by the parsing routine.
77 const unsigned char* throwaway_ptr =
78 reinterpret_cast<const unsigned char*>(public_key.data());
79 *rsa_ptr = d2i_RSAPublicKey(NULL, &throwaway_ptr, public_key.length());
80 RSA* rsa = *rsa_ptr;
81 if (!rsa) {
82 LOG(ERROR) << "Failed to parse public key.";
83 return false;
84 }
85
86 vector<unsigned char> rsa_output(RSA_size(rsa));
87 LOG(INFO) << "Encrypting data with public key.";
88 const int encrypted_length = RSA_public_encrypt(
89 data.length(),
90 // The API helpfully tells us that this operation will treat this buffer
91 // as read only, but fails to mark the parameter const.
92 reinterpret_cast<unsigned char*>(const_cast<char*>(data.data())),
93 rsa_output.data(),
94 rsa,
95 RSA_PKCS1_PADDING);
96 if (encrypted_length <= 0) {
97 LOG(ERROR) << "Error during encryption.";
98 return false;
99 }
100
101 encrypted_output->assign(reinterpret_cast<char*>(rsa_output.data()),
102 encrypted_length);
103 return true;
104 }
105
106 // Parse the EncryptDataMessage contained in |raw_input| and return an
107 // EncryptDataResponse in output on success. Returns true on success and
108 // false otherwise.
EncryptByteString(const string & raw_input,string * output)109 bool EncryptByteString(const string& raw_input, string* output) {
110 EncryptDataMessage message;
111 if (!message.ParseFromString(raw_input)) {
112 LOG(ERROR) << "Failed to read VerifyCredentialsMessage from stdin.";
113 return false;
114 }
115
116 if (!message.has_public_key() || !message.has_data()) {
117 LOG(ERROR) << "Request lacked necessary fields.";
118 return false;
119 }
120
121 RSA* rsa = NULL;
122 string encrypted_output;
123 bool operation_successful = EncryptByteStringImpl(
124 message.public_key(), message.data(), &rsa, &encrypted_output);
125 if (rsa) {
126 RSA_free(rsa);
127 rsa = NULL;
128 }
129
130 if (operation_successful) {
131 LOG(INFO) << "Filling out protobuf.";
132 EncryptDataResponse response;
133 response.set_encrypted_data(encrypted_output);
134 response.set_ret(shill_protos::OK);
135 output->clear();
136 LOG(INFO) << "Serializing protobuf.";
137 if (!response.SerializeToString(output)) {
138 LOG(ERROR) << "Failed while writing encrypted data.";
139 return false;
140 }
141 LOG(INFO) << "Encoding finished successfully.";
142 }
143
144 return operation_successful;
145 }
146
147 // Verify that the destination described by |certificate| is valid.
148 //
149 // 1) The MAC address listed in the certificate matches |connected_mac|.
150 // 2) The certificate is a valid PEM encoded certificate signed by our
151 // trusted CA.
152 // 3) |signed_data| matches the hashed |unsigned_data| encrypted with
153 // the public key in |certificate|.
154 //
155 // All pointers should be valid, but point to NULL values. Sets* ptr to
156 // NULL or a valid object which should be freed with the appropriate destructor
157 // upon completion.
VerifyCredentialsImpl(const string & certificate,const string & signed_data,const string & unsigned_data,const string & connected_mac,RSA ** rsa_ptr,EVP_PKEY ** pkey_ptr,BIO ** raw_certificate_bio_ptr,X509 ** x509_ptr)158 bool VerifyCredentialsImpl(const string& certificate,
159 const string& signed_data,
160 const string& unsigned_data,
161 const string& connected_mac,
162 RSA** rsa_ptr,
163 EVP_PKEY** pkey_ptr,
164 BIO** raw_certificate_bio_ptr,
165 X509** x509_ptr) {
166 CHECK(rsa_ptr);
167 CHECK(pkey_ptr);
168 CHECK(raw_certificate_bio_ptr);
169 CHECK(x509_ptr);
170 CHECK(!*rsa_ptr);
171 CHECK(!*pkey_ptr);
172 CHECK(!*raw_certificate_bio_ptr);
173 CHECK(!*x509_ptr);
174
175 *rsa_ptr = RSA_new();
176 RSA* rsa = *rsa_ptr;
177 *pkey_ptr = EVP_PKEY_new();
178 EVP_PKEY* pkey = *pkey_ptr;
179 if (!rsa || !pkey) {
180 LOG(ERROR) << "Failed to allocate key.";
181 return false;
182 }
183
184 rsa->e = BN_new();
185 rsa->n = BN_new();
186 if (!rsa->e || !rsa->n ||
187 !BN_set_word(rsa->e, RSA_F4) ||
188 !BN_hex2bn(&rsa->n, kTrustedCAModulus)) {
189 LOG(ERROR) << "Failed to allocate key pieces.";
190 return false;
191 }
192
193 if (!EVP_PKEY_assign_RSA(pkey, rsa)) {
194 LOG(ERROR) << "Failed to assign RSA to PKEY.";
195 return false;
196 }
197
198 *rsa_ptr = NULL; // pkey took ownership
199 // Another helpfully unmarked const interface.
200 *raw_certificate_bio_ptr = BIO_new_mem_buf(
201 const_cast<char*>(certificate.data()), certificate.length());
202 BIO* raw_certificate_bio = *raw_certificate_bio_ptr;
203 if (!raw_certificate_bio) {
204 LOG(ERROR) << "Failed to allocate openssl certificate buffer.";
205 return false;
206 }
207
208 // No callback for a passphrase, and no passphrase either.
209 *x509_ptr = PEM_read_bio_X509(raw_certificate_bio, NULL, NULL, NULL);
210 X509* x509 = *x509_ptr;
211 if (!x509) {
212 LOG(ERROR) << "Failed to parse certificate.";
213 return false;
214 }
215
216 if (X509_verify(x509, pkey) <= 0) {
217 LOG(ERROR) << "Failed to verify certificate.";
218 return false;
219 }
220
221 // Check that the device listed in the certificate is correct.
222 char device_name[100]; // A longer CN will truncate.
223 const int device_name_length = X509_NAME_get_text_by_NID(
224 x509->cert_info->subject,
225 NID_commonName,
226 device_name,
227 arraysize(device_name));
228 if (device_name_length == -1) {
229 LOG(ERROR) << "Subject invalid.";
230 return false;
231 }
232
233 // Something like evt_e161 001a11ffacdf
234 string device_cn(device_name, device_name_length);
235 const size_t space_idx = device_cn.rfind(' ');
236 if (space_idx == string::npos) {
237 LOG(ERROR) << "Badly formatted subject";
238 return false;
239 }
240
241 string device_mac;
242 for (size_t i = space_idx + 1; i < device_cn.length(); ++i) {
243 device_mac.push_back(tolower(device_cn[i]));
244 }
245 if (connected_mac != device_mac) {
246 LOG(ERROR) << "MAC addresses don't match.";
247 return false;
248 }
249
250 // Excellent, the certificate checks out, now make sure that the certificate
251 // matches the unsigned data presented.
252 // We're going to verify that hash(unsigned_data) == public(signed_data)
253 EVP_PKEY* cert_pubkey = X509_get_pubkey(x509);
254 if (!cert_pubkey) {
255 LOG(ERROR) << "Unable to extract public key from certificate.";
256 return false;
257 }
258
259 RSA* cert_rsa = EVP_PKEY_get1_RSA(cert_pubkey);
260 if (!cert_rsa) {
261 LOG(ERROR) << "Failed to extract RSA key from certificate.";
262 return false;
263 }
264
265 const unsigned char* signature =
266 reinterpret_cast<const unsigned char*>(signed_data.data());
267 const size_t signature_len = signed_data.length();
268 unsigned char* unsigned_data_bytes =
269 reinterpret_cast<unsigned char*>(const_cast<char*>(
270 unsigned_data.data()));
271 const size_t unsigned_data_len = unsigned_data.length();
272 unsigned char digest[SHA_DIGEST_LENGTH];
273 if (signature_len > numeric_limits<unsigned int>::max()) {
274 LOG(ERROR) << "Arguments to signature match were too large.";
275 return false;
276 }
277 SHA1(unsigned_data_bytes, unsigned_data_len, digest);
278 if (RSA_verify(NID_sha1, digest, arraysize(digest),
279 signature, signature_len, cert_rsa) != 1) {
280 LOG(ERROR) << "Signed blobs did not match.";
281 return false;
282 }
283
284 return true;
285 }
286
287 // Verify the credentials of the destination described in |raw_input|. Takes
288 // a serialized VerifyCredentialsMessage protobuffer in |raw_input|, returns a
289 // serialized VerifyCredentialsResponse protobuffer in |output| on success.
290 // Returns false if the credentials fail to meet a check, and true on success.
VerifyCredentials(const string & raw_input,string * output)291 bool VerifyCredentials(const string& raw_input, string* output) {
292 VerifyCredentialsMessage message;
293 if (!message.ParseFromString(raw_input)) {
294 LOG(ERROR) << "Failed to read VerifyCredentialsMessage from stdin.";
295 return false;
296 }
297
298 if (!message.has_certificate() || !message.has_signed_data() ||
299 !message.has_unsigned_data() || !message.has_mac_address()) {
300 LOG(ERROR) << "Request lacked necessary fields.";
301 return false;
302 }
303
304 string connected_mac;
305 for (size_t i = 0; i < message.mac_address().length(); ++i) {
306 const char c = message.mac_address()[i];
307 if (c != ':') {
308 connected_mac.push_back(tolower(c));
309 }
310 }
311 if (connected_mac.length() != kMacLength) {
312 LOG(ERROR) << "shill gave us a bad MAC?";
313 return false;
314 }
315
316 RSA* rsa = NULL;
317 EVP_PKEY* pkey = NULL;
318 BIO* raw_certificate_bio = NULL;
319 X509* x509 = NULL;
320 bool operation_successful = VerifyCredentialsImpl(message.certificate(),
321 message.signed_data(), message.unsigned_data(), connected_mac,
322 &rsa, &pkey, &raw_certificate_bio, &x509);
323 if (x509) {
324 X509_free(x509);
325 x509 = NULL;
326 }
327 if (raw_certificate_bio) {
328 BIO_free(raw_certificate_bio);
329 raw_certificate_bio = NULL;
330 }
331 if (pkey) {
332 EVP_PKEY_free(pkey);
333 pkey = NULL;
334 }
335 if (rsa) {
336 RSA_free(rsa);
337 rsa = NULL;
338 }
339
340 if (operation_successful) {
341 LOG(INFO) << "Filling out protobuf.";
342 VerifyCredentialsResponse response;
343 response.set_ret(shill_protos::OK);
344 output->clear();
345 LOG(INFO) << "Serializing protobuf.";
346 if (!response.SerializeToString(output)) {
347 LOG(ERROR) << "Failed while writing encrypted data.";
348 return false;
349 }
350 LOG(INFO) << "Encoding finished successfully.";
351 }
352
353 return operation_successful;
354 }
355
356 // Read the full stdin stream into a buffer, and execute the operation
357 // described in |command| with the contends of the stdin buffer. Write
358 // the serialized protocol buffer output of the command to stdout.
ParseAndExecuteCommand(const string & command)359 bool ParseAndExecuteCommand(const string& command) {
360 string raw_input;
361 char input_buffer[512];
362 LOG(INFO) << "Reading input for command " << command << ".";
363 while (true) {
364 const ssize_t bytes_read = HANDLE_EINTR(read(STDIN_FILENO,
365 input_buffer,
366 arraysize(input_buffer)));
367 if (bytes_read < 0) {
368 // Abort abort abort.
369 LOG(ERROR) << "Failed while reading from stdin.";
370 return false;
371 } else if (bytes_read > 0) {
372 raw_input.append(input_buffer, bytes_read);
373 } else {
374 break;
375 }
376 }
377 LOG(INFO) << "Read " << raw_input.length() << " bytes.";
378 ERR_clear_error();
379 string raw_output;
380 bool ret = false;
381 if (command == kCommandVerify) {
382 ret = VerifyCredentials(raw_input, &raw_output);
383 } else if (command == kCommandEncrypt) {
384 ret = EncryptByteString(raw_input, &raw_output);
385 } else {
386 LOG(ERROR) << "Invalid usage.";
387 return false;
388 }
389 if (!ret) {
390 LOG(ERROR) << "Last OpenSSL error: "
391 << ERR_reason_error_string(ERR_get_error());
392 }
393 size_t total_bytes_written = 0;
394 while (total_bytes_written < raw_output.length()) {
395 const ssize_t bytes_written = HANDLE_EINTR(write(
396 STDOUT_FILENO,
397 raw_output.data() + total_bytes_written,
398 raw_output.length() - total_bytes_written));
399 if (bytes_written < 0) {
400 LOG(ERROR) << "Result write failed with: " << errno;
401 return false;
402 }
403 total_bytes_written += bytes_written;
404 }
405 return ret;
406 }
407
408 } // namespace
409
main(int argc,char ** argv)410 int main(int argc, char** argv) {
411 base::CommandLine::Init(argc, argv);
412 brillo::InitLog(brillo::kLogToStderr | brillo::kLogHeader);
413 LOG(INFO) << "crypto-util in action";
414
415 if (argc != 2) {
416 LOG(ERROR) << "Invalid usage";
417 return EXIT_FAILURE;
418 }
419 const char* command = argv[1];
420 if (strcmp(kCommandVerify, command) && strcmp(kCommandEncrypt, command)) {
421 LOG(ERROR) << "Invalid command";
422 return EXIT_FAILURE;
423 }
424
425 CRYPTO_malloc_init();
426 ERR_load_crypto_strings();
427 OpenSSL_add_all_algorithms();
428 int return_code = EXIT_FAILURE;
429 if (ParseAndExecuteCommand(command)) {
430 return_code = EXIT_SUCCESS;
431 }
432 close(STDOUT_FILENO);
433 close(STDIN_FILENO);
434
435 CONF_modules_unload(1);
436 OBJ_cleanup();
437 EVP_cleanup();
438 CRYPTO_cleanup_all_ex_data();
439 ERR_remove_thread_state(NULL);
440 ERR_free_strings();
441
442 return return_code;
443 }
444