blob: b5ec16019be0dc2a46637151b57d6998469888ed [file] [log] [blame]
// Copyright 2021 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "oobe_config/rollback_openssl_encryption.h"
#include <optional>
#include <string>
#include <base/strings/strcat.h>
#include <brillo/secure_blob.h>
#include <openssl/rand.h>
#include <crypto/scoped_openssl_types.h>
#include <openssl/evp.h>
namespace oobe_config {
namespace {
constexpr int kIvSize = 12;
constexpr int kKeySize = 32;
constexpr int kTagSize = 16;
std::optional<brillo::SecureBlob> GenerateRandomKey() {
brillo::SecureBlob key(kKeySize);
if (!RAND_bytes(key.data(), kKeySize)) {
return std::nullopt;
}
return key;
}
// Generates a random initialization vector.
std::optional<brillo::Blob> GenerateRandomIV() {
brillo::Blob iv(kIvSize);
if (!RAND_bytes(iv.data(), kIvSize)) {
return std::nullopt;
}
return iv;
}
} // namespace
std::optional<EncryptedData> Encrypt(const brillo::SecureBlob& data) {
std::optional<brillo::SecureBlob> key = GenerateRandomKey();
std::optional<brillo::Blob> iv = GenerateRandomIV();
if (!iv.has_value() || !key.has_value()) {
return std::nullopt;
}
DCHECK_EQ(iv->size(), kIvSize);
DCHECK_EQ(key->size(), kKeySize);
crypto::ScopedEVP_CIPHER_CTX context(EVP_CIPHER_CTX_new());
if (!EVP_EncryptInit_ex(context.get(), EVP_aes_256_gcm(), nullptr,
key->data(), iv->data())) {
return std::nullopt;
}
brillo::Blob encrypted(data.size());
int encrypted_length = 0;
if (!EVP_EncryptUpdate(context.get(), encrypted.data(), &encrypted_length,
data.data(), data.size())) {
return std::nullopt;
}
DCHECK_EQ(encrypted_length, data.size());
if (!EVP_EncryptFinal_ex(context.get(), nullptr, &encrypted_length)) {
return std::nullopt;
}
DCHECK_EQ(encrypted_length, 0);
brillo::Blob tag(kTagSize);
if (!EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_GCM_GET_TAG, kTagSize,
tag.data())) {
return std::nullopt;
}
encrypted.insert(encrypted.end(), tag.begin(), tag.end());
encrypted.insert(encrypted.end(), iv->begin(), iv->end());
return {{encrypted, *key}};
}
std::optional<brillo::SecureBlob> Decrypt(const EncryptedData& encrypted_data) {
const brillo::Blob& input = encrypted_data.data;
CHECK_GE(input.size(), kTagSize + kIvSize);
CHECK_EQ(encrypted_data.key.size(), kKeySize);
brillo::Blob encrypted(input.begin(), input.end() - kTagSize - kIvSize);
brillo::Blob tag(input.begin() + encrypted.size(), input.end() - kIvSize);
brillo::Blob iv(input.begin() + encrypted.size() + kTagSize, input.end());
crypto::ScopedEVP_CIPHER_CTX context(EVP_CIPHER_CTX_new());
if (!EVP_DecryptInit_ex(context.get(), EVP_aes_256_gcm(), nullptr,
encrypted_data.key.data(), iv.data())) {
return std::nullopt;
}
brillo::SecureBlob output(encrypted.size());
int output_length;
if (!EVP_DecryptUpdate(context.get(), output.data(), &output_length,
encrypted.data(), encrypted.size())) {
return std::nullopt;
}
DCHECK_EQ(output_length, output.size());
if (!EVP_CIPHER_CTX_ctrl(context.get(), EVP_CTRL_GCM_SET_TAG, tag.size(),
tag.data())) {
return std::nullopt;
}
if (!EVP_DecryptFinal_ex(context.get(), nullptr, &output_length)) {
return std::nullopt;
}
DCHECK_EQ(output_length, 0);
return output;
}
} // namespace oobe_config