util/EncryptionUtils.h (85 lines of code) (raw):

/** * Copyright (c) 2014-present, Facebook, Inc. * All rights reserved. * * This source code is licensed under the BSD-style license found in the * LICENSE file in the root directory of this source tree. */ #pragma once #include <folly/Memory.h> #include <openssl/evp.h> #include <wdt/ErrorCodes.h> #include <string> namespace facebook { namespace wdt { /// AES encryption block size const int kAESBlockSize = 16; enum EncryptionType { ENC_NONE, ENC_AES128_CTR, ENC_AES128_GCM, NUM_ENC_TYPES }; /// @return string description for encryption type std::string encryptionTypeToStr(EncryptionType encryptionType); /// @return encryption type for the input string EncryptionType parseEncryptionType(const std::string& str); /// @returns 0 if no tag for the algorithm or the size in bytes /// for now only gcm produces/requires tag (hmac) of 128bits (16 bytes) size_t encryptionTypeToTagLen(EncryptionType type); /// class responsible for initializing openssl class WdtCryptoIntializer { public: WdtCryptoIntializer(); ~WdtCryptoIntializer(); }; class EncryptionParams { public: /** * Generates encryption params * * @param type type of encryption * * @return generated encryption params */ static EncryptionParams generateEncryptionParams(EncryptionType type); // Returns a string safe to print in logs (doesn't contain the secret but // a hash of it instead) std::string getLogSafeString() const; /** * Returns a string "type:encodedData" containing the secret using only * alphabetical characters */ std::string getUrlSafeString() const; /// Returns the type or "error: msg" upon error EncryptionType getType() const { return type_; } /// Returns the data/secret part or empty upon error const std::string& getSecret() const { return data_; } /// isSet() bool isSet() const { return type_ != ENC_NONE; } /// Empty constructor - use the default assignement operator to fill later EncryptionParams() : type_{ENC_NONE} { } /// Normal constructor when we have data EncryptionParams(EncryptionType type, const std::string& data); /// Tests equality bool operator==(const EncryptionParams& that) const; /// Erase (clears the object - call as soon as you don't need secret anymore) void erase(); /// Will also erase for safety against accidental memory recycling ~EncryptionParams(); /// Reconstructs an EncryptionParams object from a previous getUrlSafeString() static ErrorCode unserialize(const std::string& urlSafeStr, EncryptionParams& result); private: EncryptionType type_; std::string data_; std::string tag_; }; EVP_CIPHER_CTX* createAndInitCtx(); void cleanupAndDestroyCtx(EVP_CIPHER_CTX* ctx); using CipherCtxDeleter = folly::static_function_deleter<EVP_CIPHER_CTX, &cleanupAndDestroyCtx>; /// base class to share code between encyptor and decryptor class AESBase { public: int64_t getNumProcessed() const { return numProcessed_; } protected: /// evpCtx_ is copied into ctxOut /// @return whether the cloning was successful bool cloneCtx(EVP_CIPHER_CTX* ctxOut) const; /// @return cipher for a encryption type const EVP_CIPHER* getCipher(const EncryptionType encryptionType); EncryptionType type_{ENC_NONE}; std::unique_ptr<EVP_CIPHER_CTX, CipherCtxDeleter> evpCtx_; bool started_{false}; int64_t numProcessed_{0}; }; /// encryptor class class AESEncryptor : public AESBase { public: /** * should be called before starting to encrypt * * @param encryptionData encryption info * @param ivOut this is set to generated initialization vector * * @return whether start was successful */ bool start(const EncryptionParams& encryptionData, std::string& ivOut); /** * encrypts data. should be called after start * * @param in data ptr to encrypt * @param inLength input data length * @param out encrypted string is written here * * @return whether the string was successfully encrypted */ bool encrypt(const char* in, int inLength, char* out); /** * should be called after all the encryption is done. After this call, * encryptor object can be reused. tagOut is set to the generated tag * * @return whether the finish was successfully */ bool finish(std::string& tagOut); /// return current tag std::string computeCurrentTag(); /// destructor virtual ~AESEncryptor(); private: static bool finishInternal(EVP_CIPHER_CTX* ctx, EncryptionType type, std::string& tagOut); }; /// decryptor class class AESDecryptor : public AESBase { public: /** * should be called before starting to decrypt * * @param encryptionData encryption info * @param iv initialization vector * * @return whether start was successful */ bool start(const EncryptionParams& encryptionData, const std::string& iv); /** * decrypts data. should be called after start * * @param in data ptr to decrypt * @param inLength input data length * @param out decrypted string is written here * * @return whether the string was successfully decrypted */ bool decrypt(const char* in, int inLength, char* out); /** * should be called after all the decryption is done. After this call, * decryptor object can be reused. * * @return whether the finish was successfully */ bool finish(const std::string& tag); /// verify whether the given tag matches current tag bool verifyTag(const std::string& tag); /// destructor virtual ~AESDecryptor(); private: static bool finishInternal(EVP_CIPHER_CTX* ctx, EncryptionType type, const std::string& tag); }; } } // End of namespaces