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-2019 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 
23 #include "ndn-cxx/security/impl/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  NDN_THROW(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  NDN_THROW(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  NDN_THROW(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  NDN_THROW(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  NDN_THROW(Error(getIndex(), "IV length must be " + to_string(requiredIvLen)));
155 
156  BIO_set_cipher(m_impl->m_cipher, cipherType, key, iv, op == CipherOperator::ENCRYPT ? 1 : 0);
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
Definition: data.cpp:26
std::string to_string(const T &val)
Definition: backports.hpp:101
size_t getIndex() const
Get the module index.
BlockCipherAlgorithm
bool isOutputBufferEmpty() const
Check if output buffer is empty.
unique_ptr< Transform > blockCipher(BlockCipherAlgorithm algo, CipherOperator op, const uint8_t *key, size_t keyLen, const uint8_t *iv, size_t ivLen)
#define NDN_THROW(e)
Definition: exception.hpp:61
void setOutputBuffer(unique_ptr< OBuffer > buffer)
Set output buffer to buffer.
Base class of transformation error.
void flushOutputBuffer()
Read the content from output buffer and write it into next module.
BlockCipher(BlockCipherAlgorithm algo, CipherOperator op, const uint8_t *key, size_t keyLen, const uint8_t *iv, size_t ivLen)
Create a block cipher.