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