19e801a2bSVernon Mauery #include "crypt_algo.hpp" 29e801a2bSVernon Mauery 39e801a2bSVernon Mauery #include "message_parsers.hpp" 49e801a2bSVernon Mauery 5d08b5235STom Joseph #include <openssl/evp.h> 6d08b5235STom Joseph #include <openssl/hmac.h> 7d08b5235STom Joseph #include <openssl/rand.h> 89e801a2bSVernon Mauery 9518ecceaSTom Joseph #include <algorithm> 10d08b5235STom Joseph #include <numeric> 11d08b5235STom Joseph 12d08b5235STom Joseph namespace cipher 13d08b5235STom Joseph { 14d08b5235STom Joseph 15d08b5235STom Joseph namespace crypt 16d08b5235STom Joseph { 17d08b5235STom Joseph 18518ecceaSTom Joseph constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1> 19518ecceaSTom Joseph AlgoAES128::confPadBytes; 20518ecceaSTom Joseph 219e801a2bSVernon Mauery std::vector<uint8_t> 229e801a2bSVernon Mauery AlgoAES128::decryptPayload(const std::vector<uint8_t>& packet, 23518ecceaSTom Joseph const size_t sessHeaderLen, 24518ecceaSTom Joseph const size_t payloadLen) const 25518ecceaSTom Joseph { 26*2b1edef0SZhikui Ren // verify packet size minimal: sessHeaderLen + payloadLen 27*2b1edef0SZhikui Ren // and payloadLen is more than AESCBC128ConfHeader 28*2b1edef0SZhikui Ren if (packet.size() < (sessHeaderLen + payloadLen) || 29*2b1edef0SZhikui Ren payloadLen < AESCBC128ConfHeader) 30*2b1edef0SZhikui Ren { 31*2b1edef0SZhikui Ren throw std::runtime_error("Invalid data length"); 32*2b1edef0SZhikui Ren } 33*2b1edef0SZhikui Ren 349e801a2bSVernon Mauery auto plainPayload = 359e801a2bSVernon Mauery decryptData(packet.data() + sessHeaderLen, 36518ecceaSTom Joseph packet.data() + sessHeaderLen + AESCBC128ConfHeader, 37518ecceaSTom Joseph payloadLen - AESCBC128ConfHeader); 38518ecceaSTom Joseph 39518ecceaSTom Joseph /* 40518ecceaSTom Joseph * The confidentiality pad length is the last byte in the payload, it would 41518ecceaSTom Joseph * tell the number of pad bytes in the payload. We added a condition, so 4262ec622eSGunnar Mills * that buffer overrun doesn't happen. 43518ecceaSTom Joseph */ 44518ecceaSTom Joseph size_t confPadLength = plainPayload.back(); 45518ecceaSTom Joseph auto padLength = std::min(plainPayload.size() - 1, confPadLength); 46518ecceaSTom Joseph 47518ecceaSTom Joseph auto plainPayloadLen = plainPayload.size() - padLength - 1; 48518ecceaSTom Joseph 49518ecceaSTom Joseph // Additional check if the confidentiality pad bytes are as expected 50518ecceaSTom Joseph if (!std::equal(plainPayload.begin() + plainPayloadLen, 51518ecceaSTom Joseph plainPayload.begin() + plainPayloadLen + padLength, 52518ecceaSTom Joseph confPadBytes.begin())) 53518ecceaSTom Joseph { 54518ecceaSTom Joseph throw std::runtime_error("Confidentiality pad bytes check failed"); 55518ecceaSTom Joseph } 56518ecceaSTom Joseph 57518ecceaSTom Joseph plainPayload.resize(plainPayloadLen); 58518ecceaSTom Joseph 59518ecceaSTom Joseph return plainPayload; 60518ecceaSTom Joseph } 61518ecceaSTom Joseph 629e801a2bSVernon Mauery std::vector<uint8_t> 639e801a2bSVernon Mauery AlgoAES128::encryptPayload(std::vector<uint8_t>& payload) const 64518ecceaSTom Joseph { 65518ecceaSTom Joseph auto payloadLen = payload.size(); 66518ecceaSTom Joseph 67518ecceaSTom Joseph /* 68518ecceaSTom Joseph * The following logic calculates the number of padding bytes to be added to 69518ecceaSTom Joseph * the payload data. This would ensure that the length is a multiple of the 70518ecceaSTom Joseph * block size of algorithm being used. For the AES algorithm, the block size 71518ecceaSTom Joseph * is 16 bytes. 72518ecceaSTom Joseph */ 73518ecceaSTom Joseph auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF); 74518ecceaSTom Joseph 75518ecceaSTom Joseph /* 76518ecceaSTom Joseph * The additional field is for the Confidentiality Pad Length field. For the 77518ecceaSTom Joseph * AES algorithm, this number will range from 0 to 15 bytes. This field is 78518ecceaSTom Joseph * mandatory. 79518ecceaSTom Joseph */ 80518ecceaSTom Joseph payload.resize(payloadLen + paddingLen + 1); 81518ecceaSTom Joseph 82518ecceaSTom Joseph /* 83518ecceaSTom Joseph * If no Confidentiality Pad bytes are required, the Confidentiality Pad 84518ecceaSTom Joseph * Length field is set to 00h. If present, the value of the first byte of 85518ecceaSTom Joseph * Confidentiality Pad shall be one (01h) and all subsequent bytes shall 86518ecceaSTom Joseph * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc). 87518ecceaSTom Joseph */ 88518ecceaSTom Joseph if (0 != paddingLen) 89518ecceaSTom Joseph { 90518ecceaSTom Joseph std::iota(payload.begin() + payloadLen, 919e801a2bSVernon Mauery payload.begin() + payloadLen + paddingLen, 1); 92518ecceaSTom Joseph } 93518ecceaSTom Joseph 94518ecceaSTom Joseph payload.back() = paddingLen; 95518ecceaSTom Joseph 96518ecceaSTom Joseph return encryptData(payload.data(), payload.size()); 97518ecceaSTom Joseph } 98518ecceaSTom Joseph 9970fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv, 100518ecceaSTom Joseph const uint8_t* input, 101518ecceaSTom Joseph const int inputLen) const 102518ecceaSTom Joseph { 103518ecceaSTom Joseph // Initializes Cipher context 104584fa887SAdriana Kobylak EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 105518ecceaSTom Joseph 1069e801a2bSVernon Mauery auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); }; 107f6c97e11STom Joseph 1089e801a2bSVernon Mauery std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(ctx, 1099e801a2bSVernon Mauery cleanupFunc); 110f6c97e11STom Joseph 111518ecceaSTom Joseph /* 112518ecceaSTom Joseph * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type 113518ecceaSTom Joseph * AES-CBC-128. ctx must be initialized before calling this function. K2 is 114518ecceaSTom Joseph * the symmetric key used and iv is the initialization vector used. 115518ecceaSTom Joseph */ 116f6c97e11STom Joseph if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 117f6c97e11STom Joseph iv)) 118518ecceaSTom Joseph { 119518ecceaSTom Joseph throw std::runtime_error("EVP_DecryptInit_ex failed for type " 120518ecceaSTom Joseph "AES-CBC-128"); 121518ecceaSTom Joseph } 122518ecceaSTom Joseph 123518ecceaSTom Joseph /* 124518ecceaSTom Joseph * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 125518ecceaSTom Joseph * parameter is zero then no padding is performed. This function always 126518ecceaSTom Joseph * returns 1. 127518ecceaSTom Joseph */ 128f6c97e11STom Joseph EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 129518ecceaSTom Joseph 13070fd29cfSVernon Mauery std::vector<uint8_t> output(inputLen + AESCBC128BlockSize); 131518ecceaSTom Joseph 132518ecceaSTom Joseph int outputLen = 0; 133518ecceaSTom Joseph 134518ecceaSTom Joseph /* 135518ecceaSTom Joseph * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any 136518ecceaSTom Joseph * more data and it will return an error if any data remains in a partial 137518ecceaSTom Joseph * block: that is if the total data length is not a multiple of the block 138518ecceaSTom Joseph * size. Since AES-CBC-128 encrypted payload format adds padding bytes and 139518ecceaSTom Joseph * ensures that payload is a multiple of block size, we are not making the 140518ecceaSTom Joseph * call to EVP_DecryptFinal_ex(). 141518ecceaSTom Joseph */ 142f6c97e11STom Joseph if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input, 143f6c97e11STom Joseph inputLen)) 144518ecceaSTom Joseph { 145518ecceaSTom Joseph throw std::runtime_error("EVP_DecryptUpdate failed"); 146518ecceaSTom Joseph } 147518ecceaSTom Joseph 148518ecceaSTom Joseph output.resize(outputLen); 149518ecceaSTom Joseph 150518ecceaSTom Joseph return output; 151518ecceaSTom Joseph } 152518ecceaSTom Joseph 15370fd29cfSVernon Mauery std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input, 15470fd29cfSVernon Mauery const int inputLen) const 155518ecceaSTom Joseph { 15670fd29cfSVernon Mauery std::vector<uint8_t> output(inputLen + AESCBC128BlockSize); 157518ecceaSTom Joseph 158518ecceaSTom Joseph // Generate the initialization vector 159518ecceaSTom Joseph if (!RAND_bytes(output.data(), AESCBC128ConfHeader)) 160518ecceaSTom Joseph { 161518ecceaSTom Joseph throw std::runtime_error("RAND_bytes failed"); 162518ecceaSTom Joseph } 163518ecceaSTom Joseph 164518ecceaSTom Joseph // Initializes Cipher context 165584fa887SAdriana Kobylak EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 166518ecceaSTom Joseph 1679e801a2bSVernon Mauery auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) { EVP_CIPHER_CTX_free(ctx); }; 168f6c97e11STom Joseph 1699e801a2bSVernon Mauery std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> ctxPtr(ctx, 1709e801a2bSVernon Mauery cleanupFunc); 171f6c97e11STom Joseph 172518ecceaSTom Joseph /* 173518ecceaSTom Joseph * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type 174518ecceaSTom Joseph * AES-CBC-128. ctx must be initialized before calling this function. K2 is 175518ecceaSTom Joseph * the symmetric key used and iv is the initialization vector used. 176518ecceaSTom Joseph */ 177f6c97e11STom Joseph if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 178518ecceaSTom Joseph output.data())) 179518ecceaSTom Joseph { 180518ecceaSTom Joseph throw std::runtime_error("EVP_EncryptInit_ex failed for type " 181518ecceaSTom Joseph "AES-CBC-128"); 182518ecceaSTom Joseph } 183518ecceaSTom Joseph 184518ecceaSTom Joseph /* 185518ecceaSTom Joseph * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 186518ecceaSTom Joseph * parameter is zero then no padding is performed. This function always 187518ecceaSTom Joseph * returns 1. 188518ecceaSTom Joseph */ 189f6c97e11STom Joseph EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 190518ecceaSTom Joseph 191518ecceaSTom Joseph int outputLen = 0; 192518ecceaSTom Joseph 193518ecceaSTom Joseph /* 194518ecceaSTom Joseph * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any 195518ecceaSTom Joseph * more data and it will return an error if any data remains in a partial 196518ecceaSTom Joseph * block: that is if the total data length is not a multiple of the block 197518ecceaSTom Joseph * size. Since we are adding padding bytes and ensures that payload is a 198518ecceaSTom Joseph * multiple of block size, we are not making the call to 199518ecceaSTom Joseph * EVP_DecryptFinal_ex() 200518ecceaSTom Joseph */ 2019e801a2bSVernon Mauery if (!EVP_EncryptUpdate(ctxPtr.get(), output.data() + AESCBC128ConfHeader, 2029e801a2bSVernon Mauery &outputLen, input, inputLen)) 203518ecceaSTom Joseph { 204518ecceaSTom Joseph throw std::runtime_error("EVP_EncryptUpdate failed for type " 205518ecceaSTom Joseph "AES-CBC-128"); 206518ecceaSTom Joseph } 207518ecceaSTom Joseph 208518ecceaSTom Joseph output.resize(AESCBC128ConfHeader + outputLen); 209518ecceaSTom Joseph 210518ecceaSTom Joseph return output; 211518ecceaSTom Joseph } 212518ecceaSTom Joseph 213d08b5235STom Joseph } // namespace crypt 214d08b5235STom Joseph 215d08b5235STom Joseph } // namespace cipher 216