1d08b5235STom Joseph #include <openssl/evp.h> 2d08b5235STom Joseph #include <openssl/hmac.h> 3d08b5235STom Joseph #include <openssl/rand.h> 4518ecceaSTom Joseph #include <algorithm> 5d08b5235STom Joseph #include <numeric> 6d08b5235STom Joseph #include "crypt_algo.hpp" 7d08b5235STom Joseph #include "message_parsers.hpp" 8d08b5235STom Joseph 9d08b5235STom Joseph namespace cipher 10d08b5235STom Joseph { 11d08b5235STom Joseph 12d08b5235STom Joseph namespace crypt 13d08b5235STom Joseph { 14d08b5235STom Joseph 15d08b5235STom Joseph Interface::Interface(const buffer& sik, const key& addKey) 16d08b5235STom Joseph { 17d08b5235STom Joseph unsigned int mdLen = 0; 18d08b5235STom Joseph 19d08b5235STom Joseph // Generated K2 for the confidentiality algorithm with the additional key 20d08b5235STom Joseph // keyed with SIK. 21d08b5235STom Joseph if (HMAC(EVP_sha1(), sik.data(), sik.size(), addKey.data(), 22d08b5235STom Joseph addKey.size(), k2.data(), &mdLen) == NULL) 23d08b5235STom Joseph { 24d08b5235STom Joseph throw std::runtime_error("Generating K2 for confidentiality algorithm" 25d08b5235STom Joseph "failed"); 26d08b5235STom Joseph } 27d08b5235STom Joseph } 28d08b5235STom Joseph 29518ecceaSTom Joseph constexpr key AlgoAES128::const2; 30518ecceaSTom Joseph 31518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1> 32518ecceaSTom Joseph AlgoAES128::confPadBytes; 33518ecceaSTom Joseph 34518ecceaSTom Joseph buffer AlgoAES128::decryptPayload(const buffer& packet, 35518ecceaSTom Joseph const size_t sessHeaderLen, 36518ecceaSTom Joseph const size_t payloadLen) const 37518ecceaSTom Joseph { 38518ecceaSTom Joseph auto plainPayload = decryptData( 39518ecceaSTom Joseph packet.data() + sessHeaderLen, 40518ecceaSTom Joseph packet.data() + sessHeaderLen + AESCBC128ConfHeader, 41518ecceaSTom Joseph payloadLen - AESCBC128ConfHeader); 42518ecceaSTom Joseph 43518ecceaSTom Joseph /* 44518ecceaSTom Joseph * The confidentiality pad length is the last byte in the payload, it would 45518ecceaSTom Joseph * tell the number of pad bytes in the payload. We added a condition, so 46518ecceaSTom Joseph * that buffer overrun does't happen. 47518ecceaSTom Joseph */ 48518ecceaSTom Joseph size_t confPadLength = plainPayload.back(); 49518ecceaSTom Joseph auto padLength = std::min(plainPayload.size() -1, confPadLength); 50518ecceaSTom Joseph 51518ecceaSTom Joseph auto plainPayloadLen = plainPayload.size() - padLength - 1; 52518ecceaSTom Joseph 53518ecceaSTom Joseph // Additional check if the confidentiality pad bytes are as expected 54518ecceaSTom Joseph if(!std::equal(plainPayload.begin() + plainPayloadLen, 55518ecceaSTom Joseph plainPayload.begin() + plainPayloadLen + padLength, 56518ecceaSTom Joseph confPadBytes.begin())) 57518ecceaSTom Joseph { 58518ecceaSTom Joseph throw std::runtime_error("Confidentiality pad bytes check failed"); 59518ecceaSTom Joseph } 60518ecceaSTom Joseph 61518ecceaSTom Joseph plainPayload.resize(plainPayloadLen); 62518ecceaSTom Joseph 63518ecceaSTom Joseph return plainPayload; 64518ecceaSTom Joseph } 65518ecceaSTom Joseph 66518ecceaSTom Joseph buffer AlgoAES128::encryptPayload(buffer& payload) const 67518ecceaSTom Joseph { 68518ecceaSTom Joseph auto payloadLen = payload.size(); 69518ecceaSTom Joseph 70518ecceaSTom Joseph /* 71518ecceaSTom Joseph * The following logic calculates the number of padding bytes to be added to 72518ecceaSTom Joseph * the payload data. This would ensure that the length is a multiple of the 73518ecceaSTom Joseph * block size of algorithm being used. For the AES algorithm, the block size 74518ecceaSTom Joseph * is 16 bytes. 75518ecceaSTom Joseph */ 76518ecceaSTom Joseph auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF); 77518ecceaSTom Joseph 78518ecceaSTom Joseph /* 79518ecceaSTom Joseph * The additional field is for the Confidentiality Pad Length field. For the 80518ecceaSTom Joseph * AES algorithm, this number will range from 0 to 15 bytes. This field is 81518ecceaSTom Joseph * mandatory. 82518ecceaSTom Joseph */ 83518ecceaSTom Joseph payload.resize(payloadLen + paddingLen + 1); 84518ecceaSTom Joseph 85518ecceaSTom Joseph /* 86518ecceaSTom Joseph * If no Confidentiality Pad bytes are required, the Confidentiality Pad 87518ecceaSTom Joseph * Length field is set to 00h. If present, the value of the first byte of 88518ecceaSTom Joseph * Confidentiality Pad shall be one (01h) and all subsequent bytes shall 89518ecceaSTom Joseph * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc). 90518ecceaSTom Joseph */ 91518ecceaSTom Joseph if (0 != paddingLen) 92518ecceaSTom Joseph { 93518ecceaSTom Joseph std::iota(payload.begin() + payloadLen, 94518ecceaSTom Joseph payload.begin() + payloadLen + paddingLen, 95518ecceaSTom Joseph 1); 96518ecceaSTom Joseph } 97518ecceaSTom Joseph 98518ecceaSTom Joseph payload.back() = paddingLen; 99518ecceaSTom Joseph 100518ecceaSTom Joseph return encryptData(payload.data(), payload.size()); 101518ecceaSTom Joseph } 102518ecceaSTom Joseph 103518ecceaSTom Joseph buffer AlgoAES128::decryptData(const uint8_t* iv, 104518ecceaSTom Joseph const uint8_t* input, 105518ecceaSTom Joseph const int inputLen) const 106518ecceaSTom Joseph { 107518ecceaSTom Joseph EVP_CIPHER_CTX ctx; 108518ecceaSTom Joseph 109518ecceaSTom Joseph // Initializes Cipher context 110518ecceaSTom Joseph EVP_CIPHER_CTX_init(&ctx); 111518ecceaSTom Joseph 112*f6c97e11STom Joseph auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) 113*f6c97e11STom Joseph { 114*f6c97e11STom Joseph EVP_CIPHER_CTX_cleanup(ctx); 115*f6c97e11STom Joseph }; 116*f6c97e11STom Joseph 117*f6c97e11STom Joseph std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> 118*f6c97e11STom Joseph ctxPtr(&ctx, cleanupFunc); 119*f6c97e11STom Joseph 120518ecceaSTom Joseph /* 121518ecceaSTom Joseph * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type 122518ecceaSTom Joseph * AES-CBC-128. ctx must be initialized before calling this function. K2 is 123518ecceaSTom Joseph * the symmetric key used and iv is the initialization vector used. 124518ecceaSTom Joseph */ 125*f6c97e11STom Joseph if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 126*f6c97e11STom Joseph iv)) 127518ecceaSTom Joseph { 128518ecceaSTom Joseph throw std::runtime_error("EVP_DecryptInit_ex failed for type " 129518ecceaSTom Joseph "AES-CBC-128"); 130518ecceaSTom Joseph } 131518ecceaSTom Joseph 132518ecceaSTom Joseph /* 133518ecceaSTom Joseph * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 134518ecceaSTom Joseph * parameter is zero then no padding is performed. This function always 135518ecceaSTom Joseph * returns 1. 136518ecceaSTom Joseph */ 137*f6c97e11STom Joseph EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 138518ecceaSTom Joseph 139518ecceaSTom Joseph buffer output(inputLen + AESCBC128BlockSize); 140518ecceaSTom Joseph 141518ecceaSTom Joseph int outputLen = 0; 142518ecceaSTom Joseph 143518ecceaSTom Joseph /* 144518ecceaSTom Joseph * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any 145518ecceaSTom Joseph * more data and it will return an error if any data remains in a partial 146518ecceaSTom Joseph * block: that is if the total data length is not a multiple of the block 147518ecceaSTom Joseph * size. Since AES-CBC-128 encrypted payload format adds padding bytes and 148518ecceaSTom Joseph * ensures that payload is a multiple of block size, we are not making the 149518ecceaSTom Joseph * call to EVP_DecryptFinal_ex(). 150518ecceaSTom Joseph */ 151*f6c97e11STom Joseph if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input, 152*f6c97e11STom Joseph inputLen)) 153518ecceaSTom Joseph { 154518ecceaSTom Joseph throw std::runtime_error("EVP_DecryptUpdate failed"); 155518ecceaSTom Joseph } 156518ecceaSTom Joseph 157518ecceaSTom Joseph output.resize(outputLen); 158518ecceaSTom Joseph 159518ecceaSTom Joseph return output; 160518ecceaSTom Joseph } 161518ecceaSTom Joseph 162518ecceaSTom Joseph buffer AlgoAES128::encryptData(const uint8_t* input, const int inputLen) const 163518ecceaSTom Joseph { 164518ecceaSTom Joseph buffer output(inputLen + AESCBC128BlockSize); 165518ecceaSTom Joseph 166518ecceaSTom Joseph // Generate the initialization vector 167518ecceaSTom Joseph if (!RAND_bytes(output.data(), AESCBC128ConfHeader)) 168518ecceaSTom Joseph { 169518ecceaSTom Joseph throw std::runtime_error("RAND_bytes failed"); 170518ecceaSTom Joseph } 171518ecceaSTom Joseph 172518ecceaSTom Joseph EVP_CIPHER_CTX ctx; 173518ecceaSTom Joseph 174518ecceaSTom Joseph // Initializes Cipher context 175518ecceaSTom Joseph EVP_CIPHER_CTX_init(&ctx); 176518ecceaSTom Joseph 177*f6c97e11STom Joseph auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) 178*f6c97e11STom Joseph { 179*f6c97e11STom Joseph EVP_CIPHER_CTX_cleanup(ctx); 180*f6c97e11STom Joseph }; 181*f6c97e11STom Joseph 182*f6c97e11STom Joseph std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> 183*f6c97e11STom Joseph ctxPtr(&ctx, cleanupFunc); 184*f6c97e11STom Joseph 185518ecceaSTom Joseph /* 186518ecceaSTom Joseph * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type 187518ecceaSTom Joseph * AES-CBC-128. ctx must be initialized before calling this function. K2 is 188518ecceaSTom Joseph * the symmetric key used and iv is the initialization vector used. 189518ecceaSTom Joseph */ 190*f6c97e11STom Joseph if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 191518ecceaSTom Joseph output.data())) 192518ecceaSTom Joseph { 193518ecceaSTom Joseph throw std::runtime_error("EVP_EncryptInit_ex failed for type " 194518ecceaSTom Joseph "AES-CBC-128"); 195518ecceaSTom Joseph } 196518ecceaSTom Joseph 197518ecceaSTom Joseph /* 198518ecceaSTom Joseph * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 199518ecceaSTom Joseph * parameter is zero then no padding is performed. This function always 200518ecceaSTom Joseph * returns 1. 201518ecceaSTom Joseph */ 202*f6c97e11STom Joseph EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 203518ecceaSTom Joseph 204518ecceaSTom Joseph int outputLen = 0; 205518ecceaSTom Joseph 206518ecceaSTom Joseph /* 207518ecceaSTom Joseph * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any 208518ecceaSTom Joseph * more data and it will return an error if any data remains in a partial 209518ecceaSTom Joseph * block: that is if the total data length is not a multiple of the block 210518ecceaSTom Joseph * size. Since we are adding padding bytes and ensures that payload is a 211518ecceaSTom Joseph * multiple of block size, we are not making the call to 212518ecceaSTom Joseph * EVP_DecryptFinal_ex() 213518ecceaSTom Joseph */ 214*f6c97e11STom Joseph if (!EVP_EncryptUpdate(ctxPtr.get(), 215518ecceaSTom Joseph output.data() + AESCBC128ConfHeader, 216518ecceaSTom Joseph &outputLen, 217518ecceaSTom Joseph input, inputLen)) 218518ecceaSTom Joseph { 219518ecceaSTom Joseph throw std::runtime_error("EVP_EncryptUpdate failed for type " 220518ecceaSTom Joseph "AES-CBC-128"); 221518ecceaSTom Joseph } 222518ecceaSTom Joseph 223518ecceaSTom Joseph output.resize(AESCBC128ConfHeader + outputLen); 224518ecceaSTom Joseph 225518ecceaSTom Joseph return output; 226518ecceaSTom Joseph } 227518ecceaSTom Joseph 228d08b5235STom Joseph }// namespace crypt 229d08b5235STom Joseph 230d08b5235STom Joseph }// namespace cipher 231d08b5235STom Joseph 232d08b5235STom Joseph 233