[adbwifi] Add A_STLS command.
This command will be sent by adbd to notify the client that the
connection will be over TLS.
When client connects, it will send the CNXN packet, as usual. If the
server connection has TLS enabled, it will send the A_STLS packet
(regardless of whether auth is required). At this point, the client's
only valid response is to send a A_STLS packet. Once both sides have
exchanged the A_STLS packet, both will start the TLS handshake.
If auth is required, then the client will receive a CertificateRequest
with a list of known public keys (SHA256 hash) that it can use in its
certificate. Otherwise, the list will be empty and the client can assume
that either any key will work, or none will work.
If the handshake was successful, the server will send the CNXN packet
and the usual adb protocol is resumed over TLS. If the handshake failed,
both sides will disconnect, as there's no point to retry because the
server's known keys have already been communicated.
Bug: 111434128
Test: WIP; will add to adb_test.py/adb_device.py.
Enable wireless debugging in the Settings, then 'adb connect
<ip>:<port>'. Connection should succeed if key is in keystore. Used
wireshark to check for packet encryption.
Change-Id: I3d60647491c6c6b92297e4f628707a6457fa9420
diff --git a/adb/client/auth.cpp b/adb/client/auth.cpp
index a2eff7f..8738ce7 100644
--- a/adb/client/auth.cpp
+++ b/adb/client/auth.cpp
@@ -30,6 +30,9 @@
#include <string>
#include <adb/crypto/rsa_2048_key.h>
+#include <adb/crypto/x509_generator.h>
+#include <adb/tls/adb_ca_list.h>
+#include <adb/tls/tls_connection.h>
#include <android-base/errors.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
@@ -55,6 +58,7 @@
static std::map<int, std::string>& g_monitored_paths = *new std::map<int, std::string>;
using namespace adb::crypto;
+using namespace adb::tls;
static bool generate_key(const std::string& file) {
LOG(INFO) << "generate_key(" << file << ")...";
@@ -144,6 +148,7 @@
if (g_keys.find(fingerprint) != g_keys.end()) {
LOG(INFO) << "ignoring already-loaded key: " << file;
} else {
+ LOG(INFO) << "Loaded fingerprint=[" << SHA256BitsToHexString(fingerprint) << "]";
g_keys[fingerprint] = std::move(key);
}
return true;
@@ -475,3 +480,72 @@
p->msg.data_length = p->payload.size();
send_packet(p, t);
}
+
+void adb_auth_tls_handshake(atransport* t) {
+ std::thread([t]() {
+ std::shared_ptr<RSA> key = t->Key();
+ if (key == nullptr) {
+ // Can happen if !auth_required
+ LOG(INFO) << "t->auth_key not set before handshake";
+ key = t->NextKey();
+ CHECK(key);
+ }
+
+ LOG(INFO) << "Attempting to TLS handshake";
+ bool success = t->connection()->DoTlsHandshake(key.get());
+ if (success) {
+ LOG(INFO) << "Handshake succeeded. Waiting for CNXN packet...";
+ } else {
+ LOG(INFO) << "Handshake failed. Kicking transport";
+ t->Kick();
+ }
+ }).detach();
+}
+
+int adb_tls_set_certificate(SSL* ssl) {
+ LOG(INFO) << __func__;
+
+ const STACK_OF(X509_NAME)* ca_list = SSL_get_client_CA_list(ssl);
+ if (ca_list == nullptr) {
+ // Either the device doesn't know any keys, or !auth_required.
+ // So let's just try with the default certificate and see what happens.
+ LOG(INFO) << "No client CA list. Trying with default certificate.";
+ return 1;
+ }
+
+ const size_t num_cas = sk_X509_NAME_num(ca_list);
+ for (size_t i = 0; i < num_cas; ++i) {
+ auto* x509_name = sk_X509_NAME_value(ca_list, i);
+ auto adbFingerprint = ParseEncodedKeyFromCAIssuer(x509_name);
+ if (!adbFingerprint.has_value()) {
+ // This could be a real CA issuer. Unfortunately, we don't support
+ // it ATM.
+ continue;
+ }
+
+ LOG(INFO) << "Checking for fingerprint match [" << *adbFingerprint << "]";
+ auto encoded_key = SHA256HexStringToBits(*adbFingerprint);
+ if (!encoded_key.has_value()) {
+ continue;
+ }
+ // Check against our list of encoded keys for a match
+ std::lock_guard<std::mutex> lock(g_keys_mutex);
+ auto rsa_priv_key = g_keys.find(*encoded_key);
+ if (rsa_priv_key != g_keys.end()) {
+ LOG(INFO) << "Got SHA256 match on a key";
+ bssl::UniquePtr<EVP_PKEY> evp_pkey(EVP_PKEY_new());
+ CHECK(EVP_PKEY_set1_RSA(evp_pkey.get(), rsa_priv_key->second.get()));
+ auto x509 = GenerateX509Certificate(evp_pkey.get());
+ auto x509_str = X509ToPEMString(x509.get());
+ auto evp_str = Key::ToPEMString(evp_pkey.get());
+ TlsConnection::SetCertAndKey(ssl, x509_str, evp_str);
+ return 1;
+ } else {
+ LOG(INFO) << "No match for [" << *adbFingerprint << "]";
+ }
+ }
+
+ // Let's just try with the default certificate anyways, because daemon might
+ // not require auth, even though it has a list of keys.
+ return 1;
+}