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
This commit is contained in:
Joshua Duong 2019-12-19 16:36:30 -08:00
parent 9e96e71067
commit ef28ca4cdc
14 changed files with 717 additions and 72 deletions

View file

@ -3,6 +3,12 @@
{
"name": "adbd_test"
},
{
"name": "adb_crypto_test"
},
{
"name": "adb_tls_connection_test"
},
{
"name": "CtsInitTestCases"
},

View file

@ -255,6 +255,8 @@ cc_library_host_static {
},
static_libs: [
"libadb_crypto",
"libadb_protos",
"libbase",
"libcrypto_utils",
"libcrypto",
@ -272,6 +274,7 @@ cc_test_host {
defaults: ["adb_defaults"],
srcs: libadb_test_srcs,
static_libs: [
"libadb_crypto",
"libadb_host",
"libbase",
"libcutils",
@ -347,6 +350,7 @@ cc_binary_host {
],
static_libs: [
"libadb_crypto",
"libadb_host",
"libandroidfw",
"libbase",
@ -422,6 +426,7 @@ cc_library_static {
],
shared_libs: [
"libadb_crypto",
"libadbd_auth",
"libasyncio",
"libbase",
@ -765,6 +770,7 @@ cc_test_host {
"fastdeploy/deploypatchgenerator/patch_utils_test.cpp",
],
static_libs: [
"libadb_crypto",
"libadb_host",
"libandroidfw",
"libbase",

View file

@ -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 @@ static std::map<std::string, std::shared_ptr<RSA>>& g_keys =
*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 @@ static bool pubkey_from_privkey(std::string* out, const std::string& path) {
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 @@ std::string adb_auth_get_userkey() {
}
int adb_auth_keygen(const char* filename) {
return (generate_key(filename) == 0);
return !generate_key(filename);
}
int adb_auth_pubkey(const char* filename) {

85
adb/crypto/Android.bp Normal file
View file

@ -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",
],
}

View file

@ -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

View file

@ -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

View file

@ -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

47
adb/crypto/key.cpp Normal file
View file

@ -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

View file

@ -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

View file

@ -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"],
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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