[adbwifi] Add pairing_connection library.
Bug: 111434128 Bug: 119494503 Test: atest adb_pairing_connection_test Change-Id: I54d68c65067809832266d6c3043b63222c98a9cd Exempt-From-Owner-Approval: approved already
This commit is contained in:
parent
df8f1217d0
commit
c7a1fb8fd9
12 changed files with 2253 additions and 0 deletions
185
adb/pairing_connection/Android.bp
Normal file
185
adb/pairing_connection/Android.bp
Normal file
|
@ -0,0 +1,185 @@
|
|||
// Copyright (C) 2020 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_pairing_connection_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wthread-safety",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
compile_multilib: "both",
|
||||
|
||||
srcs: [
|
||||
"pairing_connection.cpp",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
version_script: "libadb_pairing_connection.map.txt",
|
||||
},
|
||||
windows: {
|
||||
compile_multilib: "first",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
visibility: [
|
||||
"//art:__subpackages__",
|
||||
"//system/core/adb:__subpackages__",
|
||||
"//frameworks/base/services:__subpackages__",
|
||||
],
|
||||
apex_available: [
|
||||
"com.android.adbd",
|
||||
],
|
||||
|
||||
// libadb_pairing_connection doesn't need an embedded build number.
|
||||
use_version_lib: false,
|
||||
|
||||
stl: "libc++_static",
|
||||
|
||||
host_supported: true,
|
||||
recovery_available: true,
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
"libssl",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcrypto",
|
||||
"liblog",
|
||||
"libadb_pairing_auth",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadb_pairing_connection",
|
||||
defaults: ["libadb_pairing_connection_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"com.android.adbd",
|
||||
],
|
||||
|
||||
stubs: {
|
||||
symbol_file: "libadb_pairing_connection.map.txt",
|
||||
versions: ["30"],
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
"libadb_protos",
|
||||
// Statically link libadb_tls_connection because it is not
|
||||
// ABI-stable.
|
||||
"libadb_tls_connection",
|
||||
"libprotobuf-cpp-lite",
|
||||
],
|
||||
}
|
||||
|
||||
// For running atest (b/147158681)
|
||||
cc_library_static {
|
||||
name: "libadb_pairing_connection_static",
|
||||
defaults: ["libadb_pairing_connection_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadb_protos_static",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libadb_tls_connection_static",
|
||||
],
|
||||
}
|
||||
|
||||
cc_defaults {
|
||||
name: "libadb_pairing_server_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wthread-safety",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
compile_multilib: "both",
|
||||
|
||||
srcs: [
|
||||
"pairing_server.cpp",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
version_script: "libadb_pairing_server.map.txt",
|
||||
},
|
||||
},
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
visibility: [
|
||||
"//art:__subpackages__",
|
||||
"//system/core/adb:__subpackages__",
|
||||
"//frameworks/base/services:__subpackages__",
|
||||
],
|
||||
|
||||
host_supported: true,
|
||||
recovery_available: true,
|
||||
|
||||
stl: "libc++_static",
|
||||
|
||||
static_libs: [
|
||||
"libbase",
|
||||
],
|
||||
shared_libs: [
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"libcutils",
|
||||
"liblog",
|
||||
"libadb_pairing_auth",
|
||||
"libadb_pairing_connection",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadb_pairing_server",
|
||||
defaults: ["libadb_pairing_server_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"com.android.adbd",
|
||||
],
|
||||
|
||||
stubs: {
|
||||
symbol_file: "libadb_pairing_server.map.txt",
|
||||
versions: ["30"],
|
||||
},
|
||||
|
||||
static_libs: [
|
||||
// Statically link libadb_crypto because it is not
|
||||
// ABI-stable.
|
||||
"libadb_crypto",
|
||||
"libadb_protos",
|
||||
],
|
||||
}
|
||||
|
||||
// For running atest (b/147158681)
|
||||
cc_library_static {
|
||||
name: "libadb_pairing_server_static",
|
||||
defaults: ["libadb_pairing_server_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
],
|
||||
|
||||
static_libs: [
|
||||
"libadb_crypto_static",
|
||||
"libadb_protos_static",
|
||||
],
|
||||
}
|
130
adb/pairing_connection/include/adb/pairing/pairing_connection.h
Normal file
130
adb/pairing_connection/include/adb/pairing/pairing_connection.h
Normal file
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#if !defined(__INTRODUCED_IN)
|
||||
#define __INTRODUCED_IN(__api_level) /* nothing */
|
||||
#endif
|
||||
|
||||
// These APIs are for the Adb pairing protocol. This protocol requires both
|
||||
// sides to possess a shared secret to authenticate each other. The connection
|
||||
// is over TLS, and requires that both the client and server have a valid
|
||||
// certificate.
|
||||
//
|
||||
// This protocol is one-to-one, i.e., one PairingConnectionCtx server instance
|
||||
// interacts with only one PairingConnectionCtx client instance. In other words,
|
||||
// every new client instance must be bound to a new server instance.
|
||||
//
|
||||
// If both sides have authenticated, they will exchange their peer information
|
||||
// (see #PeerInfo).
|
||||
__BEGIN_DECLS
|
||||
#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
|
||||
|
||||
const uint32_t kMaxPeerInfoSize = 8192;
|
||||
struct PeerInfo {
|
||||
uint8_t type;
|
||||
uint8_t data[kMaxPeerInfoSize - 1];
|
||||
} __attribute__((packed));
|
||||
typedef struct PeerInfo PeerInfo;
|
||||
static_assert(sizeof(PeerInfo) == kMaxPeerInfoSize, "PeerInfo has weird size");
|
||||
|
||||
enum PeerInfoType : uint8_t {
|
||||
ADB_RSA_PUB_KEY = 0,
|
||||
ADB_DEVICE_GUID = 1,
|
||||
};
|
||||
|
||||
struct PairingConnectionCtx;
|
||||
typedef struct PairingConnectionCtx PairingConnectionCtx;
|
||||
typedef void (*pairing_result_cb)(const PeerInfo*, int, void*);
|
||||
|
||||
// Starts the pairing connection on a separate thread.
|
||||
//
|
||||
// Upon completion, if the pairing was successful,
|
||||
// |cb| will be called with the peer information and certificate.
|
||||
// Otherwise, |cb| will be called with empty data. |fd| should already
|
||||
// be opened. PairingConnectionCtx will take ownership of the |fd|.
|
||||
//
|
||||
// Pairing is successful if both server/client uses the same non-empty
|
||||
// |pswd|, and they are able to exchange the information. |pswd| and
|
||||
// |certificate| must be non-empty. start() can only be called once in the
|
||||
// lifetime of this object.
|
||||
//
|
||||
// @param ctx the PairingConnectionCtx instance. Will abort if null.
|
||||
// @param fd the fd connecting the peers. This will take ownership of fd.
|
||||
// @param cb the user-provided callback that is called with the result of the
|
||||
// pairing. The callback will be called on a different thread from the
|
||||
// caller.
|
||||
// @param opaque opaque userdata.
|
||||
// @return true if the thread was successfully started, false otherwise. To stop
|
||||
// the connection process, destroy the instance (see
|
||||
// #pairing_connection_destroy). If false is returned, cb will not be
|
||||
// invoked. Otherwise, cb is guaranteed to be invoked, even if you
|
||||
// destroy the ctx while in the pairing process.
|
||||
bool pairing_connection_start(PairingConnectionCtx* ctx, int fd, pairing_result_cb cb, void* opaque)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
// Creates a new PairingConnectionCtx instance as the client.
|
||||
//
|
||||
// @param pswd the password to authenticate both peers. Will abort if null.
|
||||
// @param pswd_len the length of pswd. Will abort if 0.
|
||||
// @param peer_info the PeerInfo struct that is exchanged between peers if the
|
||||
// pairing was successful. Will abort if null.
|
||||
// @param x509_cert_pem the X.509 certificate in PEM format. Will abort if null.
|
||||
// @param x509_size the size of x509_cert_pem. Will abort if 0.
|
||||
// @param priv_key_pem the private key corresponding to the given X.509
|
||||
// certificate, in PEM format. Will abort if null.
|
||||
// @param priv_size the size of priv_key_pem. Will abort if 0.
|
||||
// @return a new PairingConnectionCtx client instance. The caller is responsible
|
||||
// for destroying the context via #pairing_connection_destroy.
|
||||
PairingConnectionCtx* pairing_connection_client_new(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info,
|
||||
const uint8_t* x509_cert_pem, size_t x509_size,
|
||||
const uint8_t* priv_key_pem, size_t priv_size)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
// Creates a new PairingConnectionCtx instance as the server.
|
||||
//
|
||||
// @param pswd the password to authenticate both peers. Will abort if null.
|
||||
// @param pswd_len the length of pswd. Will abort if 0.
|
||||
// @param peer_info the PeerInfo struct that is exchanged between peers if the
|
||||
// pairing was successful. Will abort if null.
|
||||
// @param x509_cert_pem the X.509 certificate in PEM format. Will abort if null.
|
||||
// @param x509_size the size of x509_cert_pem. Will abort if 0.
|
||||
// @param priv_key_pem the private key corresponding to the given X.509
|
||||
// certificate, in PEM format. Will abort if null.
|
||||
// @param priv_size the size of priv_key_pem. Will abort if 0.
|
||||
// @return a new PairingConnectionCtx server instance. The caller is responsible
|
||||
// for destroying the context via #pairing_connection_destroy.
|
||||
PairingConnectionCtx* pairing_connection_server_new(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info,
|
||||
const uint8_t* x509_cert_pem, size_t x509_size,
|
||||
const uint8_t* priv_key_pem, size_t priv_size)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
// Destroys the PairingConnectionCtx instance.
|
||||
//
|
||||
// It is safe to destroy the instance at any point in the pairing process.
|
||||
//
|
||||
// @param ctx the PairingConnectionCtx instance to destroy. Will abort if null.
|
||||
void pairing_connection_destroy(PairingConnectionCtx* ctx) __INTRODUCED_IN(30);
|
||||
|
||||
#endif //!__ANDROID__ || __ANDROID_API__ >= 30
|
||||
__END_DECLS
|
111
adb/pairing_connection/include/adb/pairing/pairing_server.h
Normal file
111
adb/pairing_connection/include/adb/pairing/pairing_server.h
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "adb/pairing/pairing_connection.h"
|
||||
|
||||
#if !defined(__INTRODUCED_IN)
|
||||
#define __INTRODUCED_IN(__api_level) /* nothing */
|
||||
#endif
|
||||
|
||||
__BEGIN_DECLS
|
||||
#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
|
||||
|
||||
// PairingServerCtx is a wrapper around the #PairingConnectionCtx APIs,
|
||||
// which handles multiple client connections.
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
struct PairingServerCtx;
|
||||
typedef struct PairingServerCtx PairingServerCtx;
|
||||
|
||||
// Callback containing the result of the pairing. If #PeerInfo is null,
|
||||
// then the pairing failed. Otherwise, pairing succeeded and #PeerInfo
|
||||
// contains information about the peer.
|
||||
typedef void (*pairing_server_result_cb)(const PeerInfo*, void*) __INTRODUCED_IN(30);
|
||||
|
||||
// Starts the pairing server.
|
||||
//
|
||||
// This call is non-blocking. Upon completion, if the pairing was successful,
|
||||
// then |cb| will be called with the PeerInfo
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// @param ctx the PairingServerCtx instance.
|
||||
// @param cb the user-provided callback to notify the result of the pairing. See
|
||||
// #pairing_server_result_cb.
|
||||
// @param opaque the opaque userdata.
|
||||
// @return the port number the server is listening on. Returns 0 on failure.
|
||||
uint16_t pairing_server_start(PairingServerCtx* ctx, pairing_server_result_cb cb, void* opaque)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
// Creates a new PairingServerCtx instance.
|
||||
//
|
||||
// @param pswd the password used to authenticate the client and server.
|
||||
// @param pswd_len the length of pswd.
|
||||
// @param peer_info the #PeerInfo struct passed to the client on successful
|
||||
// pairing.
|
||||
// @param x509_cert_pem the X.509 certificate in PEM format. Cannot be empty.
|
||||
// @param x509_size the size of x509_cert_pem.
|
||||
// @param priv_key_pem the private key corresponding to the given X.509
|
||||
// certificate, in PEM format. Cannot be empty.
|
||||
// @param priv_size the size of priv_key_pem.
|
||||
// @param port the port number the server should listen on. Must be within the
|
||||
// valid port range [0, 65535]. If port is 0, then the server will
|
||||
// find an open port to listen on. See #pairing_server_start to
|
||||
// obtain the port used.
|
||||
// @return a new PairingServerCtx instance The caller is responsible
|
||||
// for destroying the context via #pairing_server_destroy.
|
||||
PairingServerCtx* pairing_server_new(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info, const uint8_t* x509_cert_pem,
|
||||
size_t x509_size, const uint8_t* priv_key_pem,
|
||||
size_t priv_size, uint16_t port) __INTRODUCED_IN(30);
|
||||
|
||||
// Same as #pairing_server_new, except that the x509 certificate and private key
|
||||
// is generated internally.
|
||||
//
|
||||
// @param pswd the password used to authenticate the client and server.
|
||||
// @param pswd_len the length of pswd.
|
||||
// @param peer_info the #PeerInfo struct passed to the client on successful
|
||||
// pairing.
|
||||
// @param port the port number the server should listen on. Must be within the
|
||||
// valid port range [0, 65535]. If port is 0, then the server will
|
||||
// find an open port to listen on. See #pairing_server_start to
|
||||
// obtain the port used.
|
||||
// @return a new PairingServerCtx instance The caller is responsible
|
||||
// for destroying the context via #pairing_server_destroy.
|
||||
PairingServerCtx* pairing_server_new_no_cert(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info, uint16_t port)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
// Destroys the PairingServerCtx instance.
|
||||
//
|
||||
// @param ctx the PairingServerCtx instance to destroy.
|
||||
void pairing_server_destroy(PairingServerCtx* ctx) __INTRODUCED_IN(30);
|
||||
|
||||
#endif //!__ANDROID__ || __ANDROID_API__ >= 30
|
||||
__END_DECLS
|
34
adb/pairing_connection/internal/constants.h
Normal file
34
adb/pairing_connection/internal/constants.h
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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
|
||||
|
||||
// This file contains constants that can be used both in the pairing_connection
|
||||
// code and tested in the pairing_connection_test code.
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
namespace internal {
|
||||
|
||||
// The maximum number of connections the PairingServer can handle at once.
|
||||
constexpr int kMaxConnections = 10;
|
||||
// The maximum number of attempts the PairingServer will take before quitting.
|
||||
// This is to prevent someone malicious from quickly brute-forcing every
|
||||
// combination.
|
||||
constexpr int kMaxPairingAttempts = 20;
|
||||
|
||||
} // namespace internal
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
10
adb/pairing_connection/libadb_pairing_connection.map.txt
Normal file
10
adb/pairing_connection/libadb_pairing_connection.map.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
LIBADB_PAIRING_CONNECTION {
|
||||
global:
|
||||
pairing_connection_client_new; # apex introduced=30
|
||||
pairing_connection_server_new; # apex introduced=30
|
||||
pairing_connection_start; # apex introduced=30
|
||||
pairing_connection_destroy; # apex introduced=30
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
10
adb/pairing_connection/libadb_pairing_server.map.txt
Normal file
10
adb/pairing_connection/libadb_pairing_server.map.txt
Normal file
|
@ -0,0 +1,10 @@
|
|||
LIBADB_PAIRING_SERVER {
|
||||
global:
|
||||
pairing_server_start; # apex introduced=30
|
||||
pairing_server_new; # apex introduced=30
|
||||
pairing_server_new_no_cert; # apex introduced=30
|
||||
pairing_server_destroy; # apex introduced=30
|
||||
|
||||
local:
|
||||
*;
|
||||
};
|
491
adb/pairing_connection/pairing_connection.cpp
Normal file
491
adb/pairing_connection/pairing_connection.cpp
Normal file
|
@ -0,0 +1,491 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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/pairing/pairing_connection.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <adb/pairing/pairing_auth.h>
|
||||
#include <adb/tls/tls_connection.h>
|
||||
#include <android-base/endian.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/macros.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
|
||||
#include "pairing.pb.h"
|
||||
|
||||
using namespace adb;
|
||||
using android::base::unique_fd;
|
||||
using TlsError = tls::TlsConnection::TlsError;
|
||||
|
||||
const uint8_t kCurrentKeyHeaderVersion = 1;
|
||||
const uint8_t kMinSupportedKeyHeaderVersion = 1;
|
||||
const uint8_t kMaxSupportedKeyHeaderVersion = 1;
|
||||
const uint32_t kMaxPayloadSize = kMaxPeerInfoSize * 2;
|
||||
|
||||
struct PairingPacketHeader {
|
||||
uint8_t version; // PairingPacket version
|
||||
uint8_t type; // the type of packet (PairingPacket.Type)
|
||||
uint32_t payload; // Size of the payload in bytes
|
||||
} __attribute__((packed));
|
||||
|
||||
struct PairingAuthDeleter {
|
||||
void operator()(PairingAuthCtx* p) { pairing_auth_destroy(p); }
|
||||
}; // PairingAuthDeleter
|
||||
using PairingAuthPtr = std::unique_ptr<PairingAuthCtx, PairingAuthDeleter>;
|
||||
|
||||
// PairingConnectionCtx encapsulates the protocol to authenticate two peers with
|
||||
// each other. This class will open the tcp sockets and handle the pairing
|
||||
// process. On completion, both sides will have each other's public key
|
||||
// (certificate) if successful, otherwise, the pairing failed. The tcp port
|
||||
// number is hardcoded (see pairing_connection.cpp).
|
||||
//
|
||||
// Each PairingConnectionCtx instance represents a different device trying to
|
||||
// pair. So for the device, we can have multiple PairingConnectionCtxs while the
|
||||
// host may have only one (unless host has a PairingServer).
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
struct PairingConnectionCtx {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
using ResultCallback = pairing_result_cb;
|
||||
enum class Role {
|
||||
Client,
|
||||
Server,
|
||||
};
|
||||
|
||||
explicit PairingConnectionCtx(Role role, const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& certificate, const Data& priv_key);
|
||||
virtual ~PairingConnectionCtx();
|
||||
|
||||
// Starts the pairing connection on a separate thread.
|
||||
// Upon completion, if the pairing was successful,
|
||||
// |cb| will be called with the peer information and certificate.
|
||||
// Otherwise, |cb| will be called with empty data. |fd| should already
|
||||
// be opened. PairingConnectionCtx will take ownership of the |fd|.
|
||||
//
|
||||
// Pairing is successful if both server/client uses the same non-empty
|
||||
// |pswd|, and they are able to exchange the information. |pswd| and
|
||||
// |certificate| must be non-empty. Start() can only be called once in the
|
||||
// lifetime of this object.
|
||||
//
|
||||
// Returns true if the thread was successfully started, false otherwise.
|
||||
bool Start(int fd, ResultCallback cb, void* opaque);
|
||||
|
||||
private:
|
||||
// Setup the tls connection.
|
||||
bool SetupTlsConnection();
|
||||
|
||||
/************ PairingPacketHeader methods ****************/
|
||||
// Tries to write out the header and payload.
|
||||
bool WriteHeader(const PairingPacketHeader* header, std::string_view payload);
|
||||
// Tries to parse incoming data into the |header|. Returns true if header
|
||||
// is valid and header version is supported. |header| is filled on success.
|
||||
// |header| may contain garbage if unsuccessful.
|
||||
bool ReadHeader(PairingPacketHeader* header);
|
||||
// Creates a PairingPacketHeader.
|
||||
void CreateHeader(PairingPacketHeader* header, adb::proto::PairingPacket::Type type,
|
||||
uint32_t payload_size);
|
||||
// Checks if actual matches expected.
|
||||
bool CheckHeaderType(adb::proto::PairingPacket::Type expected, uint8_t actual);
|
||||
|
||||
/*********** State related methods **************/
|
||||
// Handles the State::ExchangingMsgs state.
|
||||
bool DoExchangeMsgs();
|
||||
// Handles the State::ExchangingPeerInfo state.
|
||||
bool DoExchangePeerInfo();
|
||||
|
||||
// The background task to do the pairing.
|
||||
void StartWorker();
|
||||
|
||||
// Calls |cb_| and sets the state to Stopped.
|
||||
void NotifyResult(const PeerInfo* p);
|
||||
|
||||
static PairingAuthPtr CreatePairingAuthPtr(Role role, const Data& pswd);
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
ExchangingMsgs,
|
||||
ExchangingPeerInfo,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
std::atomic<State> state_{State::Ready};
|
||||
Role role_;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
|
||||
// Peer's info
|
||||
PeerInfo their_info_;
|
||||
|
||||
ResultCallback cb_;
|
||||
void* opaque_ = nullptr;
|
||||
std::unique_ptr<tls::TlsConnection> tls_;
|
||||
PairingAuthPtr auth_;
|
||||
unique_fd fd_;
|
||||
std::thread thread_;
|
||||
static constexpr size_t kExportedKeySize = 64;
|
||||
}; // PairingConnectionCtx
|
||||
|
||||
PairingConnectionCtx::PairingConnectionCtx(Role role, const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key)
|
||||
: role_(role), pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
|
||||
}
|
||||
|
||||
PairingConnectionCtx::~PairingConnectionCtx() {
|
||||
// Force close the fd and wait for the worker thread to finish.
|
||||
fd_.reset();
|
||||
if (thread_.joinable()) {
|
||||
thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::SetupTlsConnection() {
|
||||
tls_ = tls::TlsConnection::Create(
|
||||
role_ == Role::Server ? tls::TlsConnection::Role::Server
|
||||
: tls::TlsConnection::Role::Client,
|
||||
std::string_view(reinterpret_cast<const char*>(cert_.data()), cert_.size()),
|
||||
std::string_view(reinterpret_cast<const char*>(priv_key_.data()), priv_key_.size()),
|
||||
fd_);
|
||||
|
||||
if (tls_ == nullptr) {
|
||||
LOG(ERROR) << "Unable to start TlsConnection. Unable to pair fd=" << fd_.get();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Allow any peer certificate
|
||||
tls_->SetCertVerifyCallback([](X509_STORE_CTX*) { return 1; });
|
||||
|
||||
// SSL doesn't seem to behave correctly with fdevents so just do a blocking
|
||||
// read for the pairing data.
|
||||
if (tls_->DoHandshake() != TlsError::Success) {
|
||||
LOG(ERROR) << "Failed to handshake with the peer fd=" << fd_.get();
|
||||
return false;
|
||||
}
|
||||
|
||||
// To ensure the connection is not stolen while we do the PAKE, append the
|
||||
// exported key material from the tls connection to the password.
|
||||
std::vector<uint8_t> exportedKeyMaterial = tls_->ExportKeyingMaterial(kExportedKeySize);
|
||||
if (exportedKeyMaterial.empty()) {
|
||||
LOG(ERROR) << "Failed to export key material";
|
||||
return false;
|
||||
}
|
||||
pswd_.insert(pswd_.end(), std::make_move_iterator(exportedKeyMaterial.begin()),
|
||||
std::make_move_iterator(exportedKeyMaterial.end()));
|
||||
auth_ = CreatePairingAuthPtr(role_, pswd_);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::WriteHeader(const PairingPacketHeader* header,
|
||||
std::string_view payload) {
|
||||
PairingPacketHeader network_header = *header;
|
||||
network_header.payload = htonl(network_header.payload);
|
||||
if (!tls_->WriteFully(std::string_view(reinterpret_cast<const char*>(&network_header),
|
||||
sizeof(PairingPacketHeader))) ||
|
||||
!tls_->WriteFully(payload)) {
|
||||
LOG(ERROR) << "Failed to write out PairingPacketHeader";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::ReadHeader(PairingPacketHeader* header) {
|
||||
auto data = tls_->ReadFully(sizeof(PairingPacketHeader));
|
||||
if (data.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t* p = data.data();
|
||||
// First byte is always PairingPacketHeader version
|
||||
header->version = *p;
|
||||
++p;
|
||||
if (header->version < kMinSupportedKeyHeaderVersion ||
|
||||
header->version > kMaxSupportedKeyHeaderVersion) {
|
||||
LOG(ERROR) << "PairingPacketHeader version mismatch (us=" << kCurrentKeyHeaderVersion
|
||||
<< " them=" << header->version << ")";
|
||||
return false;
|
||||
}
|
||||
// Next byte is the PairingPacket::Type
|
||||
if (!adb::proto::PairingPacket::Type_IsValid(*p)) {
|
||||
LOG(ERROR) << "Unknown PairingPacket type=" << static_cast<uint32_t>(*p);
|
||||
return false;
|
||||
}
|
||||
header->type = *p;
|
||||
++p;
|
||||
// Last, the payload size
|
||||
header->payload = ntohl(*(reinterpret_cast<uint32_t*>(p)));
|
||||
if (header->payload == 0 || header->payload > kMaxPayloadSize) {
|
||||
LOG(ERROR) << "header payload not within a safe payload size (size=" << header->payload
|
||||
<< ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingConnectionCtx::CreateHeader(PairingPacketHeader* header,
|
||||
adb::proto::PairingPacket::Type type,
|
||||
uint32_t payload_size) {
|
||||
header->version = kCurrentKeyHeaderVersion;
|
||||
uint8_t type8 = static_cast<uint8_t>(static_cast<int>(type));
|
||||
header->type = type8;
|
||||
header->payload = payload_size;
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::CheckHeaderType(adb::proto::PairingPacket::Type expected_type,
|
||||
uint8_t actual) {
|
||||
uint8_t expected = *reinterpret_cast<uint8_t*>(&expected_type);
|
||||
if (actual != expected) {
|
||||
LOG(ERROR) << "Unexpected header type (expected=" << static_cast<uint32_t>(expected)
|
||||
<< " actual=" << static_cast<uint32_t>(actual) << ")";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingConnectionCtx::NotifyResult(const PeerInfo* p) {
|
||||
cb_(p, fd_.get(), opaque_);
|
||||
state_ = State::Stopped;
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::Start(int fd, ResultCallback cb, void* opaque) {
|
||||
if (fd < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
State expected = State::Ready;
|
||||
if (!state_.compare_exchange_strong(expected, State::ExchangingMsgs)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fd_.reset(fd);
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
thread_ = std::thread([this] { StartWorker(); });
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::DoExchangeMsgs() {
|
||||
uint32_t payload = pairing_auth_msg_size(auth_.get());
|
||||
std::vector<uint8_t> msg(payload);
|
||||
pairing_auth_get_spake2_msg(auth_.get(), msg.data());
|
||||
|
||||
PairingPacketHeader header;
|
||||
CreateHeader(&header, adb::proto::PairingPacket::SPAKE2_MSG, payload);
|
||||
|
||||
// Write our SPAKE2 msg
|
||||
if (!WriteHeader(&header,
|
||||
std::string_view(reinterpret_cast<const char*>(msg.data()), msg.size()))) {
|
||||
LOG(ERROR) << "Failed to write SPAKE2 msg.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the peer's SPAKE2 msg header
|
||||
if (!ReadHeader(&header)) {
|
||||
LOG(ERROR) << "Invalid PairingPacketHeader.";
|
||||
return false;
|
||||
}
|
||||
if (!CheckHeaderType(adb::proto::PairingPacket::SPAKE2_MSG, header.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the SPAKE2 msg payload and initialize the cipher for
|
||||
// encrypting the PeerInfo and certificate.
|
||||
auto their_msg = tls_->ReadFully(header.payload);
|
||||
if (their_msg.empty() ||
|
||||
!pairing_auth_init_cipher(auth_.get(), their_msg.data(), their_msg.size())) {
|
||||
LOG(ERROR) << "Unable to initialize pairing cipher [their_msg.size=" << their_msg.size()
|
||||
<< "]";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PairingConnectionCtx::DoExchangePeerInfo() {
|
||||
// Encrypt PeerInfo
|
||||
std::vector<uint8_t> buf;
|
||||
uint8_t* p = reinterpret_cast<uint8_t*>(&peer_info_);
|
||||
buf.assign(p, p + sizeof(peer_info_));
|
||||
std::vector<uint8_t> outbuf(pairing_auth_safe_encrypted_size(auth_.get(), buf.size()));
|
||||
CHECK(!outbuf.empty());
|
||||
size_t outsize;
|
||||
if (!pairing_auth_encrypt(auth_.get(), buf.data(), buf.size(), outbuf.data(), &outsize)) {
|
||||
LOG(ERROR) << "Failed to encrypt peer info";
|
||||
return false;
|
||||
}
|
||||
outbuf.resize(outsize);
|
||||
|
||||
// Write out the packet header
|
||||
PairingPacketHeader out_header;
|
||||
out_header.version = kCurrentKeyHeaderVersion;
|
||||
out_header.type = static_cast<uint8_t>(static_cast<int>(adb::proto::PairingPacket::PEER_INFO));
|
||||
out_header.payload = htonl(outbuf.size());
|
||||
if (!tls_->WriteFully(
|
||||
std::string_view(reinterpret_cast<const char*>(&out_header), sizeof(out_header)))) {
|
||||
LOG(ERROR) << "Unable to write PairingPacketHeader";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Write out the encrypted payload
|
||||
if (!tls_->WriteFully(
|
||||
std::string_view(reinterpret_cast<const char*>(outbuf.data()), outbuf.size()))) {
|
||||
LOG(ERROR) << "Unable to write encrypted peer info";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in the peer's packet header
|
||||
PairingPacketHeader header;
|
||||
if (!ReadHeader(&header)) {
|
||||
LOG(ERROR) << "Invalid PairingPacketHeader.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CheckHeaderType(adb::proto::PairingPacket::PEER_INFO, header.type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read in the encrypted peer certificate
|
||||
buf = tls_->ReadFully(header.payload);
|
||||
if (buf.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to decrypt the certificate
|
||||
outbuf.resize(pairing_auth_safe_decrypted_size(auth_.get(), buf.data(), buf.size()));
|
||||
if (outbuf.empty()) {
|
||||
LOG(ERROR) << "Unsupported payload while decrypting peer info.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pairing_auth_decrypt(auth_.get(), buf.data(), buf.size(), outbuf.data(), &outsize)) {
|
||||
LOG(ERROR) << "Failed to decrypt";
|
||||
return false;
|
||||
}
|
||||
outbuf.resize(outsize);
|
||||
|
||||
// The decrypted message should contain the PeerInfo.
|
||||
if (outbuf.size() != sizeof(PeerInfo)) {
|
||||
LOG(ERROR) << "Got size=" << outbuf.size() << "PeerInfo.size=" << sizeof(PeerInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
p = outbuf.data();
|
||||
::memcpy(&their_info_, p, sizeof(PeerInfo));
|
||||
p += sizeof(PeerInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void PairingConnectionCtx::StartWorker() {
|
||||
// Setup the secure transport
|
||||
if (!SetupTlsConnection()) {
|
||||
NotifyResult(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
switch (state_) {
|
||||
case State::ExchangingMsgs:
|
||||
if (!DoExchangeMsgs()) {
|
||||
NotifyResult(nullptr);
|
||||
return;
|
||||
}
|
||||
state_ = State::ExchangingPeerInfo;
|
||||
break;
|
||||
case State::ExchangingPeerInfo:
|
||||
if (!DoExchangePeerInfo()) {
|
||||
NotifyResult(nullptr);
|
||||
return;
|
||||
}
|
||||
NotifyResult(&their_info_);
|
||||
return;
|
||||
case State::Ready:
|
||||
case State::Stopped:
|
||||
LOG(FATAL) << __func__ << ": Got invalid state";
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
PairingAuthPtr PairingConnectionCtx::CreatePairingAuthPtr(Role role, const Data& pswd) {
|
||||
switch (role) {
|
||||
case Role::Client:
|
||||
return PairingAuthPtr(pairing_auth_client_new(pswd.data(), pswd.size()));
|
||||
break;
|
||||
case Role::Server:
|
||||
return PairingAuthPtr(pairing_auth_server_new(pswd.data(), pswd.size()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static PairingConnectionCtx* CreateConnection(PairingConnectionCtx::Role role, const uint8_t* pswd,
|
||||
size_t pswd_len, const PeerInfo* peer_info,
|
||||
const uint8_t* x509_cert_pem, size_t x509_size,
|
||||
const uint8_t* priv_key_pem, size_t priv_size) {
|
||||
CHECK(pswd);
|
||||
CHECK_GT(pswd_len, 0U);
|
||||
CHECK(x509_cert_pem);
|
||||
CHECK_GT(x509_size, 0U);
|
||||
CHECK(priv_key_pem);
|
||||
CHECK_GT(priv_size, 0U);
|
||||
CHECK(peer_info);
|
||||
std::vector<uint8_t> vec_pswd(pswd, pswd + pswd_len);
|
||||
std::vector<uint8_t> vec_x509_cert(x509_cert_pem, x509_cert_pem + x509_size);
|
||||
std::vector<uint8_t> vec_priv_key(priv_key_pem, priv_key_pem + priv_size);
|
||||
return new PairingConnectionCtx(role, vec_pswd, *peer_info, vec_x509_cert, vec_priv_key);
|
||||
}
|
||||
|
||||
PairingConnectionCtx* pairing_connection_client_new(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info,
|
||||
const uint8_t* x509_cert_pem, size_t x509_size,
|
||||
const uint8_t* priv_key_pem, size_t priv_size) {
|
||||
return CreateConnection(PairingConnectionCtx::Role::Client, pswd, pswd_len, peer_info,
|
||||
x509_cert_pem, x509_size, priv_key_pem, priv_size);
|
||||
}
|
||||
|
||||
PairingConnectionCtx* pairing_connection_server_new(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info,
|
||||
const uint8_t* x509_cert_pem, size_t x509_size,
|
||||
const uint8_t* priv_key_pem, size_t priv_size) {
|
||||
return CreateConnection(PairingConnectionCtx::Role::Server, pswd, pswd_len, peer_info,
|
||||
x509_cert_pem, x509_size, priv_key_pem, priv_size);
|
||||
}
|
||||
|
||||
void pairing_connection_destroy(PairingConnectionCtx* ctx) {
|
||||
CHECK(ctx);
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
bool pairing_connection_start(PairingConnectionCtx* ctx, int fd, pairing_result_cb cb,
|
||||
void* opaque) {
|
||||
return ctx->Start(fd, cb, opaque);
|
||||
}
|
466
adb/pairing_connection/pairing_server.cpp
Normal file
466
adb/pairing_connection/pairing_server.cpp
Normal file
|
@ -0,0 +1,466 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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/pairing/pairing_server.h"
|
||||
|
||||
#include <sys/epoll.h>
|
||||
#include <sys/eventfd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include <adb/crypto/rsa_2048_key.h>
|
||||
#include <adb/crypto/x509_generator.h>
|
||||
#include <adb/pairing/pairing_connection.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
#include "internal/constants.h"
|
||||
|
||||
using android::base::ScopedLockAssertion;
|
||||
using android::base::unique_fd;
|
||||
using namespace adb::crypto;
|
||||
using namespace adb::pairing;
|
||||
|
||||
// The implementation has two background threads running: one to handle and
|
||||
// accept any new pairing connection requests (socket accept), and the other to
|
||||
// handle connection events (connection started, connection finished).
|
||||
struct PairingServerCtx {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
|
||||
virtual ~PairingServerCtx();
|
||||
|
||||
// All parameters must be non-empty.
|
||||
explicit PairingServerCtx(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key, uint16_t port);
|
||||
|
||||
// Starts the pairing server. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PublicKeyHeader
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object.
|
||||
//
|
||||
// Returns the port number if PairingServerCtx was successfully started. Otherwise,
|
||||
// returns 0.
|
||||
uint16_t Start(pairing_server_result_cb cb, void* opaque);
|
||||
|
||||
private:
|
||||
// Setup the server socket to accept incoming connections. Returns the
|
||||
// server port number (> 0 on success).
|
||||
uint16_t SetupServer();
|
||||
// Force stop the server thread.
|
||||
void StopServer();
|
||||
|
||||
// handles a new pairing client connection
|
||||
bool HandleNewClientConnection(int fd) EXCLUDES(conn_mutex_);
|
||||
|
||||
// ======== connection events thread =============
|
||||
std::mutex conn_mutex_;
|
||||
std::condition_variable conn_cv_;
|
||||
|
||||
using FdVal = int;
|
||||
struct ConnectionDeleter {
|
||||
void operator()(PairingConnectionCtx* p) { pairing_connection_destroy(p); }
|
||||
};
|
||||
using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, ConnectionDeleter>;
|
||||
static ConnectionPtr CreatePairingConnection(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key);
|
||||
using NewConnectionEvent = std::tuple<unique_fd, ConnectionPtr>;
|
||||
// <fd, PeerInfo.type, PeerInfo.data>
|
||||
using ConnectionFinishedEvent = std::tuple<FdVal, uint8_t, std::optional<std::string>>;
|
||||
using ConnectionEvent = std::variant<NewConnectionEvent, ConnectionFinishedEvent>;
|
||||
// Queue for connections to write into. We have a separate queue to read
|
||||
// from, in order to minimize the time the server thread is blocked.
|
||||
std::deque<ConnectionEvent> conn_write_queue_ GUARDED_BY(conn_mutex_);
|
||||
std::deque<ConnectionEvent> conn_read_queue_;
|
||||
// Map of fds to their PairingConnections currently running.
|
||||
std::unordered_map<FdVal, ConnectionPtr> connections_;
|
||||
|
||||
// Two threads launched when starting the pairing server:
|
||||
// 1) A server thread that waits for incoming client connections, and
|
||||
// 2) A connection events thread that synchonizes events from all of the
|
||||
// clients, since each PairingConnection is running in it's own thread.
|
||||
void StartConnectionEventsThread();
|
||||
void StartServerThread();
|
||||
|
||||
static void PairingConnectionCallback(const PeerInfo* peer_info, int fd, void* opaque);
|
||||
|
||||
std::thread conn_events_thread_;
|
||||
void ConnectionEventsWorker();
|
||||
std::thread server_thread_;
|
||||
void ServerWorker();
|
||||
bool is_terminate_ GUARDED_BY(conn_mutex_) = false;
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
State state_ = State::Ready;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
uint16_t port_;
|
||||
|
||||
pairing_server_result_cb cb_;
|
||||
void* opaque_ = nullptr;
|
||||
bool got_valid_pairing_ = false;
|
||||
|
||||
static const int kEpollConstSocket = 0;
|
||||
// Used to break the server thread from epoll_wait
|
||||
static const int kEpollConstEventFd = 1;
|
||||
unique_fd epoll_fd_;
|
||||
unique_fd server_fd_;
|
||||
unique_fd event_fd_;
|
||||
}; // PairingServerCtx
|
||||
|
||||
// static
|
||||
PairingServerCtx::ConnectionPtr PairingServerCtx::CreatePairingConnection(const Data& pswd,
|
||||
const PeerInfo& peer_info,
|
||||
const Data& cert,
|
||||
const Data& priv_key) {
|
||||
return ConnectionPtr(pairing_connection_server_new(pswd.data(), pswd.size(), &peer_info,
|
||||
cert.data(), cert.size(), priv_key.data(),
|
||||
priv_key.size()));
|
||||
}
|
||||
|
||||
PairingServerCtx::PairingServerCtx(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key, uint16_t port)
|
||||
: pswd_(pswd), peer_info_(peer_info), cert_(cert), priv_key_(priv_key), port_(port) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
|
||||
}
|
||||
|
||||
PairingServerCtx::~PairingServerCtx() {
|
||||
// Since these connections have references to us, let's make sure they
|
||||
// destruct before us.
|
||||
if (server_thread_.joinable()) {
|
||||
StopServer();
|
||||
server_thread_.join();
|
||||
}
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(conn_mutex_);
|
||||
is_terminate_ = true;
|
||||
}
|
||||
conn_cv_.notify_one();
|
||||
if (conn_events_thread_.joinable()) {
|
||||
conn_events_thread_.join();
|
||||
}
|
||||
|
||||
// Notify the cb_ if it hasn't already.
|
||||
if (!got_valid_pairing_ && cb_ != nullptr) {
|
||||
cb_(nullptr, opaque_);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t PairingServerCtx::Start(pairing_server_result_cb cb, void* opaque) {
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
if (state_ != State::Ready) {
|
||||
LOG(ERROR) << "PairingServerCtx already running or stopped";
|
||||
return 0;
|
||||
}
|
||||
|
||||
port_ = SetupServer();
|
||||
if (port_ == 0) {
|
||||
LOG(ERROR) << "Unable to start PairingServer";
|
||||
state_ = State::Stopped;
|
||||
return 0;
|
||||
}
|
||||
LOG(INFO) << "Pairing server started on port " << port_;
|
||||
|
||||
state_ = State::Running;
|
||||
return port_;
|
||||
}
|
||||
|
||||
void PairingServerCtx::StopServer() {
|
||||
if (event_fd_.get() == -1) {
|
||||
return;
|
||||
}
|
||||
uint64_t value = 1;
|
||||
ssize_t rc = write(event_fd_.get(), &value, sizeof(value));
|
||||
if (rc == -1) {
|
||||
// This can happen if the server didn't start.
|
||||
PLOG(ERROR) << "write to eventfd failed";
|
||||
} else if (rc != sizeof(value)) {
|
||||
LOG(FATAL) << "write to event returned short (" << rc << ")";
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t PairingServerCtx::SetupServer() {
|
||||
epoll_fd_.reset(epoll_create1(EPOLL_CLOEXEC));
|
||||
if (epoll_fd_ == -1) {
|
||||
PLOG(ERROR) << "failed to create epoll fd";
|
||||
return 0;
|
||||
}
|
||||
|
||||
event_fd_.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK));
|
||||
if (event_fd_ == -1) {
|
||||
PLOG(ERROR) << "failed to create eventfd";
|
||||
return 0;
|
||||
}
|
||||
|
||||
server_fd_.reset(socket_inaddr_any_server(port_, SOCK_STREAM));
|
||||
if (server_fd_.get() == -1) {
|
||||
PLOG(ERROR) << "Failed to start pairing connection server";
|
||||
return 0;
|
||||
} else if (fcntl(server_fd_.get(), F_SETFD, FD_CLOEXEC) != 0) {
|
||||
PLOG(ERROR) << "Failed to make server socket cloexec";
|
||||
return 0;
|
||||
} else if (fcntl(server_fd_.get(), F_SETFD, O_NONBLOCK) != 0) {
|
||||
PLOG(ERROR) << "Failed to make server socket nonblocking";
|
||||
return 0;
|
||||
}
|
||||
|
||||
StartConnectionEventsThread();
|
||||
StartServerThread();
|
||||
int port = socket_get_local_port(server_fd_.get());
|
||||
return (port <= 0 ? 0 : port);
|
||||
}
|
||||
|
||||
void PairingServerCtx::StartServerThread() {
|
||||
server_thread_ = std::thread([this]() { ServerWorker(); });
|
||||
}
|
||||
|
||||
void PairingServerCtx::StartConnectionEventsThread() {
|
||||
conn_events_thread_ = std::thread([this]() { ConnectionEventsWorker(); });
|
||||
}
|
||||
|
||||
void PairingServerCtx::ServerWorker() {
|
||||
{
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = kEpollConstSocket;
|
||||
CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, server_fd_.get(), &event));
|
||||
}
|
||||
|
||||
{
|
||||
struct epoll_event event;
|
||||
event.events = EPOLLIN;
|
||||
event.data.u64 = kEpollConstEventFd;
|
||||
CHECK_EQ(0, epoll_ctl(epoll_fd_.get(), EPOLL_CTL_ADD, event_fd_.get(), &event));
|
||||
}
|
||||
|
||||
while (true) {
|
||||
struct epoll_event events[2];
|
||||
int rc = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd_.get(), events, 2, -1));
|
||||
if (rc == -1) {
|
||||
PLOG(ERROR) << "epoll_wait failed";
|
||||
return;
|
||||
} else if (rc == 0) {
|
||||
LOG(ERROR) << "epoll_wait returned 0";
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < rc; ++i) {
|
||||
struct epoll_event& event = events[i];
|
||||
switch (event.data.u64) {
|
||||
case kEpollConstSocket:
|
||||
HandleNewClientConnection(server_fd_.get());
|
||||
break;
|
||||
case kEpollConstEventFd:
|
||||
uint64_t dummy;
|
||||
int rc = TEMP_FAILURE_RETRY(read(event_fd_.get(), &dummy, sizeof(dummy)));
|
||||
if (rc != sizeof(dummy)) {
|
||||
PLOG(FATAL) << "failed to read from eventfd (rc=" << rc << ")";
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
void PairingServerCtx::PairingConnectionCallback(const PeerInfo* peer_info, int fd, void* opaque) {
|
||||
auto* p = reinterpret_cast<PairingServerCtx*>(opaque);
|
||||
|
||||
ConnectionFinishedEvent event;
|
||||
if (peer_info != nullptr) {
|
||||
if (peer_info->type == ADB_RSA_PUB_KEY) {
|
||||
event = std::make_tuple(fd, peer_info->type,
|
||||
std::string(reinterpret_cast<const char*>(peer_info->data)));
|
||||
} else {
|
||||
LOG(WARNING) << "Ignoring successful pairing because of unknown "
|
||||
<< "PeerInfo type=" << peer_info->type;
|
||||
}
|
||||
} else {
|
||||
event = std::make_tuple(fd, 0, std::nullopt);
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(p->conn_mutex_);
|
||||
p->conn_write_queue_.push_back(std::move(event));
|
||||
}
|
||||
p->conn_cv_.notify_one();
|
||||
}
|
||||
|
||||
void PairingServerCtx::ConnectionEventsWorker() {
|
||||
uint8_t num_tries = 0;
|
||||
for (;;) {
|
||||
// Transfer the write queue to the read queue.
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(conn_mutex_);
|
||||
ScopedLockAssertion assume_locked(conn_mutex_);
|
||||
|
||||
if (is_terminate_) {
|
||||
// We check |is_terminate_| twice because condition_variable's
|
||||
// notify() only wakes up a thread if it is in the wait state
|
||||
// prior to notify(). Furthermore, we aren't holding the mutex
|
||||
// when processing the events in |conn_read_queue_|.
|
||||
return;
|
||||
}
|
||||
if (conn_write_queue_.empty()) {
|
||||
// We need to wait for new events, or the termination signal.
|
||||
conn_cv_.wait(lock, [this]() REQUIRES(conn_mutex_) {
|
||||
return (is_terminate_ || !conn_write_queue_.empty());
|
||||
});
|
||||
}
|
||||
if (is_terminate_) {
|
||||
// We're done.
|
||||
return;
|
||||
}
|
||||
// Move all events into the read queue.
|
||||
conn_read_queue_ = std::move(conn_write_queue_);
|
||||
conn_write_queue_.clear();
|
||||
}
|
||||
|
||||
// Process all events in the read queue.
|
||||
while (conn_read_queue_.size() > 0) {
|
||||
auto& event = conn_read_queue_.front();
|
||||
if (auto* p = std::get_if<NewConnectionEvent>(&event)) {
|
||||
// Ignore if we are already at the max number of connections
|
||||
if (connections_.size() >= internal::kMaxConnections) {
|
||||
conn_read_queue_.pop_front();
|
||||
continue;
|
||||
}
|
||||
auto [ufd, connection] = std::move(*p);
|
||||
int fd = ufd.release();
|
||||
bool started = pairing_connection_start(connection.get(), fd,
|
||||
PairingConnectionCallback, this);
|
||||
if (!started) {
|
||||
LOG(ERROR) << "PairingServer unable to start a PairingConnection fd=" << fd;
|
||||
ufd.reset(fd);
|
||||
} else {
|
||||
connections_[fd] = std::move(connection);
|
||||
}
|
||||
} else if (auto* p = std::get_if<ConnectionFinishedEvent>(&event)) {
|
||||
auto [fd, info_type, public_key] = std::move(*p);
|
||||
if (public_key.has_value() && !public_key->empty()) {
|
||||
// Valid pairing. Let's shutdown the server and close any
|
||||
// pairing connections in progress.
|
||||
StopServer();
|
||||
connections_.clear();
|
||||
|
||||
PeerInfo info = {};
|
||||
info.type = info_type;
|
||||
strncpy(reinterpret_cast<char*>(info.data), public_key->data(),
|
||||
public_key->size());
|
||||
|
||||
cb_(&info, opaque_);
|
||||
|
||||
got_valid_pairing_ = true;
|
||||
return;
|
||||
}
|
||||
// Invalid pairing. Close the invalid connection.
|
||||
if (connections_.find(fd) != connections_.end()) {
|
||||
connections_.erase(fd);
|
||||
}
|
||||
|
||||
if (++num_tries >= internal::kMaxPairingAttempts) {
|
||||
cb_(nullptr, opaque_);
|
||||
// To prevent the destructor from calling it again.
|
||||
cb_ = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
conn_read_queue_.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PairingServerCtx::HandleNewClientConnection(int fd) {
|
||||
unique_fd ufd(TEMP_FAILURE_RETRY(accept4(fd, nullptr, nullptr, SOCK_CLOEXEC)));
|
||||
if (ufd == -1) {
|
||||
PLOG(WARNING) << "adb_socket_accept failed fd=" << fd;
|
||||
return false;
|
||||
}
|
||||
auto connection = CreatePairingConnection(pswd_, peer_info_, cert_, priv_key_);
|
||||
if (connection == nullptr) {
|
||||
LOG(ERROR) << "PairingServer unable to create a PairingConnection fd=" << fd;
|
||||
return false;
|
||||
}
|
||||
// send the new connection to the connection thread for further processing
|
||||
NewConnectionEvent event = std::make_tuple(std::move(ufd), std::move(connection));
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(conn_mutex_);
|
||||
conn_write_queue_.push_back(std::move(event));
|
||||
}
|
||||
conn_cv_.notify_one();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint16_t pairing_server_start(PairingServerCtx* ctx, pairing_server_result_cb cb, void* opaque) {
|
||||
return ctx->Start(cb, opaque);
|
||||
}
|
||||
|
||||
PairingServerCtx* pairing_server_new(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info, const uint8_t* x509_cert_pem,
|
||||
size_t x509_size, const uint8_t* priv_key_pem,
|
||||
size_t priv_size, uint16_t port) {
|
||||
CHECK(pswd);
|
||||
CHECK_GT(pswd_len, 0U);
|
||||
CHECK(x509_cert_pem);
|
||||
CHECK_GT(x509_size, 0U);
|
||||
CHECK(priv_key_pem);
|
||||
CHECK_GT(priv_size, 0U);
|
||||
CHECK(peer_info);
|
||||
std::vector<uint8_t> vec_pswd(pswd, pswd + pswd_len);
|
||||
std::vector<uint8_t> vec_x509_cert(x509_cert_pem, x509_cert_pem + x509_size);
|
||||
std::vector<uint8_t> vec_priv_key(priv_key_pem, priv_key_pem + priv_size);
|
||||
return new PairingServerCtx(vec_pswd, *peer_info, vec_x509_cert, vec_priv_key, port);
|
||||
}
|
||||
|
||||
PairingServerCtx* pairing_server_new_no_cert(const uint8_t* pswd, size_t pswd_len,
|
||||
const PeerInfo* peer_info, uint16_t port) {
|
||||
auto rsa_2048 = CreateRSA2048Key();
|
||||
auto x509_cert = GenerateX509Certificate(rsa_2048->GetEvpPkey());
|
||||
std::string pkey_pem = Key::ToPEMString(rsa_2048->GetEvpPkey());
|
||||
std::string cert_pem = X509ToPEMString(x509_cert.get());
|
||||
|
||||
return pairing_server_new(pswd, pswd_len, peer_info,
|
||||
reinterpret_cast<const uint8_t*>(cert_pem.data()), cert_pem.size(),
|
||||
reinterpret_cast<const uint8_t*>(pkey_pem.data()), pkey_pem.size(),
|
||||
port);
|
||||
}
|
||||
|
||||
void pairing_server_destroy(PairingServerCtx* ctx) {
|
||||
CHECK(ctx);
|
||||
delete ctx;
|
||||
}
|
47
adb/pairing_connection/tests/Android.bp
Normal file
47
adb/pairing_connection/tests/Android.bp
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// Copyright (C) 2020 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_pairing_connection_test",
|
||||
srcs: [
|
||||
"pairing_client.cpp",
|
||||
"pairing_connection_test.cpp",
|
||||
],
|
||||
|
||||
compile_multilib: "first",
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcutils",
|
||||
"libcrypto",
|
||||
"libcrypto_utils",
|
||||
"libprotobuf-cpp-lite",
|
||||
"libssl",
|
||||
],
|
||||
|
||||
// Let's statically link them so we don't have to install it onto the
|
||||
// system image for testing.
|
||||
static_libs: [
|
||||
"libadb_pairing_auth_static",
|
||||
"libadb_pairing_connection_static",
|
||||
"libadb_pairing_server_static",
|
||||
"libadb_crypto_static",
|
||||
"libadb_protos_static",
|
||||
"libadb_tls_connection_static",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
}
|
201
adb/pairing_connection/tests/pairing_client.cpp
Normal file
201
adb/pairing_connection/tests/pairing_client.cpp
Normal file
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 "pairing_client.h"
|
||||
|
||||
#include <netdb.h>
|
||||
#include <netinet/tcp.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <iomanip>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/parsenetaddress.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
#include <android-base/thread_annotations.h>
|
||||
#include <android-base/unique_fd.h>
|
||||
#include <cutils/sockets.h>
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
using android::base::unique_fd;
|
||||
|
||||
static void ConnectionDeleter(PairingConnectionCtx* p) {
|
||||
pairing_connection_destroy(p);
|
||||
}
|
||||
using ConnectionPtr = std::unique_ptr<PairingConnectionCtx, decltype(&ConnectionDeleter)>;
|
||||
|
||||
namespace {
|
||||
|
||||
class PairingClientImpl : public PairingClient {
|
||||
public:
|
||||
explicit PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key);
|
||||
|
||||
// Starts the pairing client. This call is non-blocking. Upon pairing
|
||||
// completion, |cb| will be called with the PeerInfo on success,
|
||||
// or an empty value on failure.
|
||||
//
|
||||
// Returns true if PairingClient was successfully started. Otherwise,
|
||||
// return false.
|
||||
virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb,
|
||||
void* opaque) override;
|
||||
|
||||
private:
|
||||
static ConnectionPtr CreatePairingConnection(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key);
|
||||
|
||||
static void PairingResultCallback(const PeerInfo* peer_info, int fd, void* opaque);
|
||||
// Setup and start the PairingConnection
|
||||
bool StartConnection();
|
||||
|
||||
enum class State {
|
||||
Ready,
|
||||
Running,
|
||||
Stopped,
|
||||
};
|
||||
|
||||
State state_ = State::Ready;
|
||||
Data pswd_;
|
||||
PeerInfo peer_info_;
|
||||
Data cert_;
|
||||
Data priv_key_;
|
||||
std::string host_;
|
||||
int port_;
|
||||
|
||||
ConnectionPtr connection_;
|
||||
pairing_client_result_cb cb_;
|
||||
void* opaque_ = nullptr;
|
||||
}; // PairingClientImpl
|
||||
|
||||
// static
|
||||
ConnectionPtr PairingClientImpl::CreatePairingConnection(const Data& pswd,
|
||||
const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key) {
|
||||
return ConnectionPtr(
|
||||
pairing_connection_client_new(pswd.data(), pswd.size(), &peer_info, cert.data(),
|
||||
cert.size(), priv_key.data(), priv_key.size()),
|
||||
ConnectionDeleter);
|
||||
}
|
||||
|
||||
PairingClientImpl::PairingClientImpl(const Data& pswd, const PeerInfo& peer_info, const Data& cert,
|
||||
const Data& priv_key)
|
||||
: pswd_(pswd),
|
||||
peer_info_(peer_info),
|
||||
cert_(cert),
|
||||
priv_key_(priv_key),
|
||||
connection_(nullptr, ConnectionDeleter) {
|
||||
CHECK(!pswd_.empty() && !cert_.empty() && !priv_key_.empty());
|
||||
|
||||
state_ = State::Ready;
|
||||
}
|
||||
|
||||
bool PairingClientImpl::Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) {
|
||||
CHECK(!ip_addr.empty());
|
||||
cb_ = cb;
|
||||
opaque_ = opaque;
|
||||
|
||||
if (state_ != State::Ready) {
|
||||
LOG(ERROR) << "PairingClient already running or finished";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try to parse the host address
|
||||
std::string err;
|
||||
CHECK(android::base::ParseNetAddress(std::string(ip_addr), &host_, &port_, nullptr, &err));
|
||||
CHECK(port_ > 0 && port_ <= 65535);
|
||||
|
||||
if (!StartConnection()) {
|
||||
LOG(ERROR) << "Unable to start PairingClient connection";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
state_ = State::Running;
|
||||
return true;
|
||||
}
|
||||
|
||||
static int network_connect(const std::string& host, int port, int type, int timeout,
|
||||
std::string* error) {
|
||||
int getaddrinfo_error = 0;
|
||||
int fd = socket_network_client_timeout(host.c_str(), port, type, timeout, &getaddrinfo_error);
|
||||
if (fd != -1) {
|
||||
return fd;
|
||||
}
|
||||
if (getaddrinfo_error != 0) {
|
||||
*error = android::base::StringPrintf("failed to resolve host: '%s': %s", host.c_str(),
|
||||
gai_strerror(getaddrinfo_error));
|
||||
LOG(WARNING) << *error;
|
||||
} else {
|
||||
*error = android::base::StringPrintf("failed to connect to '%s:%d': %s", host.c_str(), port,
|
||||
strerror(errno));
|
||||
LOG(WARNING) << *error;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// static
|
||||
void PairingClientImpl::PairingResultCallback(const PeerInfo* peer_info, int /* fd */,
|
||||
void* opaque) {
|
||||
auto* p = reinterpret_cast<PairingClientImpl*>(opaque);
|
||||
p->cb_(peer_info, p->opaque_);
|
||||
}
|
||||
|
||||
bool PairingClientImpl::StartConnection() {
|
||||
std::string err;
|
||||
const int timeout = 10; // seconds
|
||||
unique_fd fd(network_connect(host_, port_, SOCK_STREAM, timeout, &err));
|
||||
if (fd.get() == -1) {
|
||||
LOG(ERROR) << "Failed to start pairing connection client [" << err << "]";
|
||||
return false;
|
||||
}
|
||||
int off = 1;
|
||||
setsockopt(fd.get(), IPPROTO_TCP, TCP_NODELAY, &off, sizeof(off));
|
||||
|
||||
connection_ = CreatePairingConnection(pswd_, peer_info_, cert_, priv_key_);
|
||||
if (connection_ == nullptr) {
|
||||
LOG(ERROR) << "PairingClient unable to create a PairingConnection";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!pairing_connection_start(connection_.get(), fd.release(), PairingResultCallback, this)) {
|
||||
LOG(ERROR) << "PairingClient failed to start the PairingConnection";
|
||||
state_ = State::Stopped;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
std::unique_ptr<PairingClient> PairingClient::Create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& cert, const Data& priv_key) {
|
||||
CHECK(!pswd.empty());
|
||||
CHECK(!cert.empty());
|
||||
CHECK(!priv_key.empty());
|
||||
|
||||
return std::unique_ptr<PairingClient>(new PairingClientImpl(pswd, peer_info, cert, priv_key));
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
68
adb/pairing_connection/tests/pairing_client.h
Normal file
68
adb/pairing_connection/tests/pairing_client.h
Normal file
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2020 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
#include "adb/pairing/pairing_connection.h"
|
||||
|
||||
typedef void (*pairing_client_result_cb)(const PeerInfo*, void*);
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
// PairingClient is the client side of the PairingConnection protocol. It will
|
||||
// attempt to connect to a PairingServer specified at |host| and |port|, and
|
||||
// allocate a new PairingConnection for processing.
|
||||
//
|
||||
// See pairing_connection_test.cpp for example usage.
|
||||
//
|
||||
class PairingClient {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
|
||||
virtual ~PairingClient() = default;
|
||||
|
||||
// Starts the pairing client. This call is non-blocking. Upon completion,
|
||||
// if the pairing was successful, then |cb| will be called with the PeerInfo
|
||||
// containing the info of the trusted peer. Otherwise, |cb| will be
|
||||
// called with an empty value. Start can only be called once in the lifetime
|
||||
// of this object. |ip_addr| requires a port to be specified.
|
||||
//
|
||||
// Returns true if PairingClient was successfully started. Otherwise,
|
||||
// returns false.
|
||||
virtual bool Start(std::string_view ip_addr, pairing_client_result_cb cb, void* opaque) = 0;
|
||||
|
||||
// Creates a new PairingClient instance. May return null if unable
|
||||
// to create an instance. |pswd|, |certificate|, |priv_key| and
|
||||
// |ip_addr| cannot be empty. |peer_info| must contain non-empty strings for
|
||||
// the guid and name fields.
|
||||
static std::unique_ptr<PairingClient> Create(const Data& pswd, const PeerInfo& peer_info,
|
||||
const Data& certificate, const Data& priv_key);
|
||||
|
||||
protected:
|
||||
PairingClient() = default;
|
||||
}; // class PairingClient
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
500
adb/pairing_connection/tests/pairing_connection_test.cpp
Normal file
500
adb/pairing_connection/tests/pairing_connection_test.cpp
Normal file
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
* Copyright 2020 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.
|
||||
*/
|
||||
|
||||
#define LOG_TAG "AdbPairingConnectionTest"
|
||||
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include <adb/pairing/pairing_server.h>
|
||||
#include <android-base/logging.h>
|
||||
#include <android-base/strings.h>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../internal/constants.h"
|
||||
#include "pairing_client.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
// Test X.509 certificates (RSA 2048)
|
||||
static const std::string kTestRsa2048ServerCert =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
|
||||
"MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NVoX\n"
|
||||
"DTMwMDExODIyMjU1NVowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
|
||||
"DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAK8E\n"
|
||||
"2Ck9TfuKlz7wqWdMfknjZ1luFDp2IHxAUZzh/F6jeI2dOFGAjpeloSnGOE86FIaT\n"
|
||||
"d1EvpyTh7nBwbrLZAA6XFZTo7Bl6BdNOQdqb2d2+cLEN0inFxqUIycevRtohUE1Y\n"
|
||||
"FHM9fg442X1jOTWXjDZWeiqFWo95paAPhzm6pWqfJK1+YKfT1LsWZpYqJGGQE5pi\n"
|
||||
"C3qOBYYgFpoXMxTYJNoZo3uOYEdM6upc8/vh15nMgIxX/ymJxEY5BHPpZPPWjXLg\n"
|
||||
"BfzVaV9fUfv0JT4HQ4t2WvxC3cD/UsjWp2a6p454uUp2ENrANa+jRdRJepepg9D2\n"
|
||||
"DKsx9L8zjc5Obqexrt0CAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
|
||||
"Af8EBAMCAYYwHQYDVR0OBBYEFDFW+8GTErwoZN5Uu9KyY4QdGYKpMA0GCSqGSIb3\n"
|
||||
"DQEBCwUAA4IBAQBCDEn6SHXGlq5TU7J8cg1kRPd9bsJW+0hDuKSq0REXDkl0PcBf\n"
|
||||
"fy282Agg9enKPPKmnpeQjM1dmnxdM8tT8LIUbMl779i3fn6v9HJVB+yG4gmRFThW\n"
|
||||
"c+AGlBnrIT820cX/gU3h3R3FTahfsq+1rrSJkEgHyuC0HYeRyveSckBdaEOLvx0S\n"
|
||||
"toun+32JJl5hWydpUUZhE9Mbb3KHBRM2YYZZU9JeJ08Apjl+3lRUeMAUwI5fkAAu\n"
|
||||
"z/1SqnuGL96bd8P5ixdkA1+rF8FPhodGcq9mQOuUGP9g5HOXjaNoJYvwVRUdLeGh\n"
|
||||
"cP/ReOTwQIzM1K5a83p8cX8AGGYmM7dQp7ec\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
static const std::string kTestRsa2048ServerPrivKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCvBNgpPU37ipc+\n"
|
||||
"8KlnTH5J42dZbhQ6diB8QFGc4fxeo3iNnThRgI6XpaEpxjhPOhSGk3dRL6ck4e5w\n"
|
||||
"cG6y2QAOlxWU6OwZegXTTkHam9ndvnCxDdIpxcalCMnHr0baIVBNWBRzPX4OONl9\n"
|
||||
"Yzk1l4w2VnoqhVqPeaWgD4c5uqVqnyStfmCn09S7FmaWKiRhkBOaYgt6jgWGIBaa\n"
|
||||
"FzMU2CTaGaN7jmBHTOrqXPP74deZzICMV/8picRGOQRz6WTz1o1y4AX81WlfX1H7\n"
|
||||
"9CU+B0OLdlr8Qt3A/1LI1qdmuqeOeLlKdhDawDWvo0XUSXqXqYPQ9gyrMfS/M43O\n"
|
||||
"Tm6nsa7dAgMBAAECggEAFCS2bPdUKIgjbzLgtHW+hT+J2hD20rcHdyAp+dNH/2vI\n"
|
||||
"yLfDJHJA4chGMRondKA704oDw2bSJxxlG9t83326lB35yxPhye7cM8fqgWrK8PVl\n"
|
||||
"tU22FhO1ZgeJvb9OeXWNxKZyDW9oOOJ8eazNXVMuEo+dFj7B6l3MXQyHJPL2mJDm\n"
|
||||
"u9ofFLdypX+gJncVO0oW0FNJnEUn2MMwHDNlo7gc4WdQuidPkuZItKRGcB8TTGF3\n"
|
||||
"Ka1/2taYdTQ4Aq//Z84LlFvE0zD3T4c8LwYYzOzD4gGGTXvft7vSHzIun1S8YLRS\n"
|
||||
"dEKXdVjtaFhgH3uUe4j+1b/vMvSHeoGBNX/G88GD+wKBgQDWUYVlMVqc9HD2IeYi\n"
|
||||
"EfBcNwAJFJkh51yAl5QbUBgFYgFJVkkS/EDxEGFPvEmI3/pAeQFHFY13BI466EPs\n"
|
||||
"o8Z8UUwWDp+Z1MFHHKQKnFakbsZbZlbqjJ9VJsqpezbpWhMHTOmcG0dmE7rf0lyM\n"
|
||||
"eQv9slBB8qp2NEUs5Of7f2C2bwKBgQDRDq4nUuMQF1hbjM05tGKSIwkobmGsLspv\n"
|
||||
"TMhkM7fq4RpbFHmbNgsFqMhcqYZ8gY6/scv5KCuAZ4yHUkbqwf5h+QCwrJ4uJeUJ\n"
|
||||
"ZgJfHus2mmcNSo8FwSkNoojIQtzcbJav7bs2K9VTuertk/i7IJLApU4FOZZ5pghN\n"
|
||||
"EXu0CZF1cwKBgDWFGhjRIF29tU/h20R60llU6s9Zs3wB+NmsALJpZ/ZAKS4VPB5f\n"
|
||||
"nCAXBRYSYRKrTCU5kpYbzb4BBzuysPOxWmnFK4j+keCqfrGxd02nCQP7HdHJVr8v\n"
|
||||
"6sIq88UrHeVcNxBFprjzHvtgxfQK5k22FMZ/9wbhAKyQFQ5HA5+MiaxFAoGAIcZZ\n"
|
||||
"ZIkDninnYIMS9OursShv5lRO+15j3i9tgKLKZ+wOMgDQ1L6acUOfezj4PU1BHr8+\n"
|
||||
"0PYocQpJreMhCfRlgLaV4fVBaPs+UZJld7CrF5tCYudUy/01ALrtlk0XGZWBktK5\n"
|
||||
"mDrksC4tQkzRtonAq9cJD9cJ9IVaefkFH0UcdvkCgYBpZj50VLeGhnHHBnkJRlV1\n"
|
||||
"fV+/P6PAq6RtqjA6O9Qdaoj5V3w2d63aQcQXQLJjH2BBmtCIy47r04rFvZpbCxP7\n"
|
||||
"NH/OnK9NHpk2ucRTe8TAnVbvF/TZzPJoIxAO/D3OWaW6df4R8en8u6GYzWFglAyT\n"
|
||||
"sydGT8yfWD1FYUWgfrVRbg==\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
static const std::string kTestRsa2048ClientCert =
|
||||
"-----BEGIN CERTIFICATE-----\n"
|
||||
"MIIDFzCCAf+gAwIBAgIBATANBgkqhkiG9w0BAQsFADAtMQswCQYDVQQGEwJVUzEQ\n"
|
||||
"MA4GA1UECgwHQW5kcm9pZDEMMAoGA1UEAwwDQWRiMB4XDTIwMDEyMTIyMjU1NloX\n"
|
||||
"DTMwMDExODIyMjU1NlowLTELMAkGA1UEBhMCVVMxEDAOBgNVBAoMB0FuZHJvaWQx\n"
|
||||
"DDAKBgNVBAMMA0FkYjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAI3a\n"
|
||||
"EXh1S5FTbet7JVONswffRPaekdIK53cb8SnAbSO9X5OLA4zGwdkrBvDTsd96SKrp\n"
|
||||
"JxmoNOE1DhbZh05KPlWAPkGKacjGWaz+S7biDOL0I6aaLbTlU/il1Ub9olPSBVUx\n"
|
||||
"0nhdtEFgIOzddnP6/1KmyIIeRxS5lTKeg4avqUkZNXkz/wL1dHBFL7FNFf0SCcbo\n"
|
||||
"tsub/deFbjZ27LTDN+SIBgFttTNqC5NTvoBAoMdyCOAgNYwaHO+fKiK3edfJieaw\n"
|
||||
"7HD8qqmQxcpCtRlA8CUPj7GfR+WHiCJmlevhnkFXCo56R1BS0F4wuD4KPdSWt8gc\n"
|
||||
"27ejH/9/z2cKo/6SLJMCAwEAAaNCMEAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8B\n"
|
||||
"Af8EBAMCAYYwHQYDVR0OBBYEFO/Mr5ygqqpyU/EHM9v7RDvcqaOkMA0GCSqGSIb3\n"
|
||||
"DQEBCwUAA4IBAQAH33KMouzF2DYbjg90KDrDQr4rq3WfNb6P743knxdUFuvb+40U\n"
|
||||
"QjC2OJZHkSexH7wfG/y6ic7vfCfF4clNs3QvU1lEjOZC57St8Fk7mdNdsWLwxEMD\n"
|
||||
"uePFz0dvclSxNUHyCVMqNxddzQYzxiDWQRmXWrUBliMduQqEQelcxW2yDtg8bj+s\n"
|
||||
"aMpR1ra9scaD4jzIZIIxLoOS9zBMuNRbgP217sZrniyGMhzoI1pZ/izN4oXpyH7O\n"
|
||||
"THuaCzzRT3ph2f8EgmHSodz3ttgSf2DHzi/Ez1xUkk7NOlgNtmsxEdrM47+cC5ae\n"
|
||||
"fIf2V+1o1JW8J7D11RmRbNPh3vfisueB4f88\n"
|
||||
"-----END CERTIFICATE-----\n";
|
||||
|
||||
static const std::string kTestRsa2048ClientPrivKey =
|
||||
"-----BEGIN PRIVATE KEY-----\n"
|
||||
"MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCN2hF4dUuRU23r\n"
|
||||
"eyVTjbMH30T2npHSCud3G/EpwG0jvV+TiwOMxsHZKwbw07Hfekiq6ScZqDThNQ4W\n"
|
||||
"2YdOSj5VgD5BimnIxlms/ku24gzi9COmmi205VP4pdVG/aJT0gVVMdJ4XbRBYCDs\n"
|
||||
"3XZz+v9SpsiCHkcUuZUynoOGr6lJGTV5M/8C9XRwRS+xTRX9EgnG6LbLm/3XhW42\n"
|
||||
"duy0wzfkiAYBbbUzaguTU76AQKDHcgjgIDWMGhzvnyoit3nXyYnmsOxw/KqpkMXK\n"
|
||||
"QrUZQPAlD4+xn0flh4giZpXr4Z5BVwqOekdQUtBeMLg+Cj3UlrfIHNu3ox//f89n\n"
|
||||
"CqP+kiyTAgMBAAECggEAAa64eP6ggCob1P3c73oayYPIbvRqiQdAFOrr7Vwu7zbr\n"
|
||||
"z0rde+n6RU0mrpc+4NuzyPMtrOGQiatLbidJB5Cx3z8U00ovqbCl7PtcgorOhFKe\n"
|
||||
"VEzihebCcYyQqbWQcKtpDMhOgBxRwFoXieJb6VGXfa96FAZalCWvXgOrTl7/BF2X\n"
|
||||
"qMqIm9nJi+yS5tIO8VdOsOmrMWRH/b/ENUcef4WpLoxTXr0EEgyKWraeZ/hhXo1e\n"
|
||||
"z29dZKqdr9wMsq11NPsRddwS94jnDkXTo+EQyWVTfB7gb6yyp07s8jysaDb21tVv\n"
|
||||
"UXB9MRhDV1mOv0ncXfXZ4/+4A2UahmZaLDAVLaat4QKBgQDAVRredhGRGl2Nkic3\n"
|
||||
"KvZCAfyxug788CgasBdEiouz19iCCwcgMIDwnq0s3/WM7h/laCamT2x38riYDnpq\n"
|
||||
"rkYMfuVtU9CjEL9pTrdfwbIRhTwYNqADaPz2mXwQUhRXutE5TIdgxxC/a+ZTh0qN\n"
|
||||
"S+vhTj/4hf0IZhMh5Nqj7IPExQKBgQC8zxEzhmSGjys0GuE6Wl6Doo2TpiR6vwvi\n"
|
||||
"xPLU9lmIz5eca/Rd/eERioFQqeoIWDLzx52DXuz6rUoQhbJWz9hP3yqCwXD+pbNP\n"
|
||||
"oDJqDDbCC4IMYEb0IK/PEPH+gIpnTjoFcW+ecKDFG7W5Lt05J8WsJsfOaJvMrOU+\n"
|
||||
"dLXq3IgxdwKBgQC5RAFq0v6e8G+3hFaEHL0z3igkpt3zJf7rnj37hx2FMmDa+3Z0\n"
|
||||
"umQp5B9af61PgL12xLmeMBmC/Wp1BlVDV/Yf6Uhk5Hyv5t0KuomHEtTNbbLyfAPs\n"
|
||||
"5P/vJu/L5NS1oT4S3LX3MineyjgGs+bLbpub3z1dzutrYLADUSiPCK/xJQKBgBQt\n"
|
||||
"nQ0Ao+Wtj1R2OvPdjJRM3wyUiPmFSWPm4HzaBx+T8AQLlYYmB9O0FbXlMtnJc0iS\n"
|
||||
"YMcVcgYoVu4FG9YjSF7g3s4yljzgwJUV7c1fmMqMKE3iTDLy+1cJ3JLycdgwiArk\n"
|
||||
"4KTyLHxkRbuQwpvFIF8RlfD9RQlOwQE3v+llwDhpAoGBAL6XG6Rp6mBoD2Ds5c9R\n"
|
||||
"943yYgSUes3ji1SI9zFqeJtj8Ml/enuK1xu+8E/BxB0//+vgZsH6i3i8GFwygKey\n"
|
||||
"CGJF8CbiHc3EJc3NQIIRXcni/CGacf0HwC6m+PGFDBIpA4H2iDpVvCSofxttQiq0\n"
|
||||
"/Z7HXmXUvZHVyYi/QzX2Gahj\n"
|
||||
"-----END PRIVATE KEY-----\n";
|
||||
|
||||
struct ServerDeleter {
|
||||
void operator()(PairingServerCtx* p) { pairing_server_destroy(p); }
|
||||
};
|
||||
using ServerPtr = std::unique_ptr<PairingServerCtx, ServerDeleter>;
|
||||
|
||||
struct ResultWaiter {
|
||||
std::mutex mutex_;
|
||||
std::condition_variable cv_;
|
||||
std::optional<bool> is_valid_;
|
||||
PeerInfo peer_info_;
|
||||
|
||||
static void ResultCallback(const PeerInfo* peer_info, void* opaque) {
|
||||
auto* p = reinterpret_cast<ResultWaiter*>(opaque);
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(p->mutex_);
|
||||
if (peer_info) {
|
||||
memcpy(&(p->peer_info_), peer_info, sizeof(PeerInfo));
|
||||
}
|
||||
p->is_valid_ = (peer_info != nullptr);
|
||||
}
|
||||
p->cv_.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
class AdbPairingConnectionTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() override {}
|
||||
|
||||
virtual void TearDown() override {}
|
||||
|
||||
void InitPairing(const std::vector<uint8_t>& server_pswd,
|
||||
const std::vector<uint8_t>& client_pswd) {
|
||||
server_ = CreateServer(server_pswd);
|
||||
client_ = CreateClient(client_pswd);
|
||||
}
|
||||
|
||||
ServerPtr CreateServer(const std::vector<uint8_t>& pswd) {
|
||||
return CreateServer(pswd, &server_info_, kTestRsa2048ServerCert, kTestRsa2048ServerPrivKey,
|
||||
0);
|
||||
}
|
||||
|
||||
std::unique_ptr<PairingClient> CreateClient(const std::vector<uint8_t> pswd) {
|
||||
std::vector<uint8_t> cert;
|
||||
std::vector<uint8_t> key;
|
||||
// Include the null-byte as well.
|
||||
cert.assign(reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()) +
|
||||
kTestRsa2048ClientCert.size() + 1);
|
||||
key.assign(reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()) +
|
||||
kTestRsa2048ClientPrivKey.size() + 1);
|
||||
return PairingClient::Create(pswd, client_info_, cert, key);
|
||||
}
|
||||
|
||||
static ServerPtr CreateServer(const std::vector<uint8_t>& pswd, const PeerInfo* peer_info,
|
||||
const std::string_view cert, const std::string_view priv_key,
|
||||
int port) {
|
||||
return ServerPtr(pairing_server_new(
|
||||
pswd.data(), pswd.size(), peer_info, reinterpret_cast<const uint8_t*>(cert.data()),
|
||||
cert.size(), reinterpret_cast<const uint8_t*>(priv_key.data()), priv_key.size(),
|
||||
port));
|
||||
}
|
||||
|
||||
ServerPtr server_;
|
||||
const PeerInfo server_info_ = {
|
||||
.type = ADB_DEVICE_GUID,
|
||||
.data = "my_server_info",
|
||||
};
|
||||
std::unique_ptr<PairingClient> client_;
|
||||
const PeerInfo client_info_ = {
|
||||
.type = ADB_RSA_PUB_KEY,
|
||||
.data = "my_client_info",
|
||||
};
|
||||
std::string ip_addr_ = "127.0.0.1:";
|
||||
};
|
||||
|
||||
TEST_F(AdbPairingConnectionTest, ServerCreation) {
|
||||
// All parameters bad
|
||||
ASSERT_DEATH({ auto server = CreateServer({}, nullptr, "", "", 0); }, "");
|
||||
// Bad password
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = CreateServer({}, &server_info_, kTestRsa2048ServerCert,
|
||||
kTestRsa2048ServerPrivKey, 0);
|
||||
},
|
||||
"");
|
||||
// Bad peer_info
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = CreateServer({0x01}, nullptr, kTestRsa2048ServerCert,
|
||||
kTestRsa2048ServerPrivKey, 0);
|
||||
},
|
||||
"");
|
||||
// Bad certificate
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = CreateServer({0x01}, &server_info_, "", kTestRsa2048ServerPrivKey, 0);
|
||||
},
|
||||
"");
|
||||
// Bad private key
|
||||
ASSERT_DEATH(
|
||||
{ auto server = CreateServer({0x01}, &server_info_, kTestRsa2048ServerCert, "", 0); },
|
||||
"");
|
||||
// Valid params
|
||||
auto server = CreateServer({0x01}, &server_info_, kTestRsa2048ServerCert,
|
||||
kTestRsa2048ServerPrivKey, 0);
|
||||
EXPECT_NE(nullptr, server);
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingConnectionTest, ClientCreation) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
// Bad password
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
nullptr, pswd.size(), &client_info_,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
kTestRsa2048ClientCert.size(),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
kTestRsa2048ClientPrivKey.size());
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
pswd.data(), 0, &client_info_,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
kTestRsa2048ClientCert.size(),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
kTestRsa2048ClientPrivKey.size());
|
||||
},
|
||||
"");
|
||||
|
||||
// Bad peer_info
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
pswd.data(), pswd.size(), nullptr,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
kTestRsa2048ClientCert.size(),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
kTestRsa2048ClientPrivKey.size());
|
||||
},
|
||||
"");
|
||||
|
||||
// Bad certificate
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
pswd.data(), pswd.size(), &client_info_, nullptr,
|
||||
kTestRsa2048ClientCert.size(),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
kTestRsa2048ClientPrivKey.size());
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
pswd.data(), pswd.size(), &client_info_,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()), 0,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
kTestRsa2048ClientPrivKey.size());
|
||||
},
|
||||
"");
|
||||
|
||||
// Bad private key
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
pswd.data(), pswd.size(), &client_info_,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
kTestRsa2048ClientCert.size(), nullptr, kTestRsa2048ClientPrivKey.size());
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
pairing_connection_client_new(
|
||||
pswd.data(), pswd.size(), &client_info_,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
kTestRsa2048ClientCert.size(),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()), 0);
|
||||
},
|
||||
"");
|
||||
|
||||
// Valid params
|
||||
auto client = pairing_connection_client_new(
|
||||
pswd.data(), pswd.size(), &client_info_,
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientCert.data()),
|
||||
kTestRsa2048ClientCert.size(),
|
||||
reinterpret_cast<const uint8_t*>(kTestRsa2048ClientPrivKey.data()),
|
||||
kTestRsa2048ClientPrivKey.size());
|
||||
EXPECT_NE(nullptr, client);
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingConnectionTest, SmokeValidPairing) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
InitPairing(pswd, pswd);
|
||||
|
||||
// Start the server
|
||||
ResultWaiter server_waiter;
|
||||
std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
|
||||
auto port = pairing_server_start(server_.get(), server_waiter.ResultCallback, &server_waiter);
|
||||
ASSERT_GT(port, 0);
|
||||
ip_addr_ += std::to_string(port);
|
||||
|
||||
// Start the client
|
||||
ResultWaiter client_waiter;
|
||||
std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
|
||||
ASSERT_TRUE(client_->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
|
||||
client_waiter.cv_.wait(client_lock, [&]() { return client_waiter.is_valid_.has_value(); });
|
||||
ASSERT_TRUE(*(client_waiter.is_valid_));
|
||||
ASSERT_EQ(strlen(reinterpret_cast<const char*>(client_waiter.peer_info_.data)),
|
||||
strlen(reinterpret_cast<const char*>(server_info_.data)));
|
||||
EXPECT_EQ(memcmp(client_waiter.peer_info_.data, server_info_.data, sizeof(server_info_.data)),
|
||||
0);
|
||||
|
||||
// Kill server if the pairing failed, since server only shuts down when
|
||||
// it gets a valid pairing.
|
||||
if (!client_waiter.is_valid_) {
|
||||
server_lock.unlock();
|
||||
server_.reset();
|
||||
} else {
|
||||
server_waiter.cv_.wait(server_lock, [&]() { return server_waiter.is_valid_.has_value(); });
|
||||
ASSERT_TRUE(*(server_waiter.is_valid_));
|
||||
ASSERT_EQ(strlen(reinterpret_cast<const char*>(server_waiter.peer_info_.data)),
|
||||
strlen(reinterpret_cast<const char*>(client_info_.data)));
|
||||
EXPECT_EQ(
|
||||
memcmp(server_waiter.peer_info_.data, client_info_.data, sizeof(client_info_.data)),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingConnectionTest, CancelPairing) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
InitPairing(pswd, pswd2);
|
||||
|
||||
// Start the server
|
||||
ResultWaiter server_waiter;
|
||||
std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
|
||||
auto port = pairing_server_start(server_.get(), server_waiter.ResultCallback, &server_waiter);
|
||||
ASSERT_GT(port, 0);
|
||||
ip_addr_ += std::to_string(port);
|
||||
|
||||
// Start the client. Client should fail to pair
|
||||
ResultWaiter client_waiter;
|
||||
std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
|
||||
ASSERT_TRUE(client_->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
|
||||
client_waiter.cv_.wait(client_lock, [&]() { return client_waiter.is_valid_.has_value(); });
|
||||
ASSERT_FALSE(*(client_waiter.is_valid_));
|
||||
|
||||
// Kill the server. We should still receive the callback with no valid
|
||||
// pairing.
|
||||
server_lock.unlock();
|
||||
server_.reset();
|
||||
server_lock.lock();
|
||||
ASSERT_TRUE(server_waiter.is_valid_.has_value());
|
||||
EXPECT_FALSE(*(server_waiter.is_valid_));
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingConnectionTest, MultipleClientsAllFail) {
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
|
||||
// Start the server
|
||||
auto server = CreateServer(pswd);
|
||||
ResultWaiter server_waiter;
|
||||
std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
|
||||
auto port = pairing_server_start(server.get(), server_waiter.ResultCallback, &server_waiter);
|
||||
ASSERT_GT(port, 0);
|
||||
ip_addr_ += std::to_string(port);
|
||||
|
||||
// Start multiple clients, all with bad passwords
|
||||
int test_num_clients = 5;
|
||||
int num_clients_done = 0;
|
||||
std::mutex global_clients_mutex;
|
||||
std::unique_lock<std::mutex> global_clients_lock(global_clients_mutex);
|
||||
std::condition_variable global_cv_;
|
||||
for (int i = 0; i < test_num_clients; ++i) {
|
||||
std::thread([&]() {
|
||||
auto client = CreateClient(pswd2);
|
||||
ResultWaiter client_waiter;
|
||||
std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
|
||||
ASSERT_TRUE(client->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
|
||||
client_waiter.cv_.wait(client_lock,
|
||||
[&]() { return client_waiter.is_valid_.has_value(); });
|
||||
ASSERT_FALSE(*(client_waiter.is_valid_));
|
||||
{
|
||||
std::lock_guard<std::mutex> global_lock(global_clients_mutex);
|
||||
++num_clients_done;
|
||||
}
|
||||
global_cv_.notify_one();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
global_cv_.wait(global_clients_lock, [&]() { return num_clients_done == test_num_clients; });
|
||||
server_lock.unlock();
|
||||
server.reset();
|
||||
server_lock.lock();
|
||||
ASSERT_TRUE(server_waiter.is_valid_.has_value());
|
||||
EXPECT_FALSE(*(server_waiter.is_valid_));
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingConnectionTest, MultipleClientsOnePass) {
|
||||
// Send multiple clients with bad passwords, but send the last one with the
|
||||
// correct password.
|
||||
std::vector<uint8_t> pswd{0x01, 0x03, 0x05, 0x07};
|
||||
std::vector<uint8_t> pswd2{0x01, 0x03, 0x05, 0x06};
|
||||
|
||||
// Start the server
|
||||
auto server = CreateServer(pswd);
|
||||
ResultWaiter server_waiter;
|
||||
std::unique_lock<std::mutex> server_lock(server_waiter.mutex_);
|
||||
auto port = pairing_server_start(server.get(), server_waiter.ResultCallback, &server_waiter);
|
||||
ASSERT_GT(port, 0);
|
||||
ip_addr_ += std::to_string(port);
|
||||
|
||||
// Start multiple clients, all with bad passwords
|
||||
int test_num_clients = 5;
|
||||
int num_clients_done = 0;
|
||||
std::mutex global_clients_mutex;
|
||||
std::unique_lock<std::mutex> global_clients_lock(global_clients_mutex);
|
||||
std::condition_variable global_cv_;
|
||||
for (int i = 0; i < test_num_clients; ++i) {
|
||||
std::thread([&, i]() {
|
||||
bool good_client = (i == (test_num_clients - 1));
|
||||
auto client = CreateClient((good_client ? pswd : pswd2));
|
||||
ResultWaiter client_waiter;
|
||||
std::unique_lock<std::mutex> client_lock(client_waiter.mutex_);
|
||||
ASSERT_TRUE(client->Start(ip_addr_, client_waiter.ResultCallback, &client_waiter));
|
||||
client_waiter.cv_.wait(client_lock,
|
||||
[&]() { return client_waiter.is_valid_.has_value(); });
|
||||
if (good_client) {
|
||||
ASSERT_TRUE(*(client_waiter.is_valid_));
|
||||
ASSERT_EQ(strlen(reinterpret_cast<const char*>(client_waiter.peer_info_.data)),
|
||||
strlen(reinterpret_cast<const char*>(server_info_.data)));
|
||||
EXPECT_EQ(memcmp(client_waiter.peer_info_.data, server_info_.data,
|
||||
sizeof(server_info_.data)),
|
||||
0);
|
||||
} else {
|
||||
ASSERT_FALSE(*(client_waiter.is_valid_));
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> global_lock(global_clients_mutex);
|
||||
++num_clients_done;
|
||||
}
|
||||
global_cv_.notify_one();
|
||||
}).detach();
|
||||
}
|
||||
|
||||
global_cv_.wait(global_clients_lock, [&]() { return num_clients_done == test_num_clients; });
|
||||
server_waiter.cv_.wait(server_lock, [&]() { return server_waiter.is_valid_.has_value(); });
|
||||
ASSERT_TRUE(*(server_waiter.is_valid_));
|
||||
ASSERT_EQ(strlen(reinterpret_cast<const char*>(server_waiter.peer_info_.data)),
|
||||
strlen(reinterpret_cast<const char*>(client_info_.data)));
|
||||
EXPECT_EQ(memcmp(server_waiter.peer_info_.data, client_info_.data, sizeof(client_info_.data)),
|
||||
0);
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
Loading…
Reference in a new issue