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