1dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch// found in the LICENSE file.
4513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
5ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/encryptor.h"
6513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
74a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <openssl/aes.h>
84a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include <openssl/evp.h>
94a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
10513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch#include "base/logging.h"
114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch#include "base/string_util.h"
12ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/openssl_util.h"
13ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/symmetric_key.h"
14513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
15ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsennamespace crypto {
16513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochnamespace {
184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochconst EVP_CIPHER* GetCipherForKey(SymmetricKey* key) {
204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  switch (key->key().length()) {
214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    case 16: return EVP_aes_128_cbc();
224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    case 24: return EVP_aes_192_cbc();
234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    case 32: return EVP_aes_256_cbc();
244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    default: return NULL;
254a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
264a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// On destruction this class will cleanup the ctx, and also clear the OpenSSL
294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch// ERR stack as a convenience.
304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochclass ScopedCipherCTX {
314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch public:
324a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  explicit ScopedCipherCTX() {
334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    EVP_CIPHER_CTX_init(&ctx_);
344a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
354a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ~ScopedCipherCTX() {
364a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    EVP_CIPHER_CTX_cleanup(&ctx_);
37201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    ClearOpenSSLERRStack(FROM_HERE);
384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
394a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EVP_CIPHER_CTX* get() { return &ctx_; }
404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
414a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch private:
424a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EVP_CIPHER_CTX ctx_;
434a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch};
444a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
454a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}  // namespace
464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
474a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben MurdochEncryptor::Encryptor()
48dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    : key_(NULL),
49dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      mode_(CBC) {
50513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
51513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
52513209b27ff55e2841eac0e4120199c23acce758Ben MurdochEncryptor::~Encryptor() {
53513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
54513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
55513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) {
564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(key);
574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK_EQ(CBC, mode);
584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  EnsureOpenSSLInit();
604a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (iv.size() != AES_BLOCK_SIZE)
614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return false;
624a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (GetCipherForKey(key) == NULL)
644a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return false;
654a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  key_ = key;
674a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  mode_ = mode;
684a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  iv_ = iv;
694a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return true;
70513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
71513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
72513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
734a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return Crypt(true, plaintext, ciphertext);
74513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
75513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
76513209b27ff55e2841eac0e4120199c23acce758Ben Murdochbool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) {
774a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return Crypt(false, ciphertext, plaintext);
784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch}
794a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdochbool Encryptor::Crypt(bool do_encrypt,
814a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                      const std::string& input,
824a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                      std::string* output) {
834a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(key_);  // Must call Init() before En/De-crypt.
844a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Work on the result in a local variable, and then only transfer it to
854a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // |output| on success to ensure no partial data is returned.
864a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::string result;
874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  output->swap(result);
884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  const EVP_CIPHER* cipher = GetCipherForKey(key_);
904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(cipher);  // Already handled in Init();
914a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
924a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  const std::string& key = key_->key();
934a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK_EQ(EVP_CIPHER_iv_length(cipher), static_cast<int>(iv_.length()));
944a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK_EQ(EVP_CIPHER_key_length(cipher), static_cast<int>(key.length()));
954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  ScopedCipherCTX ctx;
974a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!EVP_CipherInit_ex(ctx.get(), cipher, NULL,
984a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                         reinterpret_cast<const uint8*>(key.data()),
994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                         reinterpret_cast<const uint8*>(iv_.data()),
1004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                         do_encrypt))
1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return false;
1024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1034a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // When encrypting, add another block size of space to allow for any padding.
1044a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  const size_t output_size = input.size() + (do_encrypt ? iv_.size() : 0);
1054a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  uint8* out_ptr = reinterpret_cast<uint8*>(WriteInto(&result,
1064a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                                      output_size + 1));
1074a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  int out_len;
1084a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!EVP_CipherUpdate(ctx.get(), out_ptr, &out_len,
1094a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                        reinterpret_cast<const uint8*>(input.data()),
1104a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                        input.length()))
1114a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return false;
1124a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Write out the final block plus padding (if any) to the end of the data
1144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // just written.
1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  int tail_len;
1164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!EVP_CipherFinal_ex(ctx.get(), out_ptr + out_len, &tail_len))
1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return false;
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  out_len += tail_len;
1204a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK_LE(out_len, static_cast<int>(output_size));
1214a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  result.resize(out_len);
1224a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
1234a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  output->swap(result);
1244a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  return true;
125513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch}
126513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}  // namespace crypto
128