Move adb RSA utilities into its own library.
Since both the client and daemon will now be generating keys.
BUG: b/111434128
Test: atest adb_crypto_test
Change-Id: I6fac562ae5629ab30b6639fbd88d822dae6e96bd
diff --git a/Android.bp b/Android.bp
index 675525c..c71138a 100644
--- a/Android.bp
+++ b/Android.bp
@@ -255,6 +255,8 @@
},
static_libs: [
+ "libadb_crypto",
+ "libadb_protos",
"libbase",
"libcrypto_utils",
"libcrypto",
@@ -272,6 +274,7 @@
defaults: ["adb_defaults"],
srcs: libadb_test_srcs,
static_libs: [
+ "libadb_crypto",
"libadb_host",
"libbase",
"libcutils",
@@ -347,6 +350,7 @@
],
static_libs: [
+ "libadb_crypto",
"libadb_host",
"libandroidfw",
"libbase",
@@ -422,6 +426,7 @@
],
shared_libs: [
+ "libadb_crypto",
"libadbd_auth",
"libasyncio",
"libbase",
@@ -765,6 +770,7 @@
"fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
],
static_libs: [
+ "libadb_crypto",
"libadb_host",
"libandroidfw",
"libbase",
diff --git a/client/auth.cpp b/client/auth.cpp
index e8be784..dcf4bc0 100644
--- a/client/auth.cpp
+++ b/client/auth.cpp
@@ -29,6 +29,7 @@
#include <set>
#include <string>
+#include <adb/crypto/rsa_2048_key.h>
#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -53,100 +54,50 @@
*new std::map<std::string, std::shared_ptr<RSA>>;
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
-static std::string get_user_info() {
- std::string hostname;
- if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
-#if !defined(_WIN32)
- char buf[64];
- if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
-#endif
- if (hostname.empty()) hostname = "unknown";
+using namespace adb::crypto;
- std::string username;
- if (getenv("LOGNAME")) username = getenv("LOGNAME");
-#if !defined(_WIN32)
- if (username.empty() && getlogin()) username = getlogin();
-#endif
- if (username.empty()) hostname = "unknown";
-
- return " " + username + "@" + hostname;
-}
-
-static bool calculate_public_key(std::string* out, RSA* private_key) {
- uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
- if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
- LOG(ERROR) << "Failed to convert to public key";
- return false;
- }
-
- size_t expected_length;
- if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
- LOG(ERROR) << "Public key too large to base64 encode";
- return false;
- }
-
- out->resize(expected_length);
- size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
- sizeof(binary_key_data));
- out->resize(actual_length);
- out->append(get_user_info());
- return true;
-}
-
-static int generate_key(const std::string& file) {
+static bool generate_key(const std::string& file) {
LOG(INFO) << "generate_key(" << file << ")...";
- mode_t old_mask;
- FILE *f = nullptr;
- int ret = 0;
+ auto rsa_2048 = CreateRSA2048Key();
+ if (!rsa_2048) {
+ LOG(ERROR) << "Unable to create key";
+ return false;
+ }
std::string pubkey;
- EVP_PKEY* pkey = EVP_PKEY_new();
- BIGNUM* exponent = BN_new();
- RSA* rsa = RSA_new();
- if (!pkey || !exponent || !rsa) {
- LOG(ERROR) << "Failed to allocate key";
- goto out;
- }
+ RSA* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ CHECK(rsa);
- BN_set_word(exponent, RSA_F4);
- RSA_generate_key_ex(rsa, 2048, exponent, nullptr);
- EVP_PKEY_set1_RSA(pkey, rsa);
-
- if (!calculate_public_key(&pubkey, rsa)) {
+ if (!CalculatePublicKey(&pubkey, rsa)) {
LOG(ERROR) << "failed to calculate public key";
- goto out;
+ return false;
}
- old_mask = umask(077);
+ mode_t old_mask = umask(077);
- f = fopen(file.c_str(), "w");
+ std::unique_ptr<FILE, decltype(&fclose)> f(nullptr, &fclose);
+ f.reset(fopen(file.c_str(), "w"));
if (!f) {
PLOG(ERROR) << "Failed to open " << file;
umask(old_mask);
- goto out;
+ return false;
}
umask(old_mask);
- if (!PEM_write_PrivateKey(f, pkey, nullptr, nullptr, 0, nullptr, nullptr)) {
+ if (!PEM_write_PrivateKey(f.get(), rsa_2048->GetEvpPkey(), nullptr, nullptr, 0, nullptr,
+ nullptr)) {
LOG(ERROR) << "Failed to write key";
- goto out;
+ return false;
}
if (!android::base::WriteStringToFile(pubkey, file + ".pub")) {
PLOG(ERROR) << "failed to write public key";
- goto out;
+ return false;
}
- ret = 1;
-
-out:
- if (f) fclose(f);
- EVP_PKEY_free(pkey);
- RSA_free(rsa);
- BN_free(exponent);
- return ret;
+ return true;
}
static std::string hash_key(RSA* key) {
@@ -325,7 +276,7 @@
if (!privkey) {
return false;
}
- return calculate_public_key(out, privkey.get());
+ return CalculatePublicKey(out, privkey.get());
}
std::string adb_auth_get_userkey() {
@@ -343,7 +294,7 @@
}
int adb_auth_keygen(const char* filename) {
- return (generate_key(filename) == 0);
+ return !generate_key(filename);
}
int adb_auth_pubkey(const char* filename) {
diff --git a/crypto/Android.bp b/crypto/Android.bp
new file mode 100644
index 0000000..da4869a
--- /dev/null
+++ b/crypto/Android.bp
@@ -0,0 +1,85 @@
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+cc_defaults {
+ name: "libadb_crypto_defaults",
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Wthread-safety",
+ "-Werror",
+ ],
+
+ compile_multilib: "both",
+
+ srcs: [
+ "key.cpp",
+ "rsa_2048_key.cpp",
+ "x509_generator.cpp",
+ ],
+
+ target: {
+ windows: {
+ compile_multilib: "first",
+ enabled: true,
+ },
+ },
+
+ export_include_dirs: ["include"],
+
+ visibility: [
+ "//system/core/adb:__subpackages__",
+ ],
+
+ host_supported: true,
+ recovery_available: true,
+
+ stl: "libc++_static",
+
+ shared_libs: [
+ "libadb_protos",
+ "libbase",
+ "liblog",
+ "libcrypto",
+ "libcrypto_utils",
+ ],
+}
+
+cc_library {
+ name: "libadb_crypto",
+ defaults: ["libadb_crypto_defaults"],
+
+ apex_available: [
+ "com.android.adbd",
+ "test_com.android.adbd",
+ ],
+
+ static_libs: [
+ "libadb_protos",
+ ],
+}
+
+// For running atest (b/147158681)
+cc_library_static {
+ name: "libadb_crypto_static",
+ defaults: ["libadb_crypto_defaults"],
+
+ apex_available: [
+ "//apex_available:platform",
+ ],
+
+ static_libs: [
+ "libadb_protos_static",
+ ],
+}
diff --git a/crypto/include/adb/crypto/key.h b/crypto/include/adb/crypto/key.h
new file mode 100644
index 0000000..d9ce69e
--- /dev/null
+++ b/crypto/include/adb/crypto/key.h
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+#include <openssl/evp.h>
+
+#include "key_type.pb.h"
+
+namespace adb {
+namespace crypto {
+
+// Class that represents a public/private key pair.
+class Key {
+ public:
+ explicit Key(bssl::UniquePtr<EVP_PKEY>&& pkey, adb::proto::KeyType type)
+ : pkey_(std::move(pkey)), key_type_(type) {}
+ Key(Key&&) = default;
+ Key& operator=(Key&&) = default;
+
+ EVP_PKEY* GetEvpPkey() const { return pkey_.get(); }
+ adb::proto::KeyType GetKeyType() const { return key_type_; }
+ static std::string ToPEMString(EVP_PKEY* pkey);
+
+ private:
+ bssl::UniquePtr<EVP_PKEY> pkey_;
+ adb::proto::KeyType key_type_;
+}; // Key
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/include/adb/crypto/rsa_2048_key.h b/crypto/include/adb/crypto/rsa_2048_key.h
new file mode 100644
index 0000000..2983a84
--- /dev/null
+++ b/crypto/include/adb/crypto/rsa_2048_key.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>
+#include <optional>
+
+#include "adb/crypto/key.h"
+
+namespace adb {
+namespace crypto {
+
+// Create a new RSA2048 key pair.
+std::optional<Key> CreateRSA2048Key();
+
+// Generates the public key from the RSA private key.
+bool CalculatePublicKey(std::string* out, RSA* private_key);
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/include/adb/crypto/x509_generator.h b/crypto/include/adb/crypto/x509_generator.h
new file mode 100644
index 0000000..a269243
--- /dev/null
+++ b/crypto/include/adb/crypto/x509_generator.h
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <openssl/x509v3.h>
+
+namespace adb {
+namespace crypto {
+
+// Generate a X.509 certificate based on the key |pkey|.
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey);
+
+// Convert X509* to PEM string format
+std::string X509ToPEMString(X509* x509);
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/key.cpp b/crypto/key.cpp
new file mode 100644
index 0000000..4d87006
--- /dev/null
+++ b/crypto/key.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/key.h"
+
+#include <android-base/logging.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+// static
+std::string Key::ToPEMString(EVP_PKEY* pkey) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+ int rc = PEM_write_bio_PKCS8PrivateKey(bio.get(), pkey, nullptr, nullptr, 0, nullptr, nullptr);
+ if (rc != 1) {
+ LOG(ERROR) << "PEM_write_bio_PKCS8PrivateKey failed";
+ return "";
+ }
+
+ BUF_MEM* mem = nullptr;
+ BIO_get_mem_ptr(bio.get(), &mem);
+ if (!mem || !mem->data || !mem->length) {
+ LOG(ERROR) << "BIO_get_mem_ptr failed";
+ return "";
+ }
+
+ return std::string(mem->data, mem->length);
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/rsa_2048_key.cpp b/crypto/rsa_2048_key.cpp
new file mode 100644
index 0000000..7911af9
--- /dev/null
+++ b/crypto/rsa_2048_key.cpp
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/rsa_2048_key.h"
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+std::string get_user_info() {
+ std::string hostname;
+ if (getenv("HOSTNAME")) hostname = getenv("HOSTNAME");
+#if !defined(_WIN32)
+ char buf[64];
+ if (hostname.empty() && gethostname(buf, sizeof(buf)) != -1) hostname = buf;
+#endif
+ if (hostname.empty()) hostname = "unknown";
+
+ std::string username;
+ if (getenv("LOGNAME")) username = getenv("LOGNAME");
+#if !defined(_WIN32)
+ if (username.empty() && getlogin()) username = getlogin();
+#endif
+ if (username.empty()) hostname = "unknown";
+
+ return " " + username + "@" + hostname;
+}
+
+} // namespace
+
+bool CalculatePublicKey(std::string* out, RSA* private_key) {
+ uint8_t binary_key_data[ANDROID_PUBKEY_ENCODED_SIZE];
+ if (!android_pubkey_encode(private_key, binary_key_data, sizeof(binary_key_data))) {
+ LOG(ERROR) << "Failed to convert to public key";
+ return false;
+ }
+
+ size_t expected_length;
+ if (!EVP_EncodedLength(&expected_length, sizeof(binary_key_data))) {
+ LOG(ERROR) << "Public key too large to base64 encode";
+ return false;
+ }
+
+ out->resize(expected_length);
+ size_t actual_length = EVP_EncodeBlock(reinterpret_cast<uint8_t*>(out->data()), binary_key_data,
+ sizeof(binary_key_data));
+ out->resize(actual_length);
+ out->append(get_user_info());
+ return true;
+}
+
+std::optional<Key> CreateRSA2048Key() {
+ bssl::UniquePtr<EVP_PKEY> pkey(EVP_PKEY_new());
+ bssl::UniquePtr<BIGNUM> exponent(BN_new());
+ bssl::UniquePtr<RSA> rsa(RSA_new());
+ if (!pkey || !exponent || !rsa) {
+ LOG(ERROR) << "Failed to allocate key";
+ return std::nullopt;
+ }
+
+ BN_set_word(exponent.get(), RSA_F4);
+ RSA_generate_key_ex(rsa.get(), 2048, exponent.get(), nullptr);
+ EVP_PKEY_set1_RSA(pkey.get(), rsa.get());
+
+ return std::optional<Key>{Key(std::move(pkey), adb::proto::KeyType::RSA_2048)};
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/tests/Android.bp b/crypto/tests/Android.bp
new file mode 100644
index 0000000..b32dcf7
--- /dev/null
+++ b/crypto/tests/Android.bp
@@ -0,0 +1,41 @@
+//
+// Copyright (C) 2019 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+cc_test {
+ name: "adb_crypto_test",
+ srcs: [
+ "rsa_2048_key_test.cpp",
+ "x509_generator_test.cpp",
+ ],
+
+ compile_multilib: "first",
+
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libprotobuf-cpp-lite",
+ ],
+
+ // Let's statically link them so we don't have to install it onto the
+ // system image for testing.
+ static_libs: [
+ "libadb_crypto_static",
+ "libadb_protos_static",
+ ],
+
+ test_suites: ["device-tests"],
+}
diff --git a/crypto/tests/key_test.cpp b/crypto/tests/key_test.cpp
new file mode 100644
index 0000000..1feb6e8
--- /dev/null
+++ b/crypto/tests/key_test.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+ auto rsa_2048 = CreateRSA2048Key();
+ EXPECT_NE(rsa_2048, std::nullopt);
+ EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+ ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+ // The public key string format is expected to be: "<pub_key> <host_name>"
+ std::string pub_key_plus_name;
+ auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+ std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+ EXPECT_EQ(split.size(), 2);
+
+ LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+ // Try to sign something and decode it.
+ const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+ std::vector<uint8_t> sig(RSA_size(rsa));
+ unsigned sig_len;
+ EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+ &sig_len, rsa),
+ 1);
+ sig.resize(sig_len);
+
+ {
+ uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+ const std::string& pubkey = split[0];
+ ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+ RSA* key = nullptr;
+ ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+ EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+ sig.data(), sig.size(), key),
+ 1);
+ RSA_free(key);
+ }
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/tests/rsa_2048_key_test.cpp b/crypto/tests/rsa_2048_key_test.cpp
new file mode 100644
index 0000000..1d8880e
--- /dev/null
+++ b/crypto/tests/rsa_2048_key_test.cpp
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <resolv.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/err.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(RSA2048Key, Smoke) {
+ auto rsa_2048 = CreateRSA2048Key();
+ EXPECT_NE(rsa_2048, std::nullopt);
+ EXPECT_EQ(rsa_2048->GetKeyType(), adb::proto::KeyType::RSA_2048);
+ ASSERT_NE(rsa_2048->GetEvpPkey(), nullptr);
+
+ // The public key string format is expected to be: "<pub_key> <host_name>"
+ std::string pub_key_plus_name;
+ auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+ std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+ EXPECT_EQ(split.size(), 2);
+
+ LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+
+ std::string pemString = Key::ToPEMString(rsa_2048->GetEvpPkey());
+ ASSERT_FALSE(pemString.empty());
+
+ // Try to sign something and decode it.
+ const char token[SHA_DIGEST_LENGTH] = "abcdefghij123456789";
+ std::vector<uint8_t> sig(RSA_size(rsa));
+ unsigned sig_len;
+ EXPECT_EQ(RSA_sign(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token), sig.data(),
+ &sig_len, rsa),
+ 1);
+ sig.resize(sig_len);
+
+ {
+ uint8_t keybuf[ANDROID_PUBKEY_ENCODED_SIZE + 1];
+ const std::string& pubkey = split[0];
+ ASSERT_EQ(b64_pton(pubkey.c_str(), keybuf, sizeof(keybuf)), ANDROID_PUBKEY_ENCODED_SIZE);
+ RSA* key = nullptr;
+ ASSERT_TRUE(android_pubkey_decode(keybuf, ANDROID_PUBKEY_ENCODED_SIZE, &key));
+ EXPECT_EQ(RSA_verify(NID_sha1, reinterpret_cast<const uint8_t*>(token), sizeof(token),
+ sig.data(), sig.size(), key),
+ 1);
+ RSA_free(key);
+ }
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/tests/x509_generator_test.cpp b/crypto/tests/x509_generator_test.cpp
new file mode 100644
index 0000000..281776b
--- /dev/null
+++ b/crypto/tests/x509_generator_test.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+
+namespace adb {
+namespace crypto {
+
+TEST(X509Generator, Smoke) {
+ auto rsa_2048 = CreateRSA2048Key();
+
+ std::string pub_key_plus_name;
+ auto* rsa = EVP_PKEY_get0_RSA(rsa_2048->GetEvpPkey());
+ ASSERT_TRUE(CalculatePublicKey(&pub_key_plus_name, rsa));
+ std::vector<std::string> split = android::base::Split(std::string(pub_key_plus_name), " \t");
+ EXPECT_EQ(split.size(), 2);
+
+ LOG(INFO) << "pub_key=[" << pub_key_plus_name << "]";
+ auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey());
+ ASSERT_NE(x509_cert.get(), nullptr);
+
+ std::string x509_str = X509ToPEMString(x509_cert.get());
+ ASSERT_FALSE(x509_str.empty());
+}
+
+} // namespace crypto
+} // namespace adb
diff --git a/crypto/x509_generator.cpp b/crypto/x509_generator.cpp
new file mode 100644
index 0000000..43b8153
--- /dev/null
+++ b/crypto/x509_generator.cpp
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "adb/crypto/x509_generator.h"
+
+#include <vector>
+
+#include <android-base/logging.h>
+#include <crypto_utils/android_pubkey.h>
+#include <openssl/bn.h>
+#include <openssl/pem.h>
+#include <openssl/rsa.h>
+
+namespace adb {
+namespace crypto {
+
+namespace {
+
+const char kBasicConstraints[] = "critical,CA:TRUE";
+const char kKeyUsage[] = "critical,keyCertSign,cRLSign,digitalSignature";
+const char kSubjectKeyIdentifier[] = "hash";
+constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+
+bool add_ext(X509* cert, int nid, const char* value) {
+ size_t len = strlen(value) + 1;
+ std::vector<char> mutableValue(value, value + len);
+ X509V3_CTX context;
+
+ X509V3_set_ctx_nodb(&context);
+
+ X509V3_set_ctx(&context, cert, cert, nullptr, nullptr, 0);
+ X509_EXTENSION* ex = X509V3_EXT_nconf_nid(nullptr, &context, nid, mutableValue.data());
+ if (!ex) {
+ return false;
+ }
+
+ X509_add_ext(cert, ex, -1);
+ X509_EXTENSION_free(ex);
+ return true;
+}
+
+} // namespace
+
+bssl::UniquePtr<X509> GenerateX509Certificate(EVP_PKEY* pkey) {
+ CHECK(pkey);
+ bssl::UniquePtr<X509> x509(X509_new());
+ if (!x509) {
+ LOG(ERROR) << "Unable to allocate x509 container";
+ return nullptr;
+ }
+ X509_set_version(x509.get(), 2);
+
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), 1);
+ X509_gmtime_adj(X509_get_notBefore(x509.get()), 0);
+ X509_gmtime_adj(X509_get_notAfter(x509.get()), kCertLifetimeSeconds);
+
+ if (!X509_set_pubkey(x509.get(), pkey)) {
+ LOG(ERROR) << "Unable to set x509 public key";
+ return nullptr;
+ }
+
+ X509_NAME* name = X509_get_subject_name(x509.get());
+ if (!name) {
+ LOG(ERROR) << "Unable to get x509 subject name";
+ return nullptr;
+ }
+ X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
+ X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
+ reinterpret_cast<const unsigned char*>("Adb"), -1, -1, 0);
+ if (!X509_set_issuer_name(x509.get(), name)) {
+ LOG(ERROR) << "Unable to set x509 issuer name";
+ return nullptr;
+ }
+
+ add_ext(x509.get(), NID_basic_constraints, kBasicConstraints);
+ add_ext(x509.get(), NID_key_usage, kKeyUsage);
+ add_ext(x509.get(), NID_subject_key_identifier, kSubjectKeyIdentifier);
+
+ int bytes = X509_sign(x509.get(), pkey, EVP_sha256());
+ if (bytes <= 0) {
+ LOG(ERROR) << "Unable to sign x509 certificate";
+ return nullptr;
+ }
+
+ return x509;
+}
+
+std::string X509ToPEMString(X509* x509) {
+ bssl::UniquePtr<BIO> bio(BIO_new(BIO_s_mem()));
+ int rc = PEM_write_bio_X509(bio.get(), x509);
+ if (rc != 1) {
+ LOG(ERROR) << "PEM_write_bio_X509 failed";
+ return "";
+ }
+
+ BUF_MEM* mem = nullptr;
+ BIO_get_mem_ptr(bio.get(), &mem);
+ if (!mem || !mem->data || !mem->length) {
+ LOG(ERROR) << "BIO_get_mem_ptr failed";
+ return "";
+ }
+
+ return std::string(mem->data, mem->length);
+}
+
+} // namespace crypto
+} // namespace adb