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 15518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1> 16518ecceaSTom Joseph AlgoAES128::confPadBytes; 17518ecceaSTom Joseph 1870fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::decryptPayload( 1970fd29cfSVernon Mauery const std::vector<uint8_t>& packet, 20518ecceaSTom Joseph const size_t sessHeaderLen, 21518ecceaSTom Joseph const size_t payloadLen) const 22518ecceaSTom Joseph { 23518ecceaSTom Joseph auto plainPayload = decryptData( 24518ecceaSTom Joseph packet.data() + sessHeaderLen, 25518ecceaSTom Joseph packet.data() + sessHeaderLen + AESCBC128ConfHeader, 26518ecceaSTom Joseph payloadLen - AESCBC128ConfHeader); 27518ecceaSTom Joseph 28518ecceaSTom Joseph /* 29518ecceaSTom Joseph * The confidentiality pad length is the last byte in the payload, it would 30518ecceaSTom Joseph * tell the number of pad bytes in the payload. We added a condition, so 3162ec622eSGunnar Mills * that buffer overrun doesn't happen. 32518ecceaSTom Joseph */ 33518ecceaSTom Joseph size_t confPadLength = plainPayload.back(); 34518ecceaSTom Joseph auto padLength = std::min(plainPayload.size() -1, confPadLength); 35518ecceaSTom Joseph 36518ecceaSTom Joseph auto plainPayloadLen = plainPayload.size() - padLength - 1; 37518ecceaSTom Joseph 38518ecceaSTom Joseph // Additional check if the confidentiality pad bytes are as expected 39518ecceaSTom Joseph if(!std::equal(plainPayload.begin() + plainPayloadLen, 40518ecceaSTom Joseph plainPayload.begin() + plainPayloadLen + padLength, 41518ecceaSTom Joseph confPadBytes.begin())) 42518ecceaSTom Joseph { 43518ecceaSTom Joseph throw std::runtime_error("Confidentiality pad bytes check failed"); 44518ecceaSTom Joseph } 45518ecceaSTom Joseph 46518ecceaSTom Joseph plainPayload.resize(plainPayloadLen); 47518ecceaSTom Joseph 48518ecceaSTom Joseph return plainPayload; 49518ecceaSTom Joseph } 50518ecceaSTom Joseph 5170fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::encryptPayload( 5270fd29cfSVernon Mauery std::vector<uint8_t>& payload) const 53518ecceaSTom Joseph { 54518ecceaSTom Joseph auto payloadLen = payload.size(); 55518ecceaSTom Joseph 56518ecceaSTom Joseph /* 57518ecceaSTom Joseph * The following logic calculates the number of padding bytes to be added to 58518ecceaSTom Joseph * the payload data. This would ensure that the length is a multiple of the 59518ecceaSTom Joseph * block size of algorithm being used. For the AES algorithm, the block size 60518ecceaSTom Joseph * is 16 bytes. 61518ecceaSTom Joseph */ 62518ecceaSTom Joseph auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF); 63518ecceaSTom Joseph 64518ecceaSTom Joseph /* 65518ecceaSTom Joseph * The additional field is for the Confidentiality Pad Length field. For the 66518ecceaSTom Joseph * AES algorithm, this number will range from 0 to 15 bytes. This field is 67518ecceaSTom Joseph * mandatory. 68518ecceaSTom Joseph */ 69518ecceaSTom Joseph payload.resize(payloadLen + paddingLen + 1); 70518ecceaSTom Joseph 71518ecceaSTom Joseph /* 72518ecceaSTom Joseph * If no Confidentiality Pad bytes are required, the Confidentiality Pad 73518ecceaSTom Joseph * Length field is set to 00h. If present, the value of the first byte of 74518ecceaSTom Joseph * Confidentiality Pad shall be one (01h) and all subsequent bytes shall 75518ecceaSTom Joseph * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc). 76518ecceaSTom Joseph */ 77518ecceaSTom Joseph if (0 != paddingLen) 78518ecceaSTom Joseph { 79518ecceaSTom Joseph std::iota(payload.begin() + payloadLen, 80518ecceaSTom Joseph payload.begin() + payloadLen + paddingLen, 81518ecceaSTom Joseph 1); 82518ecceaSTom Joseph } 83518ecceaSTom Joseph 84518ecceaSTom Joseph payload.back() = paddingLen; 85518ecceaSTom Joseph 86518ecceaSTom Joseph return encryptData(payload.data(), payload.size()); 87518ecceaSTom Joseph } 88518ecceaSTom Joseph 8970fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv, 90518ecceaSTom Joseph const uint8_t* input, 91518ecceaSTom Joseph const int inputLen) const 92518ecceaSTom Joseph { 93518ecceaSTom Joseph // Initializes Cipher context 94*584fa887SAdriana Kobylak EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 95518ecceaSTom Joseph 96f6c97e11STom Joseph auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) 97f6c97e11STom Joseph { 98*584fa887SAdriana Kobylak EVP_CIPHER_CTX_free(ctx); 99f6c97e11STom Joseph }; 100f6c97e11STom Joseph 101f6c97e11STom Joseph std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> 102*584fa887SAdriana Kobylak ctxPtr(ctx, cleanupFunc); 103f6c97e11STom Joseph 104518ecceaSTom Joseph /* 105518ecceaSTom Joseph * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type 106518ecceaSTom Joseph * AES-CBC-128. ctx must be initialized before calling this function. K2 is 107518ecceaSTom Joseph * the symmetric key used and iv is the initialization vector used. 108518ecceaSTom Joseph */ 109f6c97e11STom Joseph if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 110f6c97e11STom Joseph iv)) 111518ecceaSTom Joseph { 112518ecceaSTom Joseph throw std::runtime_error("EVP_DecryptInit_ex failed for type " 113518ecceaSTom Joseph "AES-CBC-128"); 114518ecceaSTom Joseph } 115518ecceaSTom Joseph 116518ecceaSTom Joseph /* 117518ecceaSTom Joseph * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 118518ecceaSTom Joseph * parameter is zero then no padding is performed. This function always 119518ecceaSTom Joseph * returns 1. 120518ecceaSTom Joseph */ 121f6c97e11STom Joseph EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 122518ecceaSTom Joseph 12370fd29cfSVernon Mauery std::vector<uint8_t> output(inputLen + AESCBC128BlockSize); 124518ecceaSTom Joseph 125518ecceaSTom Joseph int outputLen = 0; 126518ecceaSTom Joseph 127518ecceaSTom Joseph /* 128518ecceaSTom Joseph * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any 129518ecceaSTom Joseph * more data and it will return an error if any data remains in a partial 130518ecceaSTom Joseph * block: that is if the total data length is not a multiple of the block 131518ecceaSTom Joseph * size. Since AES-CBC-128 encrypted payload format adds padding bytes and 132518ecceaSTom Joseph * ensures that payload is a multiple of block size, we are not making the 133518ecceaSTom Joseph * call to EVP_DecryptFinal_ex(). 134518ecceaSTom Joseph */ 135f6c97e11STom Joseph if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input, 136f6c97e11STom Joseph inputLen)) 137518ecceaSTom Joseph { 138518ecceaSTom Joseph throw std::runtime_error("EVP_DecryptUpdate failed"); 139518ecceaSTom Joseph } 140518ecceaSTom Joseph 141518ecceaSTom Joseph output.resize(outputLen); 142518ecceaSTom Joseph 143518ecceaSTom Joseph return output; 144518ecceaSTom Joseph } 145518ecceaSTom Joseph 14670fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input, 14770fd29cfSVernon Mauery const int inputLen) const 148518ecceaSTom Joseph { 14970fd29cfSVernon Mauery std::vector<uint8_t> output(inputLen + AESCBC128BlockSize); 150518ecceaSTom Joseph 151518ecceaSTom Joseph // Generate the initialization vector 152518ecceaSTom Joseph if (!RAND_bytes(output.data(), AESCBC128ConfHeader)) 153518ecceaSTom Joseph { 154518ecceaSTom Joseph throw std::runtime_error("RAND_bytes failed"); 155518ecceaSTom Joseph } 156518ecceaSTom Joseph 157518ecceaSTom Joseph // Initializes Cipher context 158*584fa887SAdriana Kobylak EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 159518ecceaSTom Joseph 160f6c97e11STom Joseph auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) 161f6c97e11STom Joseph { 162*584fa887SAdriana Kobylak EVP_CIPHER_CTX_free(ctx); 163f6c97e11STom Joseph }; 164f6c97e11STom Joseph 165f6c97e11STom Joseph std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> 166*584fa887SAdriana Kobylak ctxPtr(ctx, cleanupFunc); 167f6c97e11STom Joseph 168518ecceaSTom Joseph /* 169518ecceaSTom Joseph * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type 170518ecceaSTom Joseph * AES-CBC-128. ctx must be initialized before calling this function. K2 is 171518ecceaSTom Joseph * the symmetric key used and iv is the initialization vector used. 172518ecceaSTom Joseph */ 173f6c97e11STom Joseph if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 174518ecceaSTom Joseph output.data())) 175518ecceaSTom Joseph { 176518ecceaSTom Joseph throw std::runtime_error("EVP_EncryptInit_ex failed for type " 177518ecceaSTom Joseph "AES-CBC-128"); 178518ecceaSTom Joseph } 179518ecceaSTom Joseph 180518ecceaSTom Joseph /* 181518ecceaSTom Joseph * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 182518ecceaSTom Joseph * parameter is zero then no padding is performed. This function always 183518ecceaSTom Joseph * returns 1. 184518ecceaSTom Joseph */ 185f6c97e11STom Joseph EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 186518ecceaSTom Joseph 187518ecceaSTom Joseph int outputLen = 0; 188518ecceaSTom Joseph 189518ecceaSTom Joseph /* 190518ecceaSTom Joseph * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any 191518ecceaSTom Joseph * more data and it will return an error if any data remains in a partial 192518ecceaSTom Joseph * block: that is if the total data length is not a multiple of the block 193518ecceaSTom Joseph * size. Since we are adding padding bytes and ensures that payload is a 194518ecceaSTom Joseph * multiple of block size, we are not making the call to 195518ecceaSTom Joseph * EVP_DecryptFinal_ex() 196518ecceaSTom Joseph */ 197f6c97e11STom Joseph if (!EVP_EncryptUpdate(ctxPtr.get(), 198518ecceaSTom Joseph output.data() + AESCBC128ConfHeader, 199518ecceaSTom Joseph &outputLen, 200518ecceaSTom Joseph input, inputLen)) 201518ecceaSTom Joseph { 202518ecceaSTom Joseph throw std::runtime_error("EVP_EncryptUpdate failed for type " 203518ecceaSTom Joseph "AES-CBC-128"); 204518ecceaSTom Joseph } 205518ecceaSTom Joseph 206518ecceaSTom Joseph output.resize(AESCBC128ConfHeader + outputLen); 207518ecceaSTom Joseph 208518ecceaSTom Joseph return output; 209518ecceaSTom Joseph } 210518ecceaSTom Joseph 211d08b5235STom Joseph }// namespace crypt 212d08b5235STom Joseph 213d08b5235STom Joseph }// namespace cipher 214d08b5235STom Joseph 215d08b5235STom Joseph 216