+typedef std::unique_ptr<Cipher::EvpCipherWrapper<RawBuffer>> EvpCipherPtr;
+
+typedef std::function<void(EvpCipherPtr&, const RawBuffer& key, const RawBuffer& iv)> InitCipherFn;
+
+// aes mode, key length in bits, encryption
+typedef std::map<AlgoType, std::map<size_t, std::map<bool, InitCipherFn>>> CipherTree;
+
+template <typename T>
+void initCipher(EvpCipherPtr& ptr, const RawBuffer& key, const RawBuffer& iv)
+{
+ ptr.reset(new T(key, iv));
+}
+
+CipherTree initializeCipherTree()
+{
+ CipherTree tree;
+ tree[AlgoType::AES_CBC][128][true] = initCipher<Cipher::AesCbcEncryption128>;
+ tree[AlgoType::AES_CBC][192][true] = initCipher<Cipher::AesCbcEncryption192>;
+ tree[AlgoType::AES_CBC][256][true] = initCipher<Cipher::AesCbcEncryption256>;
+
+ tree[AlgoType::AES_CBC][128][false] = initCipher<Cipher::AesCbcDecryption128>;
+ tree[AlgoType::AES_CBC][192][false] = initCipher<Cipher::AesCbcDecryption192>;
+ tree[AlgoType::AES_CBC][256][false] = initCipher<Cipher::AesCbcDecryption256>;
+
+ tree[AlgoType::AES_GCM][128][true] = initCipher<Cipher::AesGcmEncryption128>;
+ tree[AlgoType::AES_GCM][192][true] = initCipher<Cipher::AesGcmEncryption192>;
+ tree[AlgoType::AES_GCM][256][true] = initCipher<Cipher::AesGcmEncryption256>;
+
+ tree[AlgoType::AES_GCM][128][false] = initCipher<Cipher::AesGcmDecryption128>;
+ tree[AlgoType::AES_GCM][192][false] = initCipher<Cipher::AesGcmDecryption192>;
+ tree[AlgoType::AES_GCM][256][false] = initCipher<Cipher::AesGcmDecryption256>;
+
+ return tree;
+}
+
+CipherTree g_cipherTree = initializeCipherTree();
+
+// key length in bytes
+InitCipherFn selectCipher(AlgoType type, size_t key_len = 32, bool encryption = true)
+{
+ try {
+ return g_cipherTree.at(type).at(key_len*8).at(encryption);
+ } catch (const std::out_of_range&) {
+ ThrowErr(Exc::Crypto::InternalError,
+ "Unsupported cipher: ",
+ static_cast<int>(type), ", ",
+ key_len, ", ",
+ encryption);
+ }
+}
+