Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 340a5e86 authored by Joshua Duong's avatar Joshua Duong
Browse files

[adbwifi] Add pairing_auth library.

Bug: 111434128
Bug: 119494503

Test: atest adb_pairing_auth_test
Change-Id: Ieada7b8d9d8817292175623af55eac235b938c65
Exempt-From-Owner-Approval: approved already
parent d62c817c
Loading
Loading
Loading
Loading
+83 −0
Original line number Diff line number Diff line
// 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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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 −0
Original line number Diff line number Diff line
/*
 * 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
Loading