Home | History | Annotate | Download | only in cipher
      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