• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // Copyright (C) 2021 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 #include "host/commands/test_gce_driver/key_pair.h"
17 
18 #include <openssl/bio.h>
19 #include <openssl/evp.h>
20 #include <openssl/pem.h>
21 #include <openssl/rsa.h>
22 
23 #include <memory>
24 #include <string>
25 
26 #include <android-base/logging.h>
27 
28 #include "common/libs/utils/result.h"
29 #include "common/libs/utils/subprocess.h"
30 
31 namespace cuttlefish {
32 
SslRecordErrCallback(const char * str,size_t len,void * data)33 static int SslRecordErrCallback(const char* str, size_t len, void* data) {
34   *reinterpret_cast<std::string*>(data) = std::string(str, len);
35   return 1;  // success
36 }
37 
38 class BoringSslKeyPair : public KeyPair {
39  public:
40   /*
41    * We interact with boringssl directly here to avoid ssh-keygen writing
42    * directly to the filesystem. The relevant ssh-keygen command here is
43    *
44    * $ ssh-keygen -t rsa -N "" -f ${TARGET}
45    *
46    * which unfortunately tries to write to `${TARGET}.pub`, making it hard to
47    * use something like /dev/stdout or /proc/self/fd/1 to get the keys.
48    */
CreateRsa(size_t bytes)49   static Result<std::unique_ptr<KeyPair>> CreateRsa(size_t bytes) {
50     std::unique_ptr<EVP_PKEY_CTX, void (*)(EVP_PKEY_CTX*)> ctx{
51         EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL), EVP_PKEY_CTX_free};
52     std::string error;
53     if (!ctx) {
54       ERR_print_errors_cb(SslRecordErrCallback, &error);
55       return CF_ERR("EVP_PKEY_CTX_new_id failed: " << error);
56     }
57     if (EVP_PKEY_keygen_init(ctx.get()) <= 0) {
58       ERR_print_errors_cb(SslRecordErrCallback, &error);
59       return CF_ERR("EVP_PKEY_keygen_init failed: " << error);
60     }
61     if (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), bytes) <= 0) {
62       ERR_print_errors_cb(SslRecordErrCallback, &error);
63       return CF_ERR("EVP_PKEY_CTX_set_rsa_keygen_bits failed: " << error);
64     }
65 
66     EVP_PKEY* pkey = nullptr;
67     if (EVP_PKEY_keygen(ctx.get(), &pkey) <= 0) {
68       ERR_print_errors_cb(SslRecordErrCallback, &error);
69       return CF_ERR("EVP_PKEY_keygen failed: " << error);
70     }
71     return std::unique_ptr<KeyPair>{new BoringSslKeyPair(pkey)};
72   }
73 
PemPrivateKey() const74   Result<std::string> PemPrivateKey() const override {
75     std::unique_ptr<BIO, int (*)(BIO*)> bo(BIO_new(BIO_s_mem()), BIO_free);
76     std::string error;
77     if (!bo) {
78       ERR_print_errors_cb(SslRecordErrCallback, &error);
79       return CF_ERR("BIO_new failed: " << error);
80     }
81     if (!PEM_write_bio_PrivateKey(bo.get(), pkey_.get(), NULL, NULL, 0, 0,
82                                   NULL)) {
83       ERR_print_errors_cb(SslRecordErrCallback, &error);
84       return CF_ERR("PEM_write_bio_PrivateKey failed: " << error);
85     }
86     std::string priv(BIO_pending(bo.get()), ' ');
87     auto written = BIO_read(bo.get(), priv.data(), priv.size());
88     if (written != priv.size()) {
89       return CF_ERR("Unexpected amount of data written: " << written << " != "
90                                                           << priv.size());
91     }
92     return priv;
93   }
94 
PemPublicKey() const95   Result<std::string> PemPublicKey() const override {
96     std::unique_ptr<BIO, int (*)(BIO*)> bo(BIO_new(BIO_s_mem()), BIO_free);
97     std::string error;
98     if (!bo) {
99       ERR_print_errors_cb(SslRecordErrCallback, &error);
100       return CF_ERR("BIO_new failed: " << error);
101     }
102     if (!PEM_write_bio_PUBKEY(bo.get(), pkey_.get())) {
103       ERR_print_errors_cb(SslRecordErrCallback, &error);
104       return CF_ERR("PEM_write_bio_PUBKEY failed: " << error);
105     }
106 
107     std::string priv(BIO_pending(bo.get()), ' ');
108     auto written = BIO_read(bo.get(), priv.data(), priv.size());
109     if (written != priv.size()) {
110       return CF_ERR("Unexpected amount of data written: " << written << " != "
111                                                           << priv.size());
112     }
113     return priv;
114   }
115 
116   /*
117    * OpenSSH has its own distinct format for public keys, which cannot be
118    * produced directly with OpenSSL/BoringSSL primitives. Luckily it is possible
119    * to convert the BoringSSL-generated RSA key without touching the filesystem.
120    */
OpenSshPublicKey() const121   Result<std::string> OpenSshPublicKey() const override {
122     auto pem_pubkey =
123         CF_EXPECT(PemPublicKey(), "Failed to get pem public key: ");
124     auto fd = SharedFD::MemfdCreateWithData("", pem_pubkey);
125     CF_EXPECT(fd->IsOpen(),
126               "Could not create pubkey memfd: " << fd->StrError());
127     Command cmd("/usr/bin/ssh-keygen");
128     cmd.AddParameter("-i");
129     cmd.AddParameter("-f");
130     cmd.AddParameter("/proc/self/fd/0");
131     cmd.RedirectStdIO(Subprocess::StdIOChannel::kStdIn, fd);
132     cmd.AddParameter("-m");
133     cmd.AddParameter("PKCS8");
134     std::string out;
135     std::string err;
136     CF_EXPECT(RunWithManagedStdio(std::move(cmd), nullptr, &out, &err) == 0,
137               "Could not convert pem key to openssh key. "
138                   << "stdout=\"" << out << "\", stderr=\"" << err << "\"");
139     return out;
140   }
141 
142  private:
BoringSslKeyPair(EVP_PKEY * pkey)143   BoringSslKeyPair(EVP_PKEY* pkey) : pkey_(pkey, EVP_PKEY_free) {}
144 
145   std::unique_ptr<EVP_PKEY, void (*)(EVP_PKEY*)> pkey_;
146 };
147 
CreateRsa(size_t bytes)148 Result<std::unique_ptr<KeyPair>> KeyPair::CreateRsa(size_t bytes) {
149   return BoringSslKeyPair::CreateRsa(bytes);
150 }
151 
152 }  // namespace cuttlefish
153