blob: b53ec4f6bd93534f1fdd61f697cb50d71f0dd2e1 [file] [log] [blame]
Yingdi Yu87516612015-07-10 18:03:52 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3 * Copyright (c) 2013-2016 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
27namespace ndn {
28namespace security {
29namespace transform {
30
31class BlockCipher::Impl
32{
33public:
34 Impl()
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_sink);
44 }
45
46public:
47 BIO* m_cipher;
48 BIO* m_sink; // BIO_f_cipher alone does not work without a sink
49};
50
51BlockCipher::BlockCipher(BlockCipherAlgorithm algo, CipherOperator op,
52 const uint8_t* key, size_t keyLen,
53 const uint8_t* iv, size_t ivLen)
54 : m_impl(new Impl)
55{
56 switch (algo) {
57 case BlockCipherAlgorithm::AES_CBC:
58 initializeAesCbc(key, keyLen, iv, ivLen, op);
59 break;
60 default:
61 BOOST_THROW_EXCEPTION(Error(getIndex(), "Cipher algorithm " +
62 boost::lexical_cast<std::string>(algo) + " is not supported"));
63 }
64}
65
66void
67BlockCipher::preTransform()
68{
69 fillOutputBuffer();
70}
71
72size_t
73BlockCipher::convert(const uint8_t* data, size_t dataLen)
74{
75 if (dataLen == 0)
76 return 0;
77
78 int wLen = BIO_write(m_impl->m_cipher, data, dataLen);
79
80 if (wLen <= 0) { // fail to write data
81 if (!BIO_should_retry(m_impl->m_cipher)) {
82 // we haven't written everything but some error happens, and we cannot retry
83 BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to accept more input"));
84 }
85 return 0;
86 }
87 else { // update number of bytes written
88 fillOutputBuffer();
89 return wLen;
90 }
91}
92
93void
94BlockCipher::finalize()
95{
96 if (BIO_flush(m_impl->m_cipher) != 1)
97 BOOST_THROW_EXCEPTION(Error(getIndex(), "Failed to flush"));
98
99 while (!isConverterEmpty()) {
100 fillOutputBuffer();
101 while (!isOutputBufferEmpty()) {
102 flushOutputBuffer();
103 }
104 }
105}
106
107void
108BlockCipher::fillOutputBuffer()
109{
110 int nRead = BIO_pending(m_impl->m_sink);
111 if (nRead <= 0)
112 return;
113
114 // there is something to read from BIO
115 auto buffer = make_unique<OBuffer>(nRead);
116 int rLen = BIO_read(m_impl->m_sink, &(*buffer)[0], nRead);
117 if (rLen < 0)
118 return;
119
120 if (rLen < nRead)
121 buffer->erase(buffer->begin() + rLen, buffer->end());
122 setOutputBuffer(std::move(buffer));
123}
124
125bool
126BlockCipher::isConverterEmpty() const
127{
128 return (BIO_pending(m_impl->m_sink) <= 0);
129}
130
131void
132BlockCipher::initializeAesCbc(const uint8_t* key, size_t keyLen,
133 const uint8_t* iv, size_t ivLen,
134 CipherOperator op)
135{
136 if (keyLen != ivLen)
137 BOOST_THROW_EXCEPTION(Error(getIndex(), "Key length must be the same as IV length"));
138
139 const EVP_CIPHER* cipherType = nullptr;
140 switch (keyLen) {
141 case 16:
142 cipherType = EVP_aes_128_cbc();
143 break;
144 case 24:
145 cipherType = EVP_aes_192_cbc();
146 break;
147 case 32:
148 cipherType = EVP_aes_256_cbc();
149 break;
150 default:
151 BOOST_THROW_EXCEPTION(Error(getIndex(), "Key length is not supported"));
152 }
153 BIO_set_cipher(m_impl->m_cipher, cipherType, key, iv, static_cast<int>(op));
154}
155
156unique_ptr<Transform>
157blockCipher(BlockCipherAlgorithm algo, CipherOperator op,
158 const uint8_t* key, size_t keyLen,
159 const uint8_t* iv, size_t ivLen)
160{
161 return make_unique<BlockCipher>(algo, op, key, keyLen, iv, ivLen);
162}
163
164} // namespace transform
165} // namespace security
166} // namespace ndn