block-cipher.cpp
Go to the documentation of this file.
1 /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2 /*
3  * Copyright (c) 2013-2017 Regents of the University of California.
4  *
5  * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
6  *
7  * ndn-cxx library is free software: you can redistribute it and/or modify it under the
8  * terms of the GNU Lesser General Public License as published by the Free Software
9  * Foundation, either version 3 of the License, or (at your option) any later version.
10  *
11  * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13  * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14  *
15  * You should have received copies of the GNU General Public License and GNU Lesser
16  * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
20  */
21 
22 #include "block-cipher.hpp"
23 #include "../detail/openssl.hpp"
24 
25 #include <boost/lexical_cast.hpp>
26 
27 namespace ndn {
28 namespace security {
29 namespace transform {
30 
31 class BlockCipher::Impl
32 {
33 public:
34  Impl() noexcept
35  : m_cipher(BIO_new(BIO_f_cipher()))
36  , m_sink(BIO_new(BIO_s_mem()))
37  {
38  BIO_push(m_cipher, m_sink);
39  }
40 
41  ~Impl()
42  {
43  BIO_free_all(m_cipher);
44  }
45 
46 public:
47  BIO* m_cipher;
48  BIO* m_sink; // BIO_f_cipher alone does not work without a sink
49 };
50 
51 
53  const uint8_t* key, size_t keyLen,
54  const uint8_t* iv, size_t ivLen)
55  : m_impl(make_unique<Impl>())
56 {
57  switch (algo) {
59  initializeAesCbc(key, keyLen, iv, ivLen, op);
60  break;
61  default:
62  BOOST_THROW_EXCEPTION(Error(getIndex(), "Unsupported block cipher algorithm " +
63  boost::lexical_cast<std::string>(algo)));
64  }
65 }
66 
67 BlockCipher::~BlockCipher() = default;
68 
69 void
70 BlockCipher::preTransform()
71 {
72  fillOutputBuffer();
73 }
74 
75 size_t
76 BlockCipher::convert(const uint8_t* data, size_t dataLen)
77 {
78  if (dataLen == 0)
79  return 0;
80 
81  int wLen = BIO_write(m_impl->m_cipher, data, dataLen);
82 
83  if (wLen <= 0) { // failed to write data
84  if (!BIO_should_retry(m_impl->m_cipher)) {
85  // we haven't written everything but some error happens, and we cannot retry
86  BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to accept more input"));
87  }
88  return 0;
89  }
90  else { // update number of bytes written
91  fillOutputBuffer();
92  return static_cast<size_t>(wLen);
93  }
94 }
95 
96 void
97 BlockCipher::finalize()
98 {
99  if (BIO_flush(m_impl->m_cipher) != 1)
100  BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to flush"));
101 
102  while (!isConverterEmpty()) {
103  fillOutputBuffer();
104  while (!isOutputBufferEmpty()) {
106  }
107  }
108 }
109 
110 void
111 BlockCipher::fillOutputBuffer()
112 {
113  int nPending = BIO_pending(m_impl->m_sink);
114  if (nPending <= 0)
115  return;
116 
117  // there is something to read from BIO
118  auto buffer = make_unique<OBuffer>(nPending);
119  int nRead = BIO_read(m_impl->m_sink, buffer->data(), nPending);
120  if (nRead < 0)
121  return;
122 
123  buffer->erase(buffer->begin() + nRead, buffer->end());
124  setOutputBuffer(std::move(buffer));
125 }
126 
127 bool
128 BlockCipher::isConverterEmpty() const
129 {
130  return BIO_pending(m_impl->m_sink) <= 0;
131 }
132 
133 void
134 BlockCipher::initializeAesCbc(const uint8_t* key, size_t keyLen,
135  const uint8_t* iv, size_t ivLen, CipherOperator op)
136 {
137  const EVP_CIPHER* cipherType = nullptr;
138  switch (keyLen) {
139  case 16:
140  cipherType = EVP_aes_128_cbc();
141  break;
142  case 24:
143  cipherType = EVP_aes_192_cbc();
144  break;
145  case 32:
146  cipherType = EVP_aes_256_cbc();
147  break;
148  default:
149  BOOST_THROW_EXCEPTION(Error(getIndex(), "Unsupported key length " + to_string(keyLen)));
150  }
151 
152  size_t requiredIvLen = static_cast<size_t>(EVP_CIPHER_iv_length(cipherType));
153  if (ivLen != requiredIvLen)
154  BOOST_THROW_EXCEPTION(Error(getIndex(), "IV length must be " + to_string(requiredIvLen)));
155 
156  BIO_set_cipher(m_impl->m_cipher, cipherType, key, iv, static_cast<int>(op));
157 }
158 
159 unique_ptr<Transform>
161  const uint8_t* key, size_t keyLen,
162  const uint8_t* iv, size_t ivLen)
163 {
164  return make_unique<BlockCipher>(algo, op, key, keyLen, iv, ivLen);
165 }
166 
167 } // namespace transform
168 } // namespace security
169 } // namespace ndn
Copyright (c) 2013-2017 Regents of the University of California.
Definition: common.hpp:66
BlockCipherAlgorithm
unique_ptr< T > make_unique(Args &&...args)
Definition: backports.hpp:73
unique_ptr< Transform > blockCipher(BlockCipherAlgorithm algo, CipherOperator op, const uint8_t *key, size_t keyLen, const uint8_t *iv, size_t ivLen)
void setOutputBuffer(unique_ptr< OBuffer > buffer)
Set output buffer to buffer.
bool isOutputBufferEmpty() const
Check if output buffer is empty.
Base class of transformation error.
void flushOutputBuffer()
Read the content from output buffer and write it into next module.
std::string to_string(const V &v)
Definition: backports.hpp:84
BlockCipher(BlockCipherAlgorithm algo, CipherOperator op, const uint8_t *key, size_t keyLen, const uint8_t *iv, size_t ivLen)
Create a block cipher.
size_t getIndex() const
Get the module index.