1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "crypto/encryptor.h"
6
7#include <cryptohi.h>
8#include <vector>
9
10#include "base/logging.h"
11#include "crypto/nss_util.h"
12#include "crypto/symmetric_key.h"
13
14namespace crypto {
15
16Encryptor::Encryptor()
17    : key_(NULL),
18      mode_(CBC) {
19  EnsureNSSInit();
20}
21
22Encryptor::~Encryptor() {
23}
24
25bool Encryptor::Init(SymmetricKey* key, Mode mode, const std::string& iv) {
26  DCHECK(key);
27  DCHECK_EQ(CBC, mode);
28
29  key_ = key;
30  mode_ = mode;
31
32  if (iv.size() != AES_BLOCK_SIZE)
33    return false;
34
35  slot_.reset(PK11_GetBestSlot(CKM_AES_CBC_PAD, NULL));
36  if (!slot_.get())
37    return false;
38
39  SECItem iv_item;
40  iv_item.type = siBuffer;
41  iv_item.data = reinterpret_cast<unsigned char*>(
42      const_cast<char *>(iv.data()));
43  iv_item.len = iv.size();
44
45  param_.reset(PK11_ParamFromIV(CKM_AES_CBC_PAD, &iv_item));
46  if (!param_.get())
47    return false;
48
49  return true;
50}
51
52bool Encryptor::Encrypt(const std::string& plaintext, std::string* ciphertext) {
53  ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD,
54                                                       CKA_ENCRYPT,
55                                                       key_->key(),
56                                                       param_.get()));
57  if (!context.get())
58    return false;
59
60  size_t ciphertext_len = plaintext.size() + AES_BLOCK_SIZE;
61  std::vector<unsigned char> buffer(ciphertext_len);
62
63  int op_len;
64  SECStatus rv = PK11_CipherOp(context.get(),
65                               &buffer[0],
66                               &op_len,
67                               ciphertext_len,
68                               reinterpret_cast<unsigned char*>(
69                                   const_cast<char*>(plaintext.data())),
70                               plaintext.size());
71  if (SECSuccess != rv)
72    return false;
73
74  unsigned int digest_len;
75  rv = PK11_DigestFinal(context.get(),
76                        &buffer[op_len],
77                        &digest_len,
78                        ciphertext_len - op_len);
79  if (SECSuccess != rv)
80    return false;
81
82  ciphertext->assign(reinterpret_cast<char *>(&buffer[0]),
83                     op_len + digest_len);
84  return true;
85}
86
87bool Encryptor::Decrypt(const std::string& ciphertext, std::string* plaintext) {
88  if (ciphertext.empty())
89    return false;
90
91  ScopedPK11Context context(PK11_CreateContextBySymKey(CKM_AES_CBC_PAD,
92                                                       CKA_DECRYPT,
93                                                       key_->key(),
94                                                       param_.get()));
95  if (!context.get())
96    return false;
97
98  size_t plaintext_len = ciphertext.size();
99  std::vector<unsigned char> buffer(plaintext_len);
100
101  int op_len;
102  SECStatus rv = PK11_CipherOp(context.get(),
103                               &buffer[0],
104                               &op_len,
105                               plaintext_len,
106                               reinterpret_cast<unsigned char*>(
107                                   const_cast<char*>(ciphertext.data())),
108                               ciphertext.size());
109  if (SECSuccess != rv)
110    return false;
111
112  unsigned int digest_len;
113  rv = PK11_DigestFinal(context.get(),
114                        &buffer[op_len],
115                        &digest_len,
116                        plaintext_len - op_len);
117  if (SECSuccess != rv)
118    return false;
119
120  plaintext->assign(reinterpret_cast<char *>(&buffer[0]),
121                    op_len + digest_len);
122  return true;
123}
124
125}  // namespace crypto
126