blob: c7a30e400809970c40bcab163423ca7e6c2f48e5 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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.
#ifndef IMPALA_UTIL_OPENSSL_UTIL_H
#define IMPALA_UTIL_OPENSSL_UTIL_H
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/sha.h>
#include <openssl/ssl.h>
#include "common/status.h"
namespace impala {
// From https://github.com/apache/kudu/commit/b88117415a02699c12a6eacbf065c4140ee0963c
//
// Hard code OpenSSL flag values from OpenSSL 1.0.1e[1][2] when compiling
// against OpenSSL 1.0.0 and below. We detect when running against a too-old
// version of OpenSSL using these definitions at runtime so that Kudu has full
// functionality when run against a new OpenSSL version, even if it's compiled
// against an older version.
//
// [1]: https://github.com/openssl/openssl/blob/OpenSSL_1_0_1e/ssl/ssl.h#L605-L609
// [2]: https://github.com/openssl/openssl/blob/OpenSSL_1_0_1e/ssl/tls1.h#L166-L172
#ifndef TLS1_1_VERSION
#define TLS1_1_VERSION 0x0302
#endif
#ifndef TLS1_2_VERSION
#define TLS1_2_VERSION 0x0303
#endif
/// Returns the maximum supported TLS version available in the linked OpenSSL library.
int MaxSupportedTlsVersion();
/// Returns true if, per the process configuration flags, server<->server communications
/// should use TLS.
bool IsInternalTlsConfigured();
/// Returns true if, per the process configuration flags, client<->server communications
/// should use TLS.
bool IsExternalTlsConfigured();
/// Add entropy from the system RNG to OpenSSL's global RNG. Called at system startup
/// and again periodically to add new entropy.
void SeedOpenSSLRNG();
enum AES_CIPHER_MODE {
AES_256_CFB,
AES_256_CTR,
AES_256_GCM
};
/// The hash of a data buffer used for checking integrity. A SHA256 hash is used
/// internally.
class IntegrityHash {
public:
/// Computes the hash of the data in a buffer and stores it in this object.
void Compute(const uint8_t* data, int64_t len);
/// Verify that the data in a buffer matches this hash. Returns true on match, false
/// otherwise.
bool Verify(const uint8_t* data, int64_t len) const WARN_UNUSED_RESULT;
private:
uint8_t hash_[SHA256_DIGEST_LENGTH];
};
/// Stores a random key that it can use to calculate and verify HMACs of data buffers for
/// authentication, eg. checking signatures of cookies. A SHA256 hash is used internally.
class AuthenticationHash {
public:
AuthenticationHash();
/// Computes the HMAC of 'data', which has length 'len', and stores it in 'out', which
/// must already be allocated with a length of HashLen() bytes. Returns an error if
/// computing the hash was unsuccessful.
Status Compute(const uint8_t* data, int64_t len, uint8_t* out) const WARN_UNUSED_RESULT;
/// Computes the HMAC of 'data', which has length 'len', and returns true if it matches
/// 'signature', which is expected to have length HashLen().
bool Verify(const uint8_t* data, int64_t len,
const uint8_t* signature) const WARN_UNUSED_RESULT;
/// Returns the length in bytes of the generated hashes. Currently we always use SHA256.
static int HashLen() { return SHA256_DIGEST_LENGTH; }
private:
/// An AES 256-bit key.
uint8_t key_[SHA256_DIGEST_LENGTH];
};
/// The key and initialization vector (IV) required to encrypt and decrypt a buffer of
/// data. This should be regenerated for each buffer of data.
///
/// We use AES with a 256-bit key and GCM/CTR/CFB cipher block mode, which gives us a
/// stream cipher that can support arbitrary-length ciphertexts. The mode is chosen
/// depends on the OpenSSL version & the hardware support at runtime. The IV is used as
/// an input to the cipher as the "block to supply before the first block of plaintext".
/// This is required because all ciphers (except the weak ECB) are built such that each
/// block depends on the output from the previous block. Since the first block doesn't
/// have a previous block, we supply this IV. Think of it as starting off the chain of
/// encryption.
///
/// Notes for GCM:
/// (1) GCM mode was supported since OpenSSL 1.0.1, however the tag verification
/// in decryption was only supported since OpenSSL 1.0.1d.
/// (2) The plaintext and the Additional Authenticated Data(AAD) are the two
/// categories of data that GCM protects. GCM protects the authenticity of the
/// plaintext and the AAD, and GCM also protects the confidentiality of the
/// plaintext. The AAD itself is not required or won't change the security.
/// In our case(Spill to Disk), we just ignore the AAD.
class EncryptionKey {
public:
EncryptionKey() : initialized_(false) { mode_ = GetSupportedDefaultMode(); }
/// Initializes a key for temporary use with randomly generated data, and clears the
/// tag for GCM mode. Reinitializes with new random values if the key was already
/// initialized. We use AES-GCM/AES-CTR/AES-CFB mode so key/IV pairs should not be
/// reused. This function automatically reseeds the RNG periodically, so callers do
/// not need to do it.
void InitializeRandom();
/// Encrypts a buffer of input data 'data' of length 'len' into an output buffer 'out'.
/// Exactly 'len' bytes will be written to 'out'. This key must be initialized before
/// calling. Operates in-place if 'in' == 'out', otherwise the buffers must not overlap.
/// For GCM mode, the hash tag will be kept inside(gcm_tag_ variable).
Status Encrypt(const uint8_t* data, int64_t len, uint8_t* out) WARN_UNUSED_RESULT;
/// Decrypts a buffer of input data 'data' of length 'len' that was encrypted with this
/// key into an output buffer 'out'. Exactly 'len' bytes will be written to 'out'.
/// This key must be initialized before calling. Operates in-place if 'in' == 'out',
/// otherwise the buffers must not overlap. For GCM mode, the hash tag, which is
/// computed during encryption, will be used for intgerity verification.
Status Decrypt(const uint8_t* data, int64_t len, uint8_t* out) WARN_UNUSED_RESULT;
/// Specify a cipher mode. Currently used only for testing but maybe in future we
/// can provide a configuration option for the end user who can choose a preferred
/// mode(GCM, CTR, CFB...) based on their software/hardware environment.
/// If not supported, fall back to the supported mode at runtime.
void SetCipherMode(AES_CIPHER_MODE m);
/// If is GCM mode at runtime
bool IsGcmMode() const { return mode_ == AES_256_GCM; }
/// Returns the a default mode which is supported at runtime. If GCM mode
/// is supported, return AES_256_GCM as the default. If GCM is not supported,
/// but CTR is still supported, return AES_256_CTR. When both GCM and
/// CTR modes are not supported, return AES_256_CFB.
static AES_CIPHER_MODE GetSupportedDefaultMode();
/// Converts mode type to string.
static const std::string ModeToString(AES_CIPHER_MODE m);
private:
/// Helper method that encrypts/decrypts if 'encrypt' is true/false respectively.
/// A buffer of input data 'data' of length 'len' is encrypted/decrypted with this
/// key into an output buffer 'out'. Exactly 'len' bytes will be written to 'out'.
/// This key must be initialized before calling. Operates in-place if 'in' == 'out',
/// otherwise the buffers must not overlap.
Status EncryptInternal(bool encrypt, const uint8_t* data, int64_t len,
uint8_t* out) WARN_UNUSED_RESULT;
/// Check if mode m is supported at runtime
static bool IsModeSupported(AES_CIPHER_MODE m);
/// Track whether this key has been initialized, to avoid accidentally using
/// uninitialized keys.
bool initialized_;
/// Returns a EVP_CIPHER according to cipher mode at runtime
const EVP_CIPHER* GetCipher() const;
/// An AES 256-bit key.
uint8_t key_[32];
/// An initialization vector to feed as the first block to AES.
uint8_t iv_[AES_BLOCK_SIZE];
/// Tag for GCM mode
uint8_t gcm_tag_[AES_BLOCK_SIZE];
/// Cipher Mode
AES_CIPHER_MODE mode_;
};
}
#endif