1 #include <openssl/evp.h> 2 #include <openssl/hmac.h> 3 #include <openssl/rand.h> 4 #include <algorithm> 5 #include <numeric> 6 #include "crypt_algo.hpp" 7 #include "message_parsers.hpp" 8 9 namespace cipher 10 { 11 12 namespace crypt 13 { 14 15 constexpr std::array<uint8_t, AlgoAES128::AESCBC128BlockSize - 1> 16 AlgoAES128::confPadBytes; 17 18 std::vector<uint8_t> AlgoAES128::decryptPayload( 19 const std::vector<uint8_t>& packet, 20 const size_t sessHeaderLen, 21 const size_t payloadLen) const 22 { 23 auto plainPayload = decryptData( 24 packet.data() + sessHeaderLen, 25 packet.data() + sessHeaderLen + AESCBC128ConfHeader, 26 payloadLen - AESCBC128ConfHeader); 27 28 /* 29 * The confidentiality pad length is the last byte in the payload, it would 30 * tell the number of pad bytes in the payload. We added a condition, so 31 * that buffer overrun doesn't happen. 32 */ 33 size_t confPadLength = plainPayload.back(); 34 auto padLength = std::min(plainPayload.size() -1, confPadLength); 35 36 auto plainPayloadLen = plainPayload.size() - padLength - 1; 37 38 // Additional check if the confidentiality pad bytes are as expected 39 if(!std::equal(plainPayload.begin() + plainPayloadLen, 40 plainPayload.begin() + plainPayloadLen + padLength, 41 confPadBytes.begin())) 42 { 43 throw std::runtime_error("Confidentiality pad bytes check failed"); 44 } 45 46 plainPayload.resize(plainPayloadLen); 47 48 return plainPayload; 49 } 50 51 std::vector<uint8_t> AlgoAES128::encryptPayload( 52 std::vector<uint8_t>& payload) const 53 { 54 auto payloadLen = payload.size(); 55 56 /* 57 * The following logic calculates the number of padding bytes to be added to 58 * the payload data. This would ensure that the length is a multiple of the 59 * block size of algorithm being used. For the AES algorithm, the block size 60 * is 16 bytes. 61 */ 62 auto paddingLen = AESCBC128BlockSize - ((payloadLen + 1) & 0xF); 63 64 /* 65 * The additional field is for the Confidentiality Pad Length field. For the 66 * AES algorithm, this number will range from 0 to 15 bytes. This field is 67 * mandatory. 68 */ 69 payload.resize(payloadLen + paddingLen + 1); 70 71 /* 72 * If no Confidentiality Pad bytes are required, the Confidentiality Pad 73 * Length field is set to 00h. If present, the value of the first byte of 74 * Confidentiality Pad shall be one (01h) and all subsequent bytes shall 75 * have a monotonically increasing value (e.g., 02h, 03h, 04h, etc). 76 */ 77 if (0 != paddingLen) 78 { 79 std::iota(payload.begin() + payloadLen, 80 payload.begin() + payloadLen + paddingLen, 81 1); 82 } 83 84 payload.back() = paddingLen; 85 86 return encryptData(payload.data(), payload.size()); 87 } 88 89 std::vector<uint8_t> AlgoAES128::decryptData(const uint8_t* iv, 90 const uint8_t* input, 91 const int inputLen) const 92 { 93 // Initializes Cipher context 94 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 95 96 auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) 97 { 98 EVP_CIPHER_CTX_free(ctx); 99 }; 100 101 std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> 102 ctxPtr(ctx, cleanupFunc); 103 104 /* 105 * EVP_DecryptInit_ex sets up cipher context ctx for encryption with type 106 * AES-CBC-128. ctx must be initialized before calling this function. K2 is 107 * the symmetric key used and iv is the initialization vector used. 108 */ 109 if (!EVP_DecryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 110 iv)) 111 { 112 throw std::runtime_error("EVP_DecryptInit_ex failed for type " 113 "AES-CBC-128"); 114 } 115 116 /* 117 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 118 * parameter is zero then no padding is performed. This function always 119 * returns 1. 120 */ 121 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 122 123 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize); 124 125 int outputLen = 0; 126 127 /* 128 * If padding is disabled then EVP_DecryptFinal_ex() will not encrypt any 129 * more data and it will return an error if any data remains in a partial 130 * block: that is if the total data length is not a multiple of the block 131 * size. Since AES-CBC-128 encrypted payload format adds padding bytes and 132 * ensures that payload is a multiple of block size, we are not making the 133 * call to EVP_DecryptFinal_ex(). 134 */ 135 if (!EVP_DecryptUpdate(ctxPtr.get(), output.data(), &outputLen, input, 136 inputLen)) 137 { 138 throw std::runtime_error("EVP_DecryptUpdate failed"); 139 } 140 141 output.resize(outputLen); 142 143 return output; 144 } 145 146 std::vector<uint8_t> AlgoAES128::encryptData(const uint8_t* input, 147 const int inputLen) const 148 { 149 std::vector<uint8_t> output(inputLen + AESCBC128BlockSize); 150 151 // Generate the initialization vector 152 if (!RAND_bytes(output.data(), AESCBC128ConfHeader)) 153 { 154 throw std::runtime_error("RAND_bytes failed"); 155 } 156 157 // Initializes Cipher context 158 EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new(); 159 160 auto cleanupFunc = [](EVP_CIPHER_CTX* ctx) 161 { 162 EVP_CIPHER_CTX_free(ctx); 163 }; 164 165 std::unique_ptr<EVP_CIPHER_CTX, decltype(cleanupFunc)> 166 ctxPtr(ctx, cleanupFunc); 167 168 /* 169 * EVP_EncryptInit_ex sets up cipher context ctx for encryption with type 170 * AES-CBC-128. ctx must be initialized before calling this function. K2 is 171 * the symmetric key used and iv is the initialization vector used. 172 */ 173 if (!EVP_EncryptInit_ex(ctxPtr.get(), EVP_aes_128_cbc(), NULL, k2.data(), 174 output.data())) 175 { 176 throw std::runtime_error("EVP_EncryptInit_ex failed for type " 177 "AES-CBC-128"); 178 } 179 180 /* 181 * EVP_CIPHER_CTX_set_padding() enables or disables padding. If the pad 182 * parameter is zero then no padding is performed. This function always 183 * returns 1. 184 */ 185 EVP_CIPHER_CTX_set_padding(ctxPtr.get(), 0); 186 187 int outputLen = 0; 188 189 /* 190 * If padding is disabled then EVP_EncryptFinal_ex() will not encrypt any 191 * more data and it will return an error if any data remains in a partial 192 * block: that is if the total data length is not a multiple of the block 193 * size. Since we are adding padding bytes and ensures that payload is a 194 * multiple of block size, we are not making the call to 195 * EVP_DecryptFinal_ex() 196 */ 197 if (!EVP_EncryptUpdate(ctxPtr.get(), 198 output.data() + AESCBC128ConfHeader, 199 &outputLen, 200 input, inputLen)) 201 { 202 throw std::runtime_error("EVP_EncryptUpdate failed for type " 203 "AES-CBC-128"); 204 } 205 206 output.resize(AESCBC128ConfHeader + outputLen); 207 208 return output; 209 } 210 211 }// namespace crypt 212 213 }// namespace cipher 214 215 216