1 /* 2 * Written by Dr Stephen N Henson (steve (at) openssl.org) for the OpenSSL 3 * project. 4 */ 5 /* ==================================================================== 6 * Copyright (c) 2015 The OpenSSL Project. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in 17 * the documentation and/or other materials provided with the 18 * distribution. 19 * 20 * 3. All advertising materials mentioning features or use of this 21 * software must display the following acknowledgment: 22 * "This product includes software developed by the OpenSSL Project 23 * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" 24 * 25 * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to 26 * endorse or promote products derived from this software without 27 * prior written permission. For written permission, please contact 28 * licensing (at) OpenSSL.org. 29 * 30 * 5. Products derived from this software may not be called "OpenSSL" 31 * nor may "OpenSSL" appear in their names without prior written 32 * permission of the OpenSSL Project. 33 * 34 * 6. Redistributions of any form whatsoever must retain the following 35 * acknowledgment: 36 * "This product includes software developed by the OpenSSL Project 37 * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" 38 * 39 * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY 40 * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 41 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 42 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR 43 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 44 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 45 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 46 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 47 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 48 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 49 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 50 * OF THE POSSIBILITY OF SUCH DAMAGE. 51 * ==================================================================== 52 */ 53 54 #include <stdlib.h> 55 #include <string.h> 56 57 #include <string> 58 #include <vector> 59 60 #include <openssl/cipher.h> 61 #include <openssl/crypto.h> 62 #include <openssl/err.h> 63 64 #include "../test/file_test.h" 65 #include "../test/scoped_types.h" 66 67 68 static const EVP_CIPHER *GetCipher(const std::string &name) { 69 if (name == "DES-CBC") { 70 return EVP_des_cbc(); 71 } else if (name == "DES-ECB") { 72 return EVP_des_ecb(); 73 } else if (name == "DES-EDE") { 74 return EVP_des_ede(); 75 } else if (name == "DES-EDE-CBC") { 76 return EVP_des_ede_cbc(); 77 } else if (name == "DES-EDE3-CBC") { 78 return EVP_des_ede3_cbc(); 79 } else if (name == "RC4") { 80 return EVP_rc4(); 81 } else if (name == "AES-128-ECB") { 82 return EVP_aes_128_ecb(); 83 } else if (name == "AES-256-ECB") { 84 return EVP_aes_256_ecb(); 85 } else if (name == "AES-128-CBC") { 86 return EVP_aes_128_cbc(); 87 } else if (name == "AES-128-GCM") { 88 return EVP_aes_128_gcm(); 89 } else if (name == "AES-128-OFB") { 90 return EVP_aes_128_ofb(); 91 } else if (name == "AES-192-CBC") { 92 return EVP_aes_192_cbc(); 93 } else if (name == "AES-192-ECB") { 94 return EVP_aes_192_ecb(); 95 } else if (name == "AES-256-CBC") { 96 return EVP_aes_256_cbc(); 97 } else if (name == "AES-128-CTR") { 98 return EVP_aes_128_ctr(); 99 } else if (name == "AES-256-CTR") { 100 return EVP_aes_256_ctr(); 101 } else if (name == "AES-256-GCM") { 102 return EVP_aes_256_gcm(); 103 } else if (name == "AES-256-OFB") { 104 return EVP_aes_256_ofb(); 105 } 106 return nullptr; 107 } 108 109 static bool TestOperation(FileTest *t, 110 const EVP_CIPHER *cipher, 111 bool encrypt, 112 bool streaming, 113 const std::vector<uint8_t> &key, 114 const std::vector<uint8_t> &iv, 115 const std::vector<uint8_t> &plaintext, 116 const std::vector<uint8_t> &ciphertext, 117 const std::vector<uint8_t> &aad, 118 const std::vector<uint8_t> &tag) { 119 const std::vector<uint8_t> *in, *out; 120 if (encrypt) { 121 in = &plaintext; 122 out = &ciphertext; 123 } else { 124 in = &ciphertext; 125 out = &plaintext; 126 } 127 128 bool is_aead = EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE; 129 130 ScopedEVP_CIPHER_CTX ctx; 131 if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, nullptr, nullptr, 132 encrypt ? 1 : 0)) { 133 return false; 134 } 135 if (t->HasAttribute("IV")) { 136 if (is_aead) { 137 if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_IVLEN, 138 iv.size(), 0)) { 139 return false; 140 } 141 } else if (iv.size() != (size_t)EVP_CIPHER_CTX_iv_length(ctx.get())) { 142 t->PrintLine("Bad IV length."); 143 return false; 144 } 145 } 146 if (is_aead && !encrypt && 147 !EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_SET_TAG, tag.size(), 148 const_cast<uint8_t*>(tag.data()))) { 149 return false; 150 } 151 // The ciphers are run with no padding. For each of the ciphers we test, the 152 // output size matches the input size. 153 std::vector<uint8_t> result(in->size()); 154 if (in->size() != out->size()) { 155 t->PrintLine("Input/output size mismatch (%u vs %u).", (unsigned)in->size(), 156 (unsigned)out->size()); 157 return false; 158 } 159 // Note: the deprecated |EVP_CIPHER|-based AES-GCM API is sensitive to whether 160 // parameters are NULL, so it is important to skip the |in| and |aad| 161 // |EVP_CipherUpdate| calls when empty. 162 int unused, result_len1 = 0, result_len2; 163 if (!EVP_CIPHER_CTX_set_key_length(ctx.get(), key.size()) || 164 !EVP_CipherInit_ex(ctx.get(), nullptr, nullptr, key.data(), iv.data(), 165 -1) || 166 (!aad.empty() && 167 !EVP_CipherUpdate(ctx.get(), nullptr, &unused, aad.data(), 168 aad.size())) || 169 !EVP_CIPHER_CTX_set_padding(ctx.get(), 0)) { 170 t->PrintLine("Operation failed."); 171 return false; 172 } 173 if (streaming) { 174 for (size_t i = 0; i < in->size(); i++) { 175 uint8_t c = (*in)[i]; 176 int len; 177 if (!EVP_CipherUpdate(ctx.get(), result.data() + result_len1, &len, &c, 178 1)) { 179 t->PrintLine("Operation failed."); 180 return false; 181 } 182 result_len1 += len; 183 } 184 } else if (!in->empty() && 185 !EVP_CipherUpdate(ctx.get(), result.data(), &result_len1, 186 in->data(), in->size())) { 187 t->PrintLine("Operation failed."); 188 return false; 189 } 190 if (!EVP_CipherFinal_ex(ctx.get(), result.data() + result_len1, 191 &result_len2)) { 192 t->PrintLine("Operation failed."); 193 return false; 194 } 195 result.resize(result_len1 + result_len2); 196 if (!t->ExpectBytesEqual(out->data(), out->size(), result.data(), 197 result.size())) { 198 return false; 199 } 200 if (encrypt && is_aead) { 201 uint8_t rtag[16]; 202 if (tag.size() > sizeof(rtag)) { 203 t->PrintLine("Bad tag length."); 204 return false; 205 } 206 if (!EVP_CIPHER_CTX_ctrl(ctx.get(), EVP_CTRL_GCM_GET_TAG, tag.size(), 207 rtag) || 208 !t->ExpectBytesEqual(tag.data(), tag.size(), rtag, 209 tag.size())) { 210 return false; 211 } 212 } 213 return true; 214 } 215 216 static bool TestCipher(FileTest *t, void *arg) { 217 std::string cipher_str; 218 if (!t->GetAttribute(&cipher_str, "Cipher")) { 219 return false; 220 } 221 const EVP_CIPHER *cipher = GetCipher(cipher_str); 222 if (cipher == nullptr) { 223 t->PrintLine("Unknown cipher: '%s'.", cipher_str.c_str()); 224 return false; 225 } 226 227 std::vector<uint8_t> key, iv, plaintext, ciphertext, aad, tag; 228 if (!t->GetBytes(&key, "Key") || 229 !t->GetBytes(&plaintext, "Plaintext") || 230 !t->GetBytes(&ciphertext, "Ciphertext")) { 231 return false; 232 } 233 if (EVP_CIPHER_iv_length(cipher) > 0 && 234 !t->GetBytes(&iv, "IV")) { 235 return false; 236 } 237 if (EVP_CIPHER_mode(cipher) == EVP_CIPH_GCM_MODE) { 238 if (!t->GetBytes(&aad, "AAD") || 239 !t->GetBytes(&tag, "Tag")) { 240 return false; 241 } 242 } 243 244 enum { 245 kEncrypt, 246 kDecrypt, 247 kBoth, 248 } operation = kBoth; 249 if (t->HasAttribute("Operation")) { 250 const std::string &str = t->GetAttributeOrDie("Operation"); 251 if (str == "ENCRYPT") { 252 operation = kEncrypt; 253 } else if (str == "DECRYPT") { 254 operation = kDecrypt; 255 } else { 256 t->PrintLine("Unknown operation: '%s'.", str.c_str()); 257 return false; 258 } 259 } 260 261 // By default, both directions are run, unless overridden by the operation. 262 if (operation != kDecrypt) { 263 if (!TestOperation(t, cipher, true /* encrypt */, false /* single-shot */, 264 key, iv, plaintext, ciphertext, aad, tag) || 265 !TestOperation(t, cipher, true /* encrypt */, true /* streaming */, key, 266 iv, plaintext, ciphertext, aad, tag)) { 267 return false; 268 } 269 } 270 if (operation != kEncrypt) { 271 if (!TestOperation(t, cipher, false /* decrypt */, false /* single-shot */, 272 key, iv, plaintext, ciphertext, aad, tag) || 273 !TestOperation(t, cipher, false /* decrypt */, true /* streaming */, 274 key, iv, plaintext, ciphertext, aad, tag)) { 275 return false; 276 } 277 } 278 279 return true; 280 } 281 282 int main(int argc, char **argv) { 283 CRYPTO_library_init(); 284 285 if (argc != 2) { 286 fprintf(stderr, "%s <test file>\n", argv[0]); 287 return 1; 288 } 289 290 return FileTestMain(TestCipher, nullptr, argv[1]); 291 } 292