blob: 9d9a8d2a841f8ad5690c0b73920f9668fad24f16 [file] [log] [blame]
// 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 "tink/aead/kms_envelope_aead.h"
#include <string>
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
#include "tink/aead.h"
#include "tink/registry.h"
#include "tink/util/errors.h"
#include "tink/util/status.h"
#include "tink/util/statusor.h"
#include "proto/tink.pb.h"
namespace crypto {
namespace tink {
namespace {
const int kEncryptedDekPrefixSize = 4;
const char* kEmptyAssociatedData = "";
void BigEndianStore32(uint8_t dst[4], uint32_t val) {
dst[0] = (val >> 24) & 0xff;
dst[1] = (val >> 16) & 0xff;
dst[2] = (val >> 8) & 0xff;
dst[3] = val & 0xff;
}
int32_t BigEndianLoad32(const uint8_t src[4]) {
return static_cast<int32_t>(src[3])
| (static_cast<int32_t>(src[2]) << 8)
| (static_cast<int32_t>(src[1]) << 16)
| (static_cast<int32_t>(src[0]) << 24);
}
// Constructs a ciphertext of KMS envelope encryption.
// The format of the ciphertext is the following:
// 4-byte-prefix | encrypted_dek | encrypted_plaintext
// where 4-byte-prefix is the length of encrypted_dek in big-endian format
// (for compatibility with Java)
std::string GetEnvelopeCiphertext(absl::string_view encrypted_dek,
absl::string_view encrypted_plaintext) {
uint8_t enc_dek_size[kEncryptedDekPrefixSize];
BigEndianStore32(enc_dek_size, encrypted_dek.size());
return absl::StrCat(
std::string(reinterpret_cast<const char*>(enc_dek_size),
kEncryptedDekPrefixSize),
encrypted_dek, encrypted_plaintext);
}
} // namespace
// static
util::StatusOr<std::unique_ptr<Aead>> KmsEnvelopeAead::New(
const google::crypto::tink::KeyTemplate& dek_template,
std::unique_ptr<Aead> remote_aead) {
if (remote_aead == nullptr) {
return util::Status(util::error::INVALID_ARGUMENT,
"remote_aead must be non-null");
}
auto km_result = Registry::get_key_manager<Aead>(dek_template.type_url());
if (!km_result.ok()) return km_result.status();
std::unique_ptr<Aead> envelope_aead(
new KmsEnvelopeAead(dek_template, std::move(remote_aead)));
return std::move(envelope_aead);
}
util::StatusOr<std::string> KmsEnvelopeAead::Encrypt(
absl::string_view plaintext, absl::string_view associated_data) const {
// Generate DEK.
auto dek_result = Registry::NewKeyData(dek_template_);
if (!dek_result.ok()) return dek_result.status();
auto dek = std::move(dek_result.ValueOrDie());
// Wrap DEK with remote.
auto dek_encrypt_result = remote_aead_->Encrypt(
dek->SerializeAsString(), kEmptyAssociatedData);
if (!dek_encrypt_result.ok()) return dek_encrypt_result.status();
// Encrypt plaintext using DEK.
auto aead_result = Registry::GetPrimitive<Aead>(*dek);
if (!aead_result.ok()) return aead_result.status();
auto aead = std::move(aead_result.ValueOrDie());
auto encrypt_result = aead->Encrypt(plaintext, associated_data);
if (!encrypt_result.ok()) return encrypt_result.status();
// Build and return ciphertext.
return GetEnvelopeCiphertext(dek_encrypt_result.ValueOrDie(),
encrypt_result.ValueOrDie());
}
util::StatusOr<std::string> KmsEnvelopeAead::Decrypt(
absl::string_view ciphertext, absl::string_view associated_data) const {
// Parse the ciphertext.
if (ciphertext.size() < kEncryptedDekPrefixSize) {
return util::Status(util::error::INVALID_ARGUMENT,
"ciphertext too short");
}
auto enc_dek_size = BigEndianLoad32(
reinterpret_cast<const uint8_t*>(ciphertext.data()));
if (enc_dek_size > ciphertext.size() - kEncryptedDekPrefixSize ||
enc_dek_size < 0) {
return util::Status(util::error::INVALID_ARGUMENT,
"invalid ciphertext");
}
// Decrypt the DEK with remote.
auto dek_decrypt_result = remote_aead_->Decrypt(
ciphertext.substr(kEncryptedDekPrefixSize, enc_dek_size),
kEmptyAssociatedData);
if (!dek_decrypt_result.ok()) {
return util::Status(
util::error::INVALID_ARGUMENT,
absl::StrCat("invalid ciphertext: ",
dek_decrypt_result.status().error_message()));
}
// Create AEAD from DEK.
google::crypto::tink::KeyData dek;
if (!dek.ParseFromString(dek_decrypt_result.ValueOrDie())) {
return util::Status(util::error::INVALID_ARGUMENT,
"invalid ciphertext");
}
// Encrypt plaintext using DEK.
auto aead_result = Registry::GetPrimitive<Aead>(dek);
if (!aead_result.ok()) return aead_result.status();
auto aead = std::move(aead_result.ValueOrDie());
return aead->Decrypt(
ciphertext.substr(kEncryptedDekPrefixSize + enc_dek_size),
associated_data);
}
} // namespace tink
} // namespace crypto