ndn-cxx: NDN C++ Library 0.9.0-33-g832ea91d
Loading...
Searching...
No Matches
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-2023 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
24#include <openssl/bio.h>
25#include <openssl/evp.h>
26
27#include <boost/lexical_cast.hpp>
28
30
31class BlockCipher::Impl : boost::noncopyable
32{
33public:
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
46public:
47 BIO* m_cipher;
48 BIO* m_sink; // BIO_f_cipher alone does not work without a sink
49};
50
51
53 span<const uint8_t> key, span<const uint8_t> iv)
54 : m_impl(make_unique<Impl>())
55{
56 switch (algo) {
58 initializeAesCbc(key, iv, op);
59 break;
60 default:
61 NDN_THROW(Error(getIndex(), "Unsupported block cipher algorithm " +
62 boost::lexical_cast<std::string>(algo)));
63 }
64}
65
67
68void
69BlockCipher::preTransform()
70{
71 fillOutputBuffer();
72}
73
74size_t
75BlockCipher::convert(span<const uint8_t> data)
76{
77 if (data.empty())
78 return 0;
79
80 int wLen = BIO_write(m_impl->m_cipher, data.data(), data.size());
81
82 if (wLen <= 0) { // failed to write data
83 if (!BIO_should_retry(m_impl->m_cipher)) {
84 // we haven't written everything but some error happens, and we cannot retry
85 NDN_THROW(Error(getIndex(), "Failed to accept more input"));
86 }
87 return 0;
88 }
89 else { // update number of bytes written
90 fillOutputBuffer();
91 return static_cast<size_t>(wLen);
92 }
93}
94
95void
96BlockCipher::finalize()
97{
98 if (BIO_flush(m_impl->m_cipher) != 1)
99 NDN_THROW(Error(getIndex(), "Failed to flush"));
100
101 while (!isConverterEmpty()) {
102 fillOutputBuffer();
103 while (!isOutputBufferEmpty()) {
105 }
106 }
107}
108
109void
110BlockCipher::fillOutputBuffer()
111{
112 int nPending = BIO_pending(m_impl->m_sink);
113 if (nPending <= 0)
114 return;
115
116 // there is something to read from BIO
117 auto buffer = make_unique<OBuffer>(nPending);
118 int nRead = BIO_read(m_impl->m_sink, buffer->data(), nPending);
119 if (nRead < 0)
120 return;
121
122 buffer->erase(buffer->begin() + nRead, buffer->end());
123 setOutputBuffer(std::move(buffer));
124}
125
126bool
127BlockCipher::isConverterEmpty() const
128{
129 return BIO_pending(m_impl->m_sink) <= 0;
130}
131
132void
133BlockCipher::initializeAesCbc(span<const uint8_t> key, span<const uint8_t> iv, CipherOperator op)
134{
135 const EVP_CIPHER* cipherType = nullptr;
136 switch (key.size()) {
137 case 16:
138 cipherType = EVP_aes_128_cbc();
139 break;
140 case 24:
141 cipherType = EVP_aes_192_cbc();
142 break;
143 case 32:
144 cipherType = EVP_aes_256_cbc();
145 break;
146 default:
147 NDN_THROW(Error(getIndex(), "Unsupported key length " + to_string(key.size())));
148 }
149
150 auto requiredIvLen = static_cast<size_t>(EVP_CIPHER_iv_length(cipherType));
151 if (iv.size() != requiredIvLen)
152 NDN_THROW(Error(getIndex(), "IV length must be " + to_string(requiredIvLen)));
153
154 BIO_set_cipher(m_impl->m_cipher, cipherType, key.data(), iv.data(),
155 op == CipherOperator::ENCRYPT ? 1 : 0);
156}
157
158unique_ptr<Transform>
160 span<const uint8_t> key, span<const uint8_t> iv)
161{
162 return make_unique<BlockCipher>(algo, op, key, iv);
163}
164
165} // namespace ndn::security::transform
BlockCipher(BlockCipherAlgorithm algo, CipherOperator op, span< const uint8_t > key, span< const uint8_t > iv)
Create a block cipher.
size_t getIndex() const
Get the module index.
Base class of transformation error.
void setOutputBuffer(unique_ptr< OBuffer > buffer)
Set output buffer to buffer.
bool isOutputBufferEmpty() const
Check if output buffer is empty.
void flushOutputBuffer()
Read the content from output buffer and write it into next module.
#define NDN_THROW(e)
Definition exception.hpp:56
std::string to_string(const errinfo_stacktrace &x)
Definition exception.cpp:30
unique_ptr< Transform > blockCipher(BlockCipherAlgorithm algo, CipherOperator op, span< const uint8_t > key, span< const uint8_t > iv)