1/*
2 * Written by Dr Stephen N Henson (steve@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@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
68static 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
109static 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
216static 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
282int 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