Merge "[adbwifi] Add pairing_auth library."
This commit is contained in:
commit
df8f1217d0
11 changed files with 1455 additions and 0 deletions
83
adb/pairing_auth/Android.bp
Normal file
83
adb/pairing_auth/Android.bp
Normal file
|
@ -0,0 +1,83 @@
|
|||
// 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_auth_defaults",
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Wthread-safety",
|
||||
"-Werror",
|
||||
],
|
||||
|
||||
compile_multilib: "both",
|
||||
|
||||
srcs: [
|
||||
"aes_128_gcm.cpp",
|
||||
"pairing_auth.cpp",
|
||||
],
|
||||
target: {
|
||||
android: {
|
||||
version_script: "libadb_pairing_auth.map.txt",
|
||||
},
|
||||
windows: {
|
||||
compile_multilib: "first",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
visibility: [
|
||||
"//art:__subpackages__",
|
||||
"//system/core/adb:__subpackages__",
|
||||
],
|
||||
|
||||
// libadb_pairing_auth doesn't need an embedded build number.
|
||||
use_version_lib: false,
|
||||
|
||||
host_supported: true,
|
||||
recovery_available: true,
|
||||
|
||||
stl: "libc++_static",
|
||||
|
||||
static_libs: ["libbase"],
|
||||
shared_libs: [
|
||||
"libcrypto",
|
||||
"liblog",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "libadb_pairing_auth",
|
||||
defaults: ["libadb_pairing_auth_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"com.android.adbd",
|
||||
],
|
||||
|
||||
stubs: {
|
||||
symbol_file: "libadb_pairing_auth.map.txt",
|
||||
versions: ["30"],
|
||||
},
|
||||
}
|
||||
|
||||
// For running atest (b/147158681)
|
||||
cc_library_static {
|
||||
name: "libadb_pairing_auth_static",
|
||||
defaults: ["libadb_pairing_auth_defaults"],
|
||||
|
||||
apex_available: [
|
||||
"//apex_available:platform",
|
||||
],
|
||||
}
|
183
adb/pairing_auth/aes_128_gcm.cpp
Normal file
183
adb/pairing_auth/aes_128_gcm.cpp
Normal file
|
@ -0,0 +1,183 @@
|
|||
/*
|
||||
* 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/aes_128_gcm.h"
|
||||
|
||||
#include <android-base/endian.h>
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <openssl/crypto.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/hkdf.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
namespace {
|
||||
static const size_t kHkdfKeyLength = 256;
|
||||
|
||||
struct Header {
|
||||
uint32_t payload;
|
||||
uint8_t iv[AES_128_GCM_IV_SIZE];
|
||||
uint8_t tag[AES_128_GCM_TAG_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
const EVP_CIPHER* Aes128Gcm::cipher_ = EVP_aes_128_gcm();
|
||||
|
||||
Aes128Gcm::Aes128Gcm(const uint8_t* key_material, size_t key_material_len) {
|
||||
CHECK(key_material);
|
||||
CHECK_NE(key_material_len, 0ul);
|
||||
context_.reset(EVP_CIPHER_CTX_new());
|
||||
CHECK(context_.get());
|
||||
|
||||
// Start with a random number for our counter
|
||||
CHECK_EQ(RAND_bytes(counter_.data(), counter_.size()), 1);
|
||||
|
||||
uint8_t key[kHkdfKeyLength] = {};
|
||||
uint8_t salt[64] = "this is the salt";
|
||||
uint8_t info[64] = "this is the info";
|
||||
CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, salt,
|
||||
sizeof(salt), info, sizeof(info)),
|
||||
1);
|
||||
CHECK_EQ(AES_set_encrypt_key(key, sizeof(key), &aes_key_), 0);
|
||||
}
|
||||
|
||||
int Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
|
||||
if (out_len < EncryptedSize(in_len)) {
|
||||
LOG(ERROR) << "out buffer size (sz=" << out_len
|
||||
<< ") not big enough (sz=" << EncryptedSize(in_len) << ")";
|
||||
return -1;
|
||||
}
|
||||
auto& header = *reinterpret_cast<Header*>(out);
|
||||
// Place the IV in the header
|
||||
memcpy(header.iv, counter_.data(), counter_.size());
|
||||
int status = EVP_EncryptInit_ex(context_.get(), cipher_, nullptr,
|
||||
reinterpret_cast<const uint8_t*>(&aes_key_), counter_.data());
|
||||
counter_.Increase();
|
||||
if (status != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int cipherLen = 0;
|
||||
out += sizeof(header);
|
||||
status = EVP_EncryptUpdate(context_.get(), out, &cipherLen, in, in_len);
|
||||
if (status != 1 || cipherLen < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Padding is enabled by default, so EVP_EncryptFinal_ex will pad any
|
||||
// remaining partial data up to the block size.
|
||||
int padding = 0;
|
||||
status = EVP_EncryptFinal_ex(context_.get(), out + cipherLen, &padding);
|
||||
if (status != 1 || padding < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Place the tag in the header
|
||||
status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_GET_TAG, sizeof(header.tag),
|
||||
header.tag);
|
||||
if (status != 1) {
|
||||
return -1;
|
||||
}
|
||||
// Place the payload size in the header
|
||||
uint32_t totalLen = sizeof(header) + cipherLen + padding;
|
||||
header.payload = htonl(static_cast<uint32_t>(cipherLen) + static_cast<uint32_t>(padding));
|
||||
return totalLen;
|
||||
}
|
||||
|
||||
int Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len) {
|
||||
if (in_len < sizeof(Header)) {
|
||||
return 0;
|
||||
}
|
||||
if (out_len < DecryptedSize(in, in_len)) {
|
||||
return 0;
|
||||
}
|
||||
const auto& header = *reinterpret_cast<const Header*>(in);
|
||||
uint32_t payload = ntohl(header.payload);
|
||||
uint32_t expected_inlen = sizeof(Header) + payload;
|
||||
if (in_len < expected_inlen) {
|
||||
// Not enough data available
|
||||
return 0;
|
||||
}
|
||||
// Initialized with expected IV from header
|
||||
int status = EVP_DecryptInit_ex(context_.get(), cipher_, nullptr,
|
||||
reinterpret_cast<const uint8_t*>(&aes_key_), header.iv);
|
||||
if (status != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
int decrypted_len = 0;
|
||||
status = EVP_DecryptUpdate(context_.get(), out, &decrypted_len, in + sizeof(header), payload);
|
||||
if (status != 1 || decrypted_len < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Set expected tag from header
|
||||
status = EVP_CIPHER_CTX_ctrl(context_.get(), EVP_CTRL_GCM_SET_TAG, sizeof(header.tag),
|
||||
const_cast<uint8_t*>(header.tag));
|
||||
if (status != 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// This is the padding. It can be ignored.
|
||||
int len = 0;
|
||||
status = EVP_DecryptFinal_ex(context_.get(), out + decrypted_len, &len);
|
||||
if (status != 1) {
|
||||
LOG(ERROR) << "EVP_DecryptFinal_ex failed. Tag mismatch";
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Return the length without the padding.
|
||||
return decrypted_len;
|
||||
}
|
||||
|
||||
size_t Aes128Gcm::EncryptedSize(size_t size) {
|
||||
// We need to account for block alignment of the encrypted data.
|
||||
// According to openssl.org/docs/man1.0.2/man3/EVP_EncryptUpdate.html,
|
||||
// "The amount of data written depends on the block alignment of the
|
||||
// encrypted data ..."
|
||||
// ".. the amount of data written may be anything from zero bytes to
|
||||
// (inl + cipher_block_size - 1) ..."
|
||||
const size_t cipher_block_size = EVP_CIPHER_block_size(cipher_);
|
||||
size_t padding = cipher_block_size - (size % cipher_block_size);
|
||||
if (padding != cipher_block_size) {
|
||||
size += padding;
|
||||
}
|
||||
return size + sizeof(Header);
|
||||
}
|
||||
|
||||
size_t Aes128Gcm::DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size) {
|
||||
if (encrypted_size < sizeof(Header)) {
|
||||
// Not enough data yet
|
||||
return 0;
|
||||
}
|
||||
auto header = reinterpret_cast<const Header*>(encrypted_data);
|
||||
uint32_t payload = ntohl(header->payload);
|
||||
size_t total_size = payload + sizeof(Header);
|
||||
if (encrypted_size < total_size) {
|
||||
// There's enough data for the header but not enough data for the
|
||||
// payload. Indicate that there's not enough data for now.
|
||||
return 0;
|
||||
}
|
||||
return payload;
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
72
adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
Normal file
72
adb/pairing_auth/include/adb/pairing/aes_128_gcm.h
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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 <openssl/aes.h>
|
||||
#include <openssl/cipher.h>
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "adb/pairing/counter.h"
|
||||
|
||||
// This is the default size of the initialization vector (iv) for AES-128-GCM
|
||||
#define AES_128_GCM_IV_SIZE 12
|
||||
// This is the full tag size for AES-128-GCM
|
||||
#define AES_128_GCM_TAG_SIZE 16
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
class Aes128Gcm {
|
||||
public:
|
||||
explicit Aes128Gcm(const uint8_t* key_material, size_t key_material_len);
|
||||
|
||||
// Encrypt a block of data in |in| of length |in_len|, this consumes all data
|
||||
// in |in| and places the encrypted data in |out| if |out_len| indicates that
|
||||
// there is enough space. The data contains information needed for
|
||||
// decryption that is specific to this implementation and is therefore only
|
||||
// suitable for decryption with this class.
|
||||
// The method returns the number of bytes placed in |out| on success and a
|
||||
// negative value if an error occurs.
|
||||
int Encrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
|
||||
// Decrypt a block of data in |in| of length |in_len|, this consumes all data
|
||||
// in |in_len| bytes of data. The decrypted output is placed in the |out|
|
||||
// buffer of length |out_len|. On successful decryption the number of bytes in
|
||||
// |out| will be placed in |out_len|.
|
||||
// The method returns the number of bytes consumed from the |in| buffer. If
|
||||
// there is not enough data available in |in| the method returns zero. If
|
||||
// an error occurs the method returns a negative value.
|
||||
int Decrypt(const uint8_t* in, size_t in_len, uint8_t* out, size_t out_len);
|
||||
|
||||
// Return a safe amount of buffer storage needed to encrypt |size| bytes.
|
||||
size_t EncryptedSize(size_t size);
|
||||
// Return a safe amount of buffer storage needed to decrypt the encrypted
|
||||
// data in |encrypted_data| which is of length |encrypted_size|. Returns 0 if
|
||||
// there is not enough data available to determine the required size.
|
||||
size_t DecryptedSize(const uint8_t* encrypted_data, size_t encrypted_size);
|
||||
|
||||
static const EVP_CIPHER* cipher_;
|
||||
|
||||
private:
|
||||
bssl::UniquePtr<EVP_CIPHER_CTX> context_;
|
||||
AES_KEY aes_key_;
|
||||
// We're going to use this counter for our iv so that it never repeats
|
||||
Counter<AES_128_GCM_IV_SIZE> counter_;
|
||||
};
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
49
adb/pairing_auth/include/adb/pairing/counter.h
Normal file
49
adb/pairing_auth/include/adb/pairing/counter.h
Normal file
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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>
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
template <size_t N>
|
||||
class Counter {
|
||||
public:
|
||||
void Increase() {
|
||||
for (size_t i = sizeof(counter_) - 1; i < sizeof(counter_); --i) {
|
||||
if (++counter_[i] != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* data() { return counter_; }
|
||||
const uint8_t* data() const { return counter_; }
|
||||
|
||||
constexpr size_t size() const { return sizeof(counter_); }
|
||||
|
||||
uint8_t& operator[](size_t index) { return counter_[index]; }
|
||||
const uint8_t& operator[](size_t index) const { return counter_[index]; }
|
||||
|
||||
private:
|
||||
uint8_t counter_[N];
|
||||
};
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
186
adb/pairing_auth/include/adb/pairing/pairing_auth.h
Normal file
186
adb/pairing_auth/include/adb/pairing/pairing_auth.h
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
* 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
|
||||
|
||||
__BEGIN_DECLS
|
||||
#if !defined(__ANDROID__) || __ANDROID_API__ >= 30
|
||||
|
||||
/**
|
||||
* PairingAuthCtx is a wrapper around the SPAKE2 protocol + cipher initialization
|
||||
* for encryption. On construction, the |password| will be used to generate a
|
||||
* SPAKE2 message. Each peer will exchange the messages in |pairing_auth_get_msg|
|
||||
* to initialize their ciphers in |pairing_auth_init_cipher|. If both peers used the
|
||||
* same |password|, then both sides will be able to decrypt each other's messages.
|
||||
*
|
||||
* On creation of a PairingAuthCtx, |pairing_auth_init_cipher| prior to using
|
||||
* the encrypt and decrypt APIs. Furthermore, you can only initialize the cipher
|
||||
* once.
|
||||
*
|
||||
* See pairing_auth_test.cpp for example usage.
|
||||
*
|
||||
*/
|
||||
struct PairingAuthCtx;
|
||||
typedef struct PairingAuthCtx PairingAuthCtx;
|
||||
|
||||
/**
|
||||
* Creates a new PairingAuthCtx instance as the server.
|
||||
*
|
||||
* @param pswd the shared secret the server and client use to authenticate each
|
||||
* other. Will abort if null.
|
||||
* @param len the length of the pswd in bytes. Will abort if 0.
|
||||
* @return a new PairingAuthCtx server instance. Caller is responsible for
|
||||
* destroying the context via #pairing_auth_destroy.
|
||||
*/
|
||||
PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Creates a new PairingAuthCtx instance as the client.
|
||||
*
|
||||
* @param pswd the shared secret the server and client use to authenticate each
|
||||
* other. Will abort if null.
|
||||
* @param len the length of the pswd in bytes. Will abort if 0.
|
||||
* @return a new PairingAuthCtx client instance. Caller is responsible for
|
||||
* destroying the context via #pairing_auth_destroy.
|
||||
*/
|
||||
PairingAuthCtx* pairing_auth_client_new(const uint8_t* pswd, size_t len) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Destroys the PairingAuthCtx.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance to destroy. Will abort if null.
|
||||
*/
|
||||
void pairing_auth_destroy(PairingAuthCtx* ctx) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Returns the exact size of the SPAKE2 msg.
|
||||
*
|
||||
* Use this size as the buffer size when retrieving the message via
|
||||
* #pairing_auth_get_msg.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @return the size of the SPAKE2 message in bytes. This is guaranteed to be > 0.
|
||||
*/
|
||||
size_t pairing_auth_msg_size(PairingAuthCtx* ctx) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Writes the SPAKE2 message to exchange with the other party to |out_buf|.
|
||||
*
|
||||
* This is guaranteed to write a valid message to |out_buf|. Use #pairing_auth_msg_size
|
||||
* to get the size the |out_buf| should be. The SPAKE2 messages will be used to
|
||||
* initialize the cipher for encryption/decryption (see #pairing_auth_init_cipher).
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @param out_buf the buffer the message is written to. The buffer is assumed to
|
||||
* be have at least #pairing_auth_msg_size size. Will abort if
|
||||
* out_buf is null.
|
||||
*/
|
||||
void pairing_auth_get_spake2_msg(PairingAuthCtx* ctx, uint8_t* out_buf) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Processes the peer's |their_msg| and attempts to initialize the cipher for
|
||||
* encryption.
|
||||
*
|
||||
* You can only call this method ONCE with a non-empty |msg|, regardless of success
|
||||
* or failure. On success, you can use the #pairing_auth_decrypt and #pairing_auth_encrypt
|
||||
* methods to exchange any further information securely. On failure, this
|
||||
* PairingAuthCtx instance has no more purpose and should be destroyed.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @param their_msg the peer's SPAKE2 msg. See #pairing_auth_get_msg. Will abort
|
||||
* if null.
|
||||
* @param msg_len the length of their_msg in bytes. Will abort if 0.
|
||||
* @return true iff the client and server used the same password when creating
|
||||
* the PairingAuthCtx. See
|
||||
* https: *commondatastorage.googleapis.com/chromium-boringssl-docs/curve25519.h.html#SPAKE2
|
||||
* for more details on the SPAKE2 protocol.
|
||||
*/
|
||||
bool pairing_auth_init_cipher(PairingAuthCtx* ctx, const uint8_t* their_msg, size_t msg_len)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Returns a safe buffer size for encrypting data of a certain size.
|
||||
*
|
||||
* IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
|
||||
* or #pairing_auth_init_cipher failed.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @param len the size of the message wanting to encrypt in bytes.
|
||||
* @return the minimum buffer size, in bytes, to hold an encrypted message of size len. See
|
||||
* #pairing_auth_encrypt for usage.
|
||||
*/
|
||||
size_t pairing_auth_safe_encrypted_size(PairingAuthCtx* ctx, size_t len) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Encrypts input data and writes the encrypted data into a user-provided buffer.
|
||||
*
|
||||
* IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
|
||||
* or #pairing_auth_init_cipher failed.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @param inbuf the buffer containing the data to encrypt. Will abort if null.
|
||||
* @param inlen the size of inbuf in bytes. Will abort if 0.
|
||||
* @param outbuf the buffer to write the encrypted data to. Will abort if null
|
||||
* @param outlen the size of outbuf in bytes. See #pairing_auth_safe_encrypted_size.
|
||||
* @return true if all the data was encrypted and written to outbuf, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool pairing_auth_encrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
|
||||
size_t* outlen) __INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Returns a safe buffer size for decrypting data of a certain size.
|
||||
*
|
||||
* IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
|
||||
* or #pairing_auth_init_cipher failed.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @param buf the buffer containing the encrypted data. Will abort if null.
|
||||
* @param len the size of the buf in bytes. Will abort if 0.
|
||||
* @return the minimum buffer size, in bytes, to hold a decrypted message of size len. See
|
||||
* #pairing_auth_decrypt for usage.
|
||||
*/
|
||||
size_t pairing_auth_safe_decrypted_size(PairingAuthCtx* ctx, const uint8_t* buf, size_t len)
|
||||
__INTRODUCED_IN(30);
|
||||
|
||||
/**
|
||||
* Decrypts input data and writes the decrypted data into a user-provided buffer.
|
||||
*
|
||||
* IMPORTANT: This will abort if either #pairing_auth_init_cipher was not called
|
||||
* or #pairing_auth_init_cipher failed.
|
||||
*
|
||||
* @param ctx the PairingAuthCtx instance. Will abort if null.
|
||||
* @param inbuf the buffer containing the data to decrypt. Will abort if null.
|
||||
* @param inlen the size of inbuf in bytes. WIll abort if 0.
|
||||
* @param outbuf the buffer to write the decrypted data to. Will abort if null.
|
||||
* @param outlen the size of outbuf in bytes. See #pairing_auth_safe_decrypted_size.
|
||||
* Will abort if 0.
|
||||
* @return true if all the data was decrypted and written to outbuf, false
|
||||
* otherwise.
|
||||
*/
|
||||
bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
|
||||
size_t* outlen) __INTRODUCED_IN(30);
|
||||
|
||||
#endif //!__ANDROID__ || __ANDROID_API__ >= 30
|
||||
__END_DECLS
|
15
adb/pairing_auth/libadb_pairing_auth.map.txt
Normal file
15
adb/pairing_auth/libadb_pairing_auth.map.txt
Normal file
|
@ -0,0 +1,15 @@
|
|||
LIBADB_PAIRING_AUTH {
|
||||
global:
|
||||
pairing_auth_msg_size; # apex introduced=30
|
||||
pairing_auth_get_spake2_msg; # apex introduced=30
|
||||
pairing_auth_init_cipher; # apex introduced=30
|
||||
pairing_auth_safe_encrypted_size; # apex introduced=30
|
||||
pairing_auth_encrypt; # apex introduced=30
|
||||
pairing_auth_safe_decrypted_size; # apex introduced=30
|
||||
pairing_auth_decrypt; # apex introduced=30
|
||||
pairing_auth_server_new; # apex introduced=30
|
||||
pairing_auth_client_new; # apex introduced=30
|
||||
pairing_auth_destroy; # apex introduced=30
|
||||
local:
|
||||
*;
|
||||
};
|
300
adb/pairing_auth/pairing_auth.cpp
Normal file
300
adb/pairing_auth/pairing_auth.cpp
Normal file
|
@ -0,0 +1,300 @@
|
|||
/*
|
||||
* 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_auth.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
|
||||
#include <openssl/curve25519.h>
|
||||
#include <openssl/mem.h>
|
||||
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
#include "adb/pairing/aes_128_gcm.h"
|
||||
|
||||
using namespace adb::pairing;
|
||||
|
||||
static constexpr spake2_role_t kClientRole = spake2_role_alice;
|
||||
static constexpr spake2_role_t kServerRole = spake2_role_bob;
|
||||
|
||||
static const uint8_t kClientName[] = "adb pair client";
|
||||
static const uint8_t kServerName[] = "adb pair server";
|
||||
|
||||
// This class is basically a wrapper around the SPAKE2 protocol + initializing a
|
||||
// cipher with the generated key material for encryption.
|
||||
struct PairingAuthCtx {
|
||||
public:
|
||||
using Data = std::vector<uint8_t>;
|
||||
enum class Role {
|
||||
Client,
|
||||
Server,
|
||||
};
|
||||
|
||||
explicit PairingAuthCtx(Role role, const Data& pswd);
|
||||
|
||||
// Returns the message to exchange with the other party. This is guaranteed
|
||||
// to have a non-empty message if creating this object with
|
||||
// |PairingAuthCtx::Create|, so you won't need to check.
|
||||
const Data& msg() const;
|
||||
|
||||
// Processes the peer's |msg| and attempts to initialize the cipher for
|
||||
// encryption. You can only call this method ONCE with a non-empty |msg|,
|
||||
// regardless of success or failure. Subsequent calls will always return
|
||||
// false. On success, you can use the |decrypt|
|
||||
// and |encrypt| methods to exchange any further information securely.
|
||||
//
|
||||
// Note: Once you call this with a non-empty key, the state is locked, which
|
||||
// means that you cannot try and register another key, regardless of the
|
||||
// return value. In order to register another key, you have to create a new
|
||||
// instance of PairingAuthCtx.
|
||||
bool InitCipher(const Data& their_msg);
|
||||
|
||||
// Encrypts |data| and returns the result. If encryption fails, the return
|
||||
// will be an empty vector.
|
||||
Data Encrypt(const Data& data);
|
||||
|
||||
// Decrypts |data| and returns the result. If decryption fails, the return
|
||||
// will be an empty vector.
|
||||
Data Decrypt(const Data& data);
|
||||
|
||||
// Returns a safe buffer size for encrypting a buffer of size |len|.
|
||||
size_t SafeEncryptedSize(size_t len);
|
||||
|
||||
// Returns a safe buffer size for decrypting a buffer |buf|.
|
||||
size_t SafeDecryptedSize(const Data& buf);
|
||||
|
||||
private:
|
||||
Data our_msg_;
|
||||
Role role_;
|
||||
bssl::UniquePtr<SPAKE2_CTX> spake2_ctx_;
|
||||
std::unique_ptr<Aes128Gcm> cipher_;
|
||||
}; // PairingAuthCtx
|
||||
|
||||
PairingAuthCtx::PairingAuthCtx(Role role, const Data& pswd) : role_(role) {
|
||||
CHECK(!pswd.empty());
|
||||
// Try to create the spake2 context and generate the public key.
|
||||
spake2_role_t spake_role;
|
||||
const uint8_t* my_name = nullptr;
|
||||
const uint8_t* their_name = nullptr;
|
||||
size_t my_len = 0;
|
||||
size_t their_len = 0;
|
||||
|
||||
// Create the SPAKE2 context
|
||||
switch (role_) {
|
||||
case Role::Client:
|
||||
spake_role = kClientRole;
|
||||
my_name = kClientName;
|
||||
my_len = sizeof(kClientName);
|
||||
their_name = kServerName;
|
||||
their_len = sizeof(kServerName);
|
||||
break;
|
||||
case Role::Server:
|
||||
spake_role = kServerRole;
|
||||
my_name = kServerName;
|
||||
my_len = sizeof(kServerName);
|
||||
their_name = kClientName;
|
||||
their_len = sizeof(kClientName);
|
||||
break;
|
||||
}
|
||||
spake2_ctx_.reset(SPAKE2_CTX_new(spake_role, my_name, my_len, their_name, their_len));
|
||||
if (spake2_ctx_ == nullptr) {
|
||||
LOG(ERROR) << "Unable to create a SPAKE2 context.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Generate the SPAKE2 public key
|
||||
size_t key_size = 0;
|
||||
uint8_t key[SPAKE2_MAX_MSG_SIZE];
|
||||
int status = SPAKE2_generate_msg(spake2_ctx_.get(), key, &key_size, SPAKE2_MAX_MSG_SIZE,
|
||||
pswd.data(), pswd.size());
|
||||
if (status != 1 || key_size == 0) {
|
||||
LOG(ERROR) << "Unable to generate the SPAKE2 public key.";
|
||||
return;
|
||||
}
|
||||
our_msg_.assign(key, key + key_size);
|
||||
}
|
||||
|
||||
const PairingAuthCtx::Data& PairingAuthCtx::msg() const {
|
||||
return our_msg_;
|
||||
}
|
||||
|
||||
bool PairingAuthCtx::InitCipher(const PairingAuthCtx::Data& their_msg) {
|
||||
// You can only register a key once.
|
||||
CHECK(!their_msg.empty());
|
||||
CHECK(!cipher_);
|
||||
|
||||
// Don't even try to process a message over the SPAKE2_MAX_MSG_SIZE
|
||||
if (their_msg.size() > SPAKE2_MAX_MSG_SIZE) {
|
||||
LOG(ERROR) << "their_msg size [" << their_msg.size() << "] greater then max size ["
|
||||
<< SPAKE2_MAX_MSG_SIZE << "].";
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t key_material_len = 0;
|
||||
uint8_t key_material[SPAKE2_MAX_KEY_SIZE];
|
||||
int status = SPAKE2_process_msg(spake2_ctx_.get(), key_material, &key_material_len,
|
||||
sizeof(key_material), their_msg.data(), their_msg.size());
|
||||
if (status != 1) {
|
||||
LOG(ERROR) << "Unable to process their public key";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Once SPAKE2_process_msg returns successfully, you can't do anything else
|
||||
// with the context, besides destroy it.
|
||||
cipher_.reset(new Aes128Gcm(key_material, key_material_len));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PairingAuthCtx::Data PairingAuthCtx::Encrypt(const PairingAuthCtx::Data& data) {
|
||||
CHECK(cipher_);
|
||||
CHECK(!data.empty());
|
||||
|
||||
// Determine the size for the encrypted data based on the raw data.
|
||||
Data encrypted(cipher_->EncryptedSize(data.size()));
|
||||
int bytes = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
|
||||
if (bytes < 0) {
|
||||
LOG(ERROR) << "Unable to encrypt data";
|
||||
return Data();
|
||||
}
|
||||
encrypted.resize(bytes);
|
||||
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
PairingAuthCtx::Data PairingAuthCtx::Decrypt(const PairingAuthCtx::Data& data) {
|
||||
CHECK(cipher_);
|
||||
CHECK(!data.empty());
|
||||
|
||||
// Determine the size for the decrypted data based on the raw data.
|
||||
Data decrypted(cipher_->DecryptedSize(data.data(), data.size()));
|
||||
size_t decrypted_size = decrypted.size();
|
||||
int bytes = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
|
||||
if (bytes <= 0) {
|
||||
LOG(ERROR) << "Unable to decrypt data";
|
||||
return Data();
|
||||
}
|
||||
decrypted.resize(bytes);
|
||||
|
||||
return decrypted;
|
||||
}
|
||||
|
||||
size_t PairingAuthCtx::SafeEncryptedSize(size_t len) {
|
||||
CHECK(cipher_);
|
||||
return cipher_->EncryptedSize(len);
|
||||
}
|
||||
|
||||
size_t PairingAuthCtx::SafeDecryptedSize(const PairingAuthCtx::Data& buf) {
|
||||
CHECK(cipher_);
|
||||
return cipher_->DecryptedSize(buf.data(), buf.size());
|
||||
}
|
||||
|
||||
PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) {
|
||||
CHECK(pswd);
|
||||
CHECK_GT(len, 0U);
|
||||
std::vector<uint8_t> p(pswd, pswd + len);
|
||||
auto* ret = new PairingAuthCtx(PairingAuthCtx::Role::Server, std::move(p));
|
||||
CHECK(!ret->msg().empty());
|
||||
return ret;
|
||||
}
|
||||
|
||||
PairingAuthCtx* pairing_auth_client_new(const uint8_t* pswd, size_t len) {
|
||||
CHECK(pswd);
|
||||
CHECK_GT(len, 0U);
|
||||
std::vector<uint8_t> p(pswd, pswd + len);
|
||||
auto* ret = new PairingAuthCtx(PairingAuthCtx::Role::Client, std::move(p));
|
||||
CHECK(!ret->msg().empty());
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t pairing_auth_msg_size(PairingAuthCtx* ctx) {
|
||||
CHECK(ctx);
|
||||
return ctx->msg().size();
|
||||
}
|
||||
|
||||
void pairing_auth_get_spake2_msg(PairingAuthCtx* ctx, uint8_t* out_buf) {
|
||||
CHECK(ctx);
|
||||
CHECK(out_buf);
|
||||
auto& msg = ctx->msg();
|
||||
memcpy(out_buf, msg.data(), msg.size());
|
||||
}
|
||||
|
||||
bool pairing_auth_init_cipher(PairingAuthCtx* ctx, const uint8_t* their_msg, size_t msg_len) {
|
||||
CHECK(ctx);
|
||||
CHECK(their_msg);
|
||||
CHECK_GT(msg_len, 0U);
|
||||
|
||||
std::vector<uint8_t> p(their_msg, their_msg + msg_len);
|
||||
return ctx->InitCipher(p);
|
||||
}
|
||||
|
||||
size_t pairing_auth_safe_encrypted_size(PairingAuthCtx* ctx, size_t len) {
|
||||
CHECK(ctx);
|
||||
return ctx->SafeEncryptedSize(len);
|
||||
}
|
||||
|
||||
bool pairing_auth_encrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
|
||||
size_t* outlen) {
|
||||
CHECK(ctx);
|
||||
CHECK(inbuf);
|
||||
CHECK(outbuf);
|
||||
CHECK(outlen);
|
||||
CHECK_GT(inlen, 0U);
|
||||
|
||||
std::vector<uint8_t> in(inbuf, inbuf + inlen);
|
||||
auto out = ctx->Encrypt(in);
|
||||
if (out.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(outbuf, out.data(), out.size());
|
||||
*outlen = out.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t pairing_auth_safe_decrypted_size(PairingAuthCtx* ctx, const uint8_t* buf, size_t len) {
|
||||
CHECK(ctx);
|
||||
CHECK(buf);
|
||||
CHECK_GT(len, 0U);
|
||||
std::vector<uint8_t> p(buf, buf + len);
|
||||
return ctx->SafeDecryptedSize(p);
|
||||
}
|
||||
|
||||
bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
|
||||
size_t* outlen) {
|
||||
CHECK(ctx);
|
||||
CHECK(inbuf);
|
||||
CHECK(outbuf);
|
||||
CHECK(outlen);
|
||||
CHECK_GT(inlen, 0U);
|
||||
|
||||
std::vector<uint8_t> in(inbuf, inbuf + inlen);
|
||||
auto out = ctx->Decrypt(in);
|
||||
if (out.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
memcpy(outbuf, out.data(), out.size());
|
||||
*outlen = out.size();
|
||||
return true;
|
||||
}
|
||||
|
||||
void pairing_auth_destroy(PairingAuthCtx* ctx) {
|
||||
CHECK(ctx);
|
||||
delete ctx;
|
||||
}
|
39
adb/pairing_auth/tests/Android.bp
Normal file
39
adb/pairing_auth/tests/Android.bp
Normal file
|
@ -0,0 +1,39 @@
|
|||
//
|
||||
// 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_auth_test",
|
||||
srcs: [
|
||||
"aes_128_gcm_test.cpp",
|
||||
"counter_test.cpp",
|
||||
"pairing_auth_test.cpp",
|
||||
],
|
||||
|
||||
compile_multilib: "first",
|
||||
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
],
|
||||
|
||||
// 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",
|
||||
],
|
||||
|
||||
test_suites: ["device-tests"],
|
||||
}
|
128
adb/pairing_auth/tests/aes_128_gcm_test.cpp
Normal file
128
adb/pairing_auth/tests/aes_128_gcm_test.cpp
Normal file
|
@ -0,0 +1,128 @@
|
|||
/*
|
||||
* 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 <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <adb/pairing/aes_128_gcm.h>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
TEST(Aes128GcmTest, init_null_material) {
|
||||
std::unique_ptr<Aes128Gcm> cipher;
|
||||
ASSERT_DEATH({ cipher.reset(new Aes128Gcm(nullptr, 42)); }, "");
|
||||
}
|
||||
|
||||
TEST(Aes128GcmTest, init_empty_material) {
|
||||
uint8_t material[64];
|
||||
std::unique_ptr<Aes128Gcm> cipher;
|
||||
ASSERT_DEATH({ cipher.reset(new Aes128Gcm(material, 0)); }, "");
|
||||
}
|
||||
|
||||
TEST(Aes128GcmTest, encrypt_decrypt) {
|
||||
const uint8_t msg[] = "alice and bob, sitting in a binary tree";
|
||||
uint8_t material[256];
|
||||
uint8_t encrypted[1024];
|
||||
uint8_t out_buf[1024];
|
||||
|
||||
RAND_bytes(material, sizeof(material));
|
||||
Aes128Gcm alice(material, sizeof(material));
|
||||
Aes128Gcm bob(material, sizeof(material));
|
||||
;
|
||||
|
||||
ASSERT_GE(alice.EncryptedSize(sizeof(msg)), sizeof(msg));
|
||||
int encrypted_size = alice.Encrypt(msg, sizeof(msg), encrypted, sizeof(encrypted));
|
||||
ASSERT_GT(encrypted_size, 0);
|
||||
size_t out_size = sizeof(out_buf);
|
||||
ASSERT_GE(bob.DecryptedSize(encrypted, sizeof(encrypted)), sizeof(msg));
|
||||
int decrypted_size = bob.Decrypt(encrypted, sizeof(encrypted), out_buf, out_size);
|
||||
ASSERT_EQ(sizeof(msg), decrypted_size);
|
||||
memset(out_buf + decrypted_size, 0, sizeof(out_buf) - decrypted_size);
|
||||
ASSERT_STREQ(reinterpret_cast<const char*>(msg), reinterpret_cast<const char*>(out_buf));
|
||||
}
|
||||
|
||||
TEST(Aes128GcmTest, padding) {
|
||||
// Test with block-align data as well as unaligned data.
|
||||
const size_t cipher_block_size = EVP_CIPHER_block_size(Aes128Gcm::cipher_);
|
||||
uint8_t material[256];
|
||||
RAND_bytes(material, sizeof(material));
|
||||
Aes128Gcm alice(material, sizeof(material));
|
||||
Aes128Gcm bob(material, sizeof(material));
|
||||
;
|
||||
std::vector<uint8_t> msg;
|
||||
std::vector<uint8_t> encrypted;
|
||||
std::vector<uint8_t> decrypted;
|
||||
|
||||
// Test with aligned data
|
||||
{
|
||||
msg.resize(cipher_block_size);
|
||||
RAND_bytes(msg.data(), msg.size());
|
||||
|
||||
// encrypt
|
||||
size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
|
||||
ASSERT_GE(safe_encrypted_sz, msg.size());
|
||||
encrypted.resize(safe_encrypted_sz);
|
||||
int encrypted_size =
|
||||
alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
|
||||
ASSERT_GT(encrypted_size, 0);
|
||||
ASSERT_LE(encrypted_size, safe_encrypted_sz);
|
||||
encrypted.resize(encrypted_size);
|
||||
|
||||
// decrypt
|
||||
size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
|
||||
ASSERT_GE(safe_decrypted_size, msg.size());
|
||||
decrypted.resize(safe_decrypted_size);
|
||||
int decrypted_size =
|
||||
bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
|
||||
ASSERT_GT(decrypted_size, 0);
|
||||
ASSERT_LE(decrypted_size, safe_decrypted_size);
|
||||
ASSERT_EQ(msg.size(), decrypted_size);
|
||||
ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
|
||||
}
|
||||
|
||||
// Test with unaligned data
|
||||
{
|
||||
msg.resize(cipher_block_size + 1);
|
||||
RAND_bytes(msg.data(), msg.size());
|
||||
|
||||
// encrypt
|
||||
size_t safe_encrypted_sz = alice.EncryptedSize(msg.size());
|
||||
ASSERT_GE(safe_encrypted_sz, msg.size());
|
||||
encrypted.resize(safe_encrypted_sz);
|
||||
int encrypted_size =
|
||||
alice.Encrypt(msg.data(), msg.size(), encrypted.data(), encrypted.size());
|
||||
ASSERT_GT(encrypted_size, 0);
|
||||
ASSERT_LE(encrypted_size, safe_encrypted_sz);
|
||||
encrypted.resize(encrypted_size);
|
||||
|
||||
// decrypt
|
||||
size_t safe_decrypted_size = bob.DecryptedSize(encrypted.data(), encrypted.size());
|
||||
ASSERT_GE(safe_decrypted_size, msg.size());
|
||||
decrypted.resize(safe_decrypted_size);
|
||||
int decrypted_size =
|
||||
bob.Decrypt(encrypted.data(), encrypted.size(), decrypted.data(), decrypted.size());
|
||||
ASSERT_GT(decrypted_size, 0);
|
||||
ASSERT_LE(decrypted_size, safe_decrypted_size);
|
||||
ASSERT_EQ(msg.size(), decrypted_size);
|
||||
ASSERT_EQ(memcmp(msg.data(), decrypted.data(), decrypted.size()), 0);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
70
adb/pairing_auth/tests/counter_test.cpp
Normal file
70
adb/pairing_auth/tests/counter_test.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
* 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 <gtest/gtest.h>
|
||||
|
||||
#include <adb/pairing/counter.h>
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
static constexpr size_t kTestCounterSize = 13;
|
||||
static const uint8_t kZeroes[64] = {0};
|
||||
|
||||
TEST(AdbCounterTest, size_match) {
|
||||
Counter<kTestCounterSize> counter;
|
||||
ASSERT_EQ(kTestCounterSize, counter.size());
|
||||
}
|
||||
|
||||
TEST(AdbCounterTest, Increase) {
|
||||
Counter<kTestCounterSize> counter;
|
||||
memset(counter.data(), 0, counter.size());
|
||||
counter.Increase();
|
||||
EXPECT_EQ(1, counter[counter.size() - 1]);
|
||||
EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 1));
|
||||
}
|
||||
|
||||
TEST(AdbCounterTest, rollover_first_byte) {
|
||||
Counter<kTestCounterSize> counter;
|
||||
memset(counter.data(), 0, counter.size());
|
||||
counter[counter.size() - 1] = 0xFF;
|
||||
counter.Increase();
|
||||
EXPECT_EQ(0, counter[counter.size() - 1]);
|
||||
EXPECT_EQ(1, counter[counter.size() - 2]);
|
||||
EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size() - 2));
|
||||
}
|
||||
|
||||
TEST(AdbCounterTest, multiple_rollover) {
|
||||
Counter<kTestCounterSize> counter;
|
||||
memset(counter.data(), 0xFF, counter.size());
|
||||
memset(counter.data(), 0, counter.size() - 3);
|
||||
counter.Increase();
|
||||
EXPECT_EQ(0, counter[counter.size() - 5]);
|
||||
EXPECT_EQ(1, counter[counter.size() - 4]);
|
||||
EXPECT_EQ(0, counter[counter.size() - 3]);
|
||||
EXPECT_EQ(0, counter[counter.size() - 2]);
|
||||
EXPECT_EQ(0, counter[counter.size() - 1]);
|
||||
}
|
||||
|
||||
TEST(AdbCounterTest, full_rollover) {
|
||||
Counter<kTestCounterSize> counter;
|
||||
memset(counter.data(), 0xFF, counter.size());
|
||||
counter.Increase();
|
||||
EXPECT_EQ(0, memcmp(counter.data(), kZeroes, counter.size()));
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
330
adb/pairing_auth/tests/pairing_auth_test.cpp
Normal file
330
adb/pairing_auth/tests/pairing_auth_test.cpp
Normal file
|
@ -0,0 +1,330 @@
|
|||
/*
|
||||
* 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 "AdbPairingAuthTest"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <adb/pairing/pairing_auth.h>
|
||||
#include <android-base/endian.h>
|
||||
|
||||
namespace adb {
|
||||
namespace pairing {
|
||||
|
||||
static void PairingAuthDeleter(PairingAuthCtx* p) {
|
||||
pairing_auth_destroy(p);
|
||||
}
|
||||
|
||||
class AdbPairingAuthTest : public testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() override {}
|
||||
|
||||
virtual void TearDown() override {}
|
||||
|
||||
using PairingAuthUniquePtr = std::unique_ptr<PairingAuthCtx, decltype(&PairingAuthDeleter)>;
|
||||
|
||||
PairingAuthUniquePtr makeClient(std::vector<uint8_t> pswd) {
|
||||
return PairingAuthUniquePtr(pairing_auth_client_new(pswd.data(), pswd.size()),
|
||||
PairingAuthDeleter);
|
||||
}
|
||||
|
||||
PairingAuthUniquePtr makeServer(std::vector<uint8_t> pswd) {
|
||||
return PairingAuthUniquePtr(pairing_auth_server_new(pswd.data(), pswd.size()),
|
||||
PairingAuthDeleter);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(AdbPairingAuthTest, EmptyPassword) {
|
||||
// Context creation should fail if password is empty
|
||||
PairingAuthUniquePtr client(nullptr, PairingAuthDeleter);
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
client = PairingAuthUniquePtr(pairing_auth_client_new(nullptr, 0),
|
||||
PairingAuthDeleter);
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
client = PairingAuthUniquePtr(pairing_auth_client_new(nullptr, 2),
|
||||
PairingAuthDeleter);
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
uint8_t p;
|
||||
client = PairingAuthUniquePtr(pairing_auth_client_new(&p, 0), PairingAuthDeleter);
|
||||
},
|
||||
"");
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingAuthTest, ValidPassword) {
|
||||
const char* kPswd = "password";
|
||||
std::vector<uint8_t> pswd(kPswd, kPswd + sizeof(kPswd));
|
||||
auto client = makeClient(pswd);
|
||||
auto server = makeServer(pswd);
|
||||
|
||||
ASSERT_NE(nullptr, client);
|
||||
ASSERT_NE(nullptr, server);
|
||||
|
||||
// msg should not be empty.
|
||||
{
|
||||
size_t msg_size = pairing_auth_msg_size(client.get());
|
||||
std::vector<uint8_t> buf(msg_size);
|
||||
ASSERT_GT(msg_size, 0);
|
||||
pairing_auth_get_spake2_msg(client.get(), buf.data());
|
||||
}
|
||||
{
|
||||
size_t msg_size = pairing_auth_msg_size(server.get());
|
||||
std::vector<uint8_t> buf(msg_size);
|
||||
ASSERT_GT(msg_size, 0);
|
||||
pairing_auth_get_spake2_msg(server.get(), buf.data());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingAuthTest, NoInitCipher) {
|
||||
// Register a non-empty password, but not the peer's msg.
|
||||
// You should not be able to encrypt/decrypt messages.
|
||||
const char* kPswd = "password";
|
||||
std::vector<uint8_t> pswd(kPswd, kPswd + sizeof(kPswd));
|
||||
std::vector<uint8_t> data{0x01, 0x02, 0x03};
|
||||
uint8_t outbuf[256];
|
||||
size_t outsize;
|
||||
|
||||
// All other functions should crash if cipher hasn't been initialized.
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = makeServer(pswd);
|
||||
pairing_auth_init_cipher(server.get(), nullptr, 0);
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = makeServer(pswd);
|
||||
pairing_auth_encrypt(server.get(), data.data(), data.size(), outbuf, &outsize);
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = makeServer(pswd);
|
||||
pairing_auth_decrypt(server.get(), data.data(), data.size(), outbuf, &outsize);
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = makeServer(pswd);
|
||||
pairing_auth_safe_decrypted_size(server.get(), data.data(), data.size());
|
||||
},
|
||||
"");
|
||||
ASSERT_DEATH(
|
||||
{
|
||||
auto server = makeServer(pswd);
|
||||
pairing_auth_safe_encrypted_size(server.get(), data.size());
|
||||
},
|
||||
"");
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingAuthTest, DifferentPasswords) {
|
||||
// Register different passwords and then exchange the msgs. The
|
||||
// encryption should succeed, but the decryption should fail, since the
|
||||
// ciphers have been initialized with different keys.
|
||||
auto client = makeClient({0x01, 0x02, 0x03});
|
||||
std::vector<uint8_t> client_msg(pairing_auth_msg_size(client.get()));
|
||||
ASSERT_FALSE(client_msg.empty());
|
||||
pairing_auth_get_spake2_msg(client.get(), client_msg.data());
|
||||
|
||||
auto server = makeServer({0x01, 0x02, 0x04});
|
||||
std::vector<uint8_t> server_msg(pairing_auth_msg_size(server.get()));
|
||||
ASSERT_FALSE(server_msg.empty());
|
||||
pairing_auth_get_spake2_msg(server.get(), server_msg.data());
|
||||
|
||||
EXPECT_TRUE(pairing_auth_init_cipher(client.get(), server_msg.data(), server_msg.size()));
|
||||
EXPECT_TRUE(pairing_auth_init_cipher(server.get(), client_msg.data(), client_msg.size()));
|
||||
|
||||
// We shouldn't be able to decrypt.
|
||||
std::vector<uint8_t> msg{0x2a, 0x2b, 0x2c};
|
||||
// Client encrypts, server can't decrypt
|
||||
size_t out_size;
|
||||
client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
client_msg.resize(out_size);
|
||||
|
||||
server_msg.resize(
|
||||
pairing_auth_safe_decrypted_size(server.get(), client_msg.data(), client_msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_FALSE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
|
||||
server_msg.data(), &out_size));
|
||||
|
||||
// Server encrypts, client can't decrypt
|
||||
server_msg.resize(pairing_auth_safe_encrypted_size(server.get(), msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(server.get(), msg.data(), msg.size(), server_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
server_msg.resize(out_size);
|
||||
|
||||
client_msg.resize(
|
||||
pairing_auth_safe_decrypted_size(client.get(), server_msg.data(), server_msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_FALSE(pairing_auth_decrypt(client.get(), server_msg.data(), server_msg.size(),
|
||||
client_msg.data(), &out_size));
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingAuthTest, SamePasswords) {
|
||||
// Register same password and then exchange the msgs. The
|
||||
// encryption and decryption should succeed and have the same, unencrypted
|
||||
// values.
|
||||
std::vector<uint8_t> pswd{0x4f, 0x5a, 0x01, 0x46};
|
||||
auto client = makeClient(pswd);
|
||||
std::vector<uint8_t> client_msg(pairing_auth_msg_size(client.get()));
|
||||
ASSERT_FALSE(client_msg.empty());
|
||||
pairing_auth_get_spake2_msg(client.get(), client_msg.data());
|
||||
|
||||
auto server = makeServer(pswd);
|
||||
std::vector<uint8_t> server_msg(pairing_auth_msg_size(server.get()));
|
||||
ASSERT_FALSE(server_msg.empty());
|
||||
pairing_auth_get_spake2_msg(server.get(), server_msg.data());
|
||||
|
||||
EXPECT_TRUE(pairing_auth_init_cipher(client.get(), server_msg.data(), server_msg.size()));
|
||||
EXPECT_TRUE(pairing_auth_init_cipher(server.get(), client_msg.data(), client_msg.size()));
|
||||
|
||||
// We should be able to decrypt.
|
||||
std::vector<uint8_t> msg{0x2a, 0x2b, 0x2c, 0xff, 0x45, 0x12, 0x33};
|
||||
// Client encrypts, server decrypts
|
||||
size_t out_size;
|
||||
client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
client_msg.resize(out_size);
|
||||
|
||||
server_msg.resize(
|
||||
pairing_auth_safe_decrypted_size(server.get(), client_msg.data(), client_msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
|
||||
server_msg.data(), &out_size));
|
||||
ASSERT_EQ(out_size, msg.size());
|
||||
EXPECT_EQ(memcmp(msg.data(), server_msg.data(), out_size), 0);
|
||||
|
||||
// Server encrypts, client decrypt
|
||||
server_msg.resize(pairing_auth_safe_encrypted_size(server.get(), msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(server.get(), msg.data(), msg.size(), server_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
server_msg.resize(out_size);
|
||||
|
||||
client_msg.resize(
|
||||
pairing_auth_safe_decrypted_size(client.get(), server_msg.data(), server_msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_decrypt(client.get(), server_msg.data(), server_msg.size(),
|
||||
client_msg.data(), &out_size));
|
||||
ASSERT_EQ(out_size, msg.size());
|
||||
EXPECT_EQ(memcmp(msg.data(), client_msg.data(), out_size), 0);
|
||||
}
|
||||
|
||||
TEST_F(AdbPairingAuthTest, CorruptedPayload) {
|
||||
// Do a matching password for both server/client, but let's fudge with the
|
||||
// header payload field. The decryption should fail.
|
||||
std::vector<uint8_t> pswd{0x4f, 0x5a, 0x01, 0x46};
|
||||
auto client = makeClient(pswd);
|
||||
std::vector<uint8_t> client_msg(pairing_auth_msg_size(client.get()));
|
||||
ASSERT_FALSE(client_msg.empty());
|
||||
pairing_auth_get_spake2_msg(client.get(), client_msg.data());
|
||||
|
||||
auto server = makeServer(pswd);
|
||||
std::vector<uint8_t> server_msg(pairing_auth_msg_size(server.get()));
|
||||
ASSERT_FALSE(server_msg.empty());
|
||||
pairing_auth_get_spake2_msg(server.get(), server_msg.data());
|
||||
|
||||
EXPECT_TRUE(pairing_auth_init_cipher(client.get(), server_msg.data(), server_msg.size()));
|
||||
EXPECT_TRUE(pairing_auth_init_cipher(server.get(), client_msg.data(), client_msg.size()));
|
||||
|
||||
std::vector<uint8_t> msg{0x2a, 0x2b, 0x2c, 0xff, 0x45, 0x12,
|
||||
0x33, 0x45, 0x12, 0xea, 0xf2, 0xdb};
|
||||
{
|
||||
// Client encrypts whole msg, server decrypts msg. Should be fine.
|
||||
size_t out_size;
|
||||
client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
client_msg.resize(out_size);
|
||||
|
||||
server_msg.resize(pairing_auth_safe_decrypted_size(server.get(), client_msg.data(),
|
||||
client_msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
|
||||
server_msg.data(), &out_size));
|
||||
ASSERT_EQ(out_size, msg.size());
|
||||
EXPECT_EQ(memcmp(msg.data(), server_msg.data(), out_size), 0);
|
||||
}
|
||||
{
|
||||
// 1) Client encrypts msg
|
||||
// 2) append some data to the encrypted msg
|
||||
// 3) change the payload field
|
||||
// 4) server tries to decrypt. It should fail.
|
||||
size_t out_size;
|
||||
client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
client_msg.resize(out_size);
|
||||
client_msg.push_back(0xaa);
|
||||
// This requires knowledge of the layout of the data. payload is the
|
||||
// first four bytes of the client_msg.
|
||||
uint32_t* payload = reinterpret_cast<uint32_t*>(client_msg.data());
|
||||
*payload = ntohl(*payload);
|
||||
*payload = htonl(*payload + 1);
|
||||
|
||||
server_msg.resize(pairing_auth_safe_decrypted_size(server.get(), client_msg.data(),
|
||||
client_msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_FALSE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
|
||||
server_msg.data(), &out_size));
|
||||
}
|
||||
{
|
||||
// 1) Client encrypts msg
|
||||
// 3) decrement the payload field
|
||||
// 4) server tries to decrypt. It should fail.
|
||||
size_t out_size;
|
||||
client_msg.resize(pairing_auth_safe_encrypted_size(client.get(), msg.size()));
|
||||
ASSERT_GT(client_msg.size(), 0);
|
||||
ASSERT_TRUE(pairing_auth_encrypt(client.get(), msg.data(), msg.size(), client_msg.data(),
|
||||
&out_size));
|
||||
ASSERT_GT(out_size, 0);
|
||||
client_msg.resize(out_size);
|
||||
// This requires knowledge of the layout of the data. payload is the
|
||||
// first four bytes of the client_msg.
|
||||
uint32_t* payload = reinterpret_cast<uint32_t*>(client_msg.data());
|
||||
*payload = ntohl(*payload);
|
||||
*payload = htonl(*payload - 1);
|
||||
|
||||
server_msg.resize(pairing_auth_safe_decrypted_size(server.get(), client_msg.data(),
|
||||
client_msg.size()));
|
||||
ASSERT_GT(server_msg.size(), 0);
|
||||
ASSERT_FALSE(pairing_auth_decrypt(server.get(), client_msg.data(), client_msg.size(),
|
||||
server_msg.data(), &out_size));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace pairing
|
||||
} // namespace adb
|
Loading…
Reference in a new issue