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

Commit aed3408f authored by Joshua Duong's avatar Joshua Duong
Browse files

Fix pairing aes_128_gcm key initialization.

Bug: 150719467

Test: atest adb_pairing_auth_test
Test: check 'adb pair' command on all three platforms work
Change-Id: Idfc64fe7bed2d09f4da9d2f7df70f9d6ae4e8fa3
parent 5eb30876
Loading
Loading
Loading
Loading
+37 −129
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@
#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>
@@ -28,155 +27,64 @@ 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));
// Size of AES-128-GCM key, in bytes
static constexpr size_t kHkdfKeyLength = 16;

}  // 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)),
    uint8_t key[kHkdfKeyLength];
    uint8_t info[] = "adb pairing_auth aes-128-gcm key";
    CHECK_EQ(HKDF(key, sizeof(key), EVP_sha256(), key_material, key_material_len, nullptr, 0, info,
                  sizeof(info) - 1),
             1);
    CHECK_EQ(AES_set_encrypt_key(key, sizeof(key), &aes_key_), 0);
    CHECK(EVP_AEAD_CTX_init(context_.get(), EVP_aead_aes_128_gcm(), key, sizeof(key),
                            EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr));
}

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;
std::optional<size_t> Aes128Gcm::Encrypt(const uint8_t* in, size_t in_len, uint8_t* out,
                                         size_t out_len) {
    std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(EVP_AEAD_CTX_aead(context_.get())), 0);
    memcpy(nonce.data(), &enc_sequence_, sizeof(enc_sequence_));
    size_t written_sz;
    if (!EVP_AEAD_CTX_seal(context_.get(), out, &written_sz, out_len, nonce.data(), nonce.size(),
                           in, in_len, nullptr, 0)) {
        LOG(ERROR) << "Failed to encrypt (in_len=" << in_len << ", out_len=" << out_len
                   << ", out_len_needed=" << EncryptedSize(in_len) << ")";
        return std::nullopt;
    }

    int cipherLen = 0;
    out += sizeof(header);
    status = EVP_EncryptUpdate(context_.get(), out, &cipherLen, in, in_len);
    if (status != 1 || cipherLen < 0) {
        return -1;
    ++enc_sequence_;
    return written_sz;
}

    // 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;
std::optional<size_t> Aes128Gcm::Decrypt(const uint8_t* in, size_t in_len, uint8_t* out,
                                         size_t out_len) {
    std::vector<uint8_t> nonce(EVP_AEAD_nonce_length(EVP_AEAD_CTX_aead(context_.get())), 0);
    memcpy(nonce.data(), &dec_sequence_, sizeof(dec_sequence_));
    size_t written_sz;
    if (!EVP_AEAD_CTX_open(context_.get(), out, &written_sz, out_len, nonce.data(), nonce.size(),
                           in, in_len, nullptr, 0)) {
        LOG(ERROR) << "Failed to decrypt (in_len=" << in_len << ", out_len=" << out_len
                   << ", out_len_needed=" << DecryptedSize(in_len) << ")";
        return std::nullopt;
    }

    // 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;
    ++dec_sequence_;
    return written_sz;
}

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);
    // https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_CTX_seal
    return size + EVP_AEAD_max_overhead(EVP_AEAD_CTX_aead(context_.get()));
}

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;
size_t Aes128Gcm::DecryptedSize(size_t size) {
    // https://commondatastorage.googleapis.com/chromium-boringssl-docs/aead.h.html#EVP_AEAD_CTX_open
    return size;
}

}  // namespace pairing
+11 −20
Original line number Diff line number Diff line
@@ -16,17 +16,12 @@

#pragma once

#include <openssl/aes.h>
#include <openssl/cipher.h>

#include <stdint.h>

#include "adb/pairing/counter.h"
#include <optional>
#include <vector>

// 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
#include <openssl/aead.h>

namespace adb {
namespace pairing {
@@ -42,7 +37,7 @@ class Aes128Gcm {
    // 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);
    std::optional<size_t> 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
@@ -50,22 +45,18 @@ class Aes128Gcm {
    // 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);
    std::optional<size_t> 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_;
    // Return a safe amount of buffer storage needed to decrypt |size| bytes.
    size_t DecryptedSize(size_t size);

  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_;
    bssl::ScopedEVP_AEAD_CTX context_;
    // Sequence numbers to use as nonces in the encryption scheme
    uint64_t dec_sequence_ = 0;
    uint64_t enc_sequence_ = 0;
};

}  // namespace pairing
+0 −49
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
+13 −13
Original line number Diff line number Diff line
@@ -75,8 +75,8 @@ struct PairingAuthCtx {
    // 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);
    // Returns a safe buffer size for decrypting a buffer of size |len|.
    size_t SafeDecryptedSize(size_t len);

  private:
    Data our_msg_;
@@ -167,12 +167,12 @@ PairingAuthCtx::Data PairingAuthCtx::Encrypt(const PairingAuthCtx::Data& data) {

    // 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) {
    auto out_size = cipher_->Encrypt(data.data(), data.size(), encrypted.data(), encrypted.size());
    if (!out_size.has_value() || *out_size == 0) {
        LOG(ERROR) << "Unable to encrypt data";
        return Data();
    }
    encrypted.resize(bytes);
    encrypted.resize(*out_size);

    return encrypted;
}
@@ -182,14 +182,14 @@ PairingAuthCtx::Data PairingAuthCtx::Decrypt(const PairingAuthCtx::Data& data) {
    CHECK(!data.empty());

    // Determine the size for the decrypted data based on the raw data.
    Data decrypted(cipher_->DecryptedSize(data.data(), data.size()));
    Data decrypted(cipher_->DecryptedSize(data.size()));
    size_t decrypted_size = decrypted.size();
    int bytes = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
    if (bytes <= 0) {
    auto out_size = cipher_->Decrypt(data.data(), data.size(), decrypted.data(), decrypted_size);
    if (!out_size.has_value() || *out_size == 0) {
        LOG(ERROR) << "Unable to decrypt data";
        return Data();
    }
    decrypted.resize(bytes);
    decrypted.resize(*out_size);

    return decrypted;
}
@@ -199,9 +199,9 @@ size_t PairingAuthCtx::SafeEncryptedSize(size_t len) {
    return cipher_->EncryptedSize(len);
}

size_t PairingAuthCtx::SafeDecryptedSize(const PairingAuthCtx::Data& buf) {
size_t PairingAuthCtx::SafeDecryptedSize(size_t len) {
    CHECK(cipher_);
    return cipher_->DecryptedSize(buf.data(), buf.size());
    return cipher_->DecryptedSize(len);
}

PairingAuthCtx* pairing_auth_server_new(const uint8_t* pswd, size_t len) {
@@ -271,8 +271,8 @@ size_t pairing_auth_safe_decrypted_size(PairingAuthCtx* ctx, const uint8_t* buf,
    CHECK(ctx);
    CHECK(buf);
    CHECK_GT(len, 0U);
    std::vector<uint8_t> p(buf, buf + len);
    return ctx->SafeDecryptedSize(p);
    // We no longer need buf for EVP_AEAD
    return ctx->SafeDecryptedSize(len);
}

bool pairing_auth_decrypt(PairingAuthCtx* ctx, const uint8_t* inbuf, size_t inlen, uint8_t* outbuf,
+0 −1
Original line number Diff line number Diff line
@@ -18,7 +18,6 @@ cc_test {
    name: "adb_pairing_auth_test",
    srcs: [
        "aes_128_gcm_test.cpp",
        "counter_test.cpp",
        "pairing_auth_test.cpp",
    ],

Loading