/** * Copyright (C) 2019-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #include "mongo/platform/basic.h" #include "mongo/crypto/aead_encryption.h" #include "mongo/base/data_view.h" #include "mongo/crypto/sha512_block.h" #include "mongo/crypto/symmetric_crypto.h" #include "mongo/db/matcher/schema/encrypt_schema_gen.h" #include "mongo/util/secure_compare_memory.h" namespace mongo { namespace crypto { namespace { constexpr size_t kIVSize = 16; // AssociatedData can be 2^24 bytes but since there needs to be room for the ciphertext in the // object, a value of 1<<16 was decided to cap the maximum size of AssociatedData. constexpr int kMaxAssociatedDataLength = 1 << 16; size_t aesCBCCipherOutputLength(size_t plainTextLen) { return aesBlockSize * (1 + plainTextLen / aesBlockSize); } std::pair aesCBCExpectedPlaintextLen(size_t cipherTextLength) { return {cipherTextLength - aesCBCIVSize - aesBlockSize, cipherTextLength - aesCBCIVSize}; } void aeadGenerateIV(const SymmetricKey* key, uint8_t* buffer, size_t bufferLen) { if (bufferLen < aesCBCIVSize) { fassert(51235, "IV buffer is too small for selected mode"); } auto status = engineRandBytes(buffer, aesCBCIVSize); if (!status.isOK()) { fassert(51236, status); } } Status _aesEncrypt(const SymmetricKey& key, const std::uint8_t* in, std::size_t inLen, std::uint8_t* out, std::size_t outLen, std::size_t* resultLen, bool ivProvided) try { if (!ivProvided) { aeadGenerateIV(&key, out, aesCBCIVSize); } auto encryptor = uassertStatusOK(SymmetricEncryptor::create(key, aesMode::cbc, out, aesCBCIVSize)); const size_t dataSize = outLen - aesCBCIVSize; uint8_t* data = out + aesCBCIVSize; const auto updateLen = uassertStatusOK(encryptor->update(in, inLen, data, dataSize)); const auto finalLen = uassertStatusOK(encryptor->finalize(data + updateLen, dataSize - updateLen)); const auto len = updateLen + finalLen; // Some cipher modes, such as GCM, will know in advance exactly how large their ciphertexts will // be. Others, like CBC, will have an upper bound. When this is true, we must allocate enough // memory to store the worst case. We must then set the actual size of the ciphertext so that // the buffer it has been written to may be serialized. invariant(len <= dataSize); *resultLen = aesCBCIVSize + len; // Check the returned length, including block size padding if (len != aesCBCCipherOutputLength(inLen)) { return {ErrorCodes::BadValue, str::stream() << "Encrypt error, expected cipher text of length " << aesCBCCipherOutputLength(inLen) << " but found " << len}; } return Status::OK(); } catch (const AssertionException& ex) { return ex.toStatus(); } Status _aesDecrypt(const SymmetricKey& key, ConstDataRange in, std::uint8_t* out, std::size_t outLen, std::size_t* resultLen) try { // Check the plaintext buffer can fit the product of decryption auto [lowerBound, upperBound] = aesCBCExpectedPlaintextLen(in.length()); if (upperBound > outLen) { return {ErrorCodes::BadValue, str::stream() << "Cleartext buffer of size " << outLen << " too small for output which can be as large as " << upperBound << "]"}; } const uint8_t* dataPtr = reinterpret_cast(in.data()); auto decryptor = uassertStatusOK(SymmetricDecryptor::create(key, aesMode::cbc, dataPtr, aesCBCIVSize)); const size_t dataSize = in.length() - aesCBCIVSize; const uint8_t* data = dataPtr + aesCBCIVSize; const auto updateLen = uassertStatusOK(decryptor->update(data, dataSize, out, outLen)); const auto finalLen = uassertStatusOK(decryptor->finalize(out + updateLen, outLen - updateLen)); *resultLen = updateLen + finalLen; invariant(*resultLen <= outLen); // Check the returned length, excluding headers block padding if (*resultLen < lowerBound || *resultLen > upperBound) { return {ErrorCodes::BadValue, str::stream() << "Decrypt error, expected clear text length in interval" << "[" << lowerBound << "," << upperBound << "]" << "but found " << *resultLen}; } /* Check that padding was removed. * * PKCS7 padding guarantees that the encrypted payload has * between 1 and blocksize bytes of padding which should be * removed during the decrypt process. * * If resultLen is the same as the payload len, * that means no padding was removed. * * macOS CommonCrypto will return such payloads when either the * key or ciphertext are corrupted and its unable to find any * expected padding. It fails open by returning whatever it can. */ if (*resultLen >= dataSize) { return {ErrorCodes::BadValue, "Decrypt error, plaintext is as large or larger than " "the ciphertext. This usually indicates an invalid key."}; } return Status::OK(); } catch (const AssertionException& ex) { return ex.toStatus(); } } // namespace size_t aeadCipherOutputLength(size_t plainTextLen) { // To calculate the size of the byte, we divide by the byte size and add 2 for padding // (1 for the attached IV, and 1 for the extra padding). The algorithm will add padding even // if the len is a multiple of the byte size, so if the len divides cleanly it will be // 32 bytes longer than the original, which is 16 bytes as padding and 16 bytes for the // IV. For things that don't divide cleanly, the cast takes care of floor dividing so it will // be 0 < x < 16 bytes added for padding and 16 bytes added for the IV. size_t aesOutLen = aesBlockSize * (plainTextLen / aesBlockSize + 2); return aesOutLen + kHmacOutSize; } Status aeadEncryptLocalKMS(const SymmetricKey& key, const ConstDataRange in, uint8_t* out, size_t outLen) { if (key.getKeySize() != kFieldLevelEncryptionKeySize) { return Status(ErrorCodes::BadValue, "AEAD encryption key is the incorrect length. " "Must be 96 bytes."); } // According to the rfc on AES encryption, the associatedDataLength is defined as the // number of bits in associatedData in BigEndian format. This is what the code segment // below describes. // RFC: (https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-01#section-2.1) std::array dataLenBitsEncodedStorage; DataRange dataLenBitsEncoded(dataLenBitsEncodedStorage); dataLenBitsEncoded.write>(static_cast(0)); ConstDataRange keyCDR(key.getKey(), kAeadAesHmacKeySize); return aeadEncryptWithIV(keyCDR, in.data(), in.length(), nullptr, 0, nullptr, 0, dataLenBitsEncoded, out, outLen); } Status aeadEncryptDataFrame(FLEEncryptionFrame& dataframe) { auto associatedData = dataframe.getAssociatedData(); if (associatedData.length() >= kMaxAssociatedDataLength) { return Status(ErrorCodes::BadValue, str::stream() << "AssociatedData for encryption is too large. Cannot be larger than " << kMaxAssociatedDataLength << " bytes."); } // According to the rfc on AES encryption, the associatedDataLength is defined as the // number of bits in associatedData in BigEndian format. This is what the code segment // below describes. // RFC: (https://tools.ietf.org/html/draft-mcgrew-aead-aes-cbc-hmac-sha2-01#section-2.1) std::array dataLenBitsEncodedStorage; DataRange dataLenBitsEncoded(dataLenBitsEncodedStorage); dataLenBitsEncoded.write>(static_cast(associatedData.length()) * 8); auto key = dataframe.getKey(); auto plaintext = dataframe.getPlaintext(); if (key->getKeySize() != kFieldLevelEncryptionKeySize) { return Status(ErrorCodes::BadValue, "Invalid key size."); } if (plaintext.data() == nullptr) { return Status(ErrorCodes::BadValue, "Invalid AEAD plaintext input."); } if (key->getAlgorithm() != aesAlgorithm) { return Status(ErrorCodes::BadValue, "Invalid algorithm for key."); } ConstDataRange iv(nullptr, 0); SHA512Block hmacOutput; if (dataframe.getFLEAlgorithmType() == FleAlgorithmInt::kDeterministic) { const uint8_t* ivKey = key->getKey() + kAeadAesHmacKeySize; hmacOutput = SHA512Block::computeHmac( ivKey, sym256KeySize, {associatedData, dataLenBitsEncoded, plaintext}); static_assert(SHA512Block::kHashLength >= kIVSize, "Invalid AEAD parameters. Generated IV too short."); iv = ConstDataRange(hmacOutput.data(), kIVSize); } ConstDataRange aeadKey(key->getKey(), kAeadAesHmacKeySize); return aeadEncryptWithIV(aeadKey, plaintext.data(), plaintext.length(), iv.data(), iv.length(), associatedData.data(), associatedData.length(), dataLenBitsEncoded, dataframe.getCiphertextMutable(), dataframe.getDataLength()); } Status aeadEncryptWithIV(ConstDataRange key, const uint8_t* in, const size_t inLen, const uint8_t* iv, const size_t ivLen, const uint8_t* associatedData, const uint64_t associatedDataLen, ConstDataRange dataLenBitsEncoded, uint8_t* out, size_t outLen) { if (key.length() != kAeadAesHmacKeySize) { return Status(ErrorCodes::BadValue, "Invalid key size."); } if (!(in && out)) { return Status(ErrorCodes::BadValue, "Invalid AEAD parameters."); } if (outLen != aeadCipherOutputLength(inLen)) { return Status(ErrorCodes::BadValue, "Invalid output buffer size."); } if (associatedDataLen >= kMaxAssociatedDataLength) { return Status(ErrorCodes::BadValue, str::stream() << "AssociatedData for encryption is too large. Cannot be larger than " << kMaxAssociatedDataLength << " bytes."); } const uint8_t* macKey = reinterpret_cast(key.data()); const uint8_t* encKey = reinterpret_cast(key.data() + sym256KeySize); size_t aesOutLen = outLen - kHmacOutSize; size_t cipherTextLen = 0; SymmetricKey symEncKey(encKey, sym256KeySize, aesAlgorithm, "aesKey", 1); bool ivProvided = false; if (ivLen != 0) { invariant(ivLen == 16); std::copy(iv, iv + ivLen, out); ivProvided = true; } auto sEncrypt = _aesEncrypt(symEncKey, in, inLen, out, aesOutLen, &cipherTextLen, ivProvided); if (!sEncrypt.isOK()) { return sEncrypt; } SHA512Block hmacOutput = SHA512Block::computeHmac(macKey, sym256KeySize, {ConstDataRange(associatedData, associatedDataLen), ConstDataRange(out, cipherTextLen), dataLenBitsEncoded}); std::copy(hmacOutput.data(), hmacOutput.data() + kHmacOutSize, out + cipherTextLen); return Status::OK(); } Status aeadDecrypt(const SymmetricKey& key, ConstDataRange ciphertext, const uint8_t* associatedData, const uint64_t associatedDataLen, uint8_t* out, size_t* outLen) { if (key.getKeySize() < kAeadAesHmacKeySize) { return Status(ErrorCodes::BadValue, "Invalid key size."); } if (!out) { return Status(ErrorCodes::BadValue, "Invalid AEAD parameters."); } if (ciphertext.length() < kHmacOutSize) { return Status(ErrorCodes::BadValue, "Ciphertext is not long enough."); } size_t expectedMaximumPlainTextSize = uassertStatusOK(aeadGetMaximumPlainTextLength(ciphertext.length())); if ((*outLen) != expectedMaximumPlainTextSize) { return Status(ErrorCodes::BadValue, "Output buffer must be as long as the cipherText."); } if (associatedDataLen >= kMaxAssociatedDataLength) { return Status(ErrorCodes::BadValue, str::stream() << "AssociatedData for encryption is too large. Cannot be larger than " << kMaxAssociatedDataLength << " bytes."); } const uint8_t* macKey = key.getKey(); const uint8_t* encKey = key.getKey() + sym256KeySize; size_t aesLen = ciphertext.length() - kHmacOutSize; // According to the rfc on AES encryption, the associatedDataLength is defined as the // number of bits in associatedData in BigEndian format. This is what the code segment // below describes. std::array dataLenBitsEncodedStorage; DataRange dataLenBitsEncoded(dataLenBitsEncodedStorage); dataLenBitsEncoded.write>(associatedDataLen * 8); SHA512Block hmacOutput = SHA512Block::computeHmac(macKey, sym256KeySize, {ConstDataRange(associatedData, associatedDataLen), ConstDataRange(ciphertext.data(), aesLen), dataLenBitsEncoded}); if (consttimeMemEqual(reinterpret_cast(hmacOutput.data()), ciphertext.data() + aesLen, kHmacOutSize) == false) { return Status(ErrorCodes::BadValue, "HMAC data authentication failed."); } SymmetricKey symEncKey(encKey, sym256KeySize, aesAlgorithm, key.getKeyId(), 1); auto sDecrypt = _aesDecrypt(symEncKey, ConstDataRange(ciphertext.data(), aesLen), out, aesLen, outLen); if (!sDecrypt.isOK()) { return sDecrypt; } return Status::OK(); } Status aeadDecryptDataFrame(FLEDecryptionFrame& dataframe) { auto ciphertext = dataframe.getCiphertext(); auto associatedData = dataframe.getAssociatedData(); auto& plaintext = dataframe.getPlaintextMutable(); size_t outLen = plaintext.size(); uassertStatusOK(aeadDecrypt(*dataframe.getKey(), ciphertext, associatedData.data(), associatedData.length(), plaintext.data(), &outLen)); plaintext.resize(outLen); return Status::OK(); } Status aeadDecryptLocalKMS(const SymmetricKey& key, const ConstDataRange cipher, uint8_t* out, size_t* outLen) { return aeadDecrypt(key, cipher, nullptr, 0, out, outLen); } } // namespace crypto } // namespace mongo