blob: 2978834d88580cc37a0024e9822fc704833ff688 [file] [log] [blame]
/*
* 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