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
38using shill_protos::EncryptDataMessage;
39using shill_protos::EncryptDataResponse;
40using shill_protos::VerifyCredentialsMessage;
41using shill_protos::VerifyCredentialsResponse;
42using std::numeric_limits;
43using std::string;
44using std::vector;
45
46namespace {
47
48const char kTrustedCAModulus[] =
49    "BC2280BD80F63A21003BAE765E357F3DC3645C559486342F058728CDF7698C17B350A7B8"
50    "82FADFC7432DD67EABA06FB7137280A44715C1209950CDEC1462095BA498CDD241B6364E"
51    "FFE82E32304A81A842A36C9B336ECAB2F55366E02753861A851EA7393F4A778EFB546666"
52    "FB5854C05E39C7F550060BE08AD4CEE16A551F8B1700E669A327E60825693C129D8D052C"
53    "D62EA231DEB45250D62049DE71A0F9AD204012F1DD25EBD5E6B836F4D68F7FCA43DCD710"
54    "5BE63F518A85B3F3FFF6032DCB234F9CAD18E793058CAC529AF74CE9997ABE6E7E4D0AE3"
55    "C61CA993FA3AA5915D1CBD66EBCC60DC8674CACFF8921C987D57FA61479EAB80B7E44880"
56    "2A92C51B";
57const char kCommandVerify[] = "verify";
58const char kCommandEncrypt[] = "encrypt";
59const 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.
68bool 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.
109bool 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.
158bool 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.
291bool 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.
359bool 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
410int 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