1/****************************************************************************
2**
3** Copyright (C) 2013 Ruslan Nigmatullin <euroelessar@yandex.ru>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtCore module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qmessageauthenticationcode.h"
41#include "qvarlengtharray.h"
42
43/*
44 These #defines replace the typedefs needed by the RFC6234 code. Normally
45 the typedefs would come from from stdint.h, but since this header is not
46 available on all platforms (MSVC 2008, for example), we #define them to the
47 Qt equivalents.
48*/
49
50#ifdef uint64_t
51#undef uint64_t
52#endif
53
54#define uint64_t QT_PREPEND_NAMESPACE(quint64)
55
56#ifdef uint32_t
57#undef uint32_t
58#endif
59
60#define uint32_t QT_PREPEND_NAMESPACE(quint32)
61
62#ifdef uint8_t
63#undef uint8_t
64#endif
65
66#define uint8_t QT_PREPEND_NAMESPACE(quint8)
67
68#ifdef int_least16_t
69#undef int_least16_t
70#endif
71
72#define int_least16_t QT_PREPEND_NAMESPACE(qint16)
73
74// Header from rfc6234 with 1 modification:
75// sha1.h - commented out '#include <stdint.h>' on line 74
76#include "../../3rdparty/rfc6234/sha.h"
77
78#undef uint64_t
79#undef uint32_t
80#undef uint68_t
81#undef int_least16_t
82
83QT_BEGIN_NAMESPACE
84
85static int qt_hash_block_size(QCryptographicHash::Algorithm method)
86{
87 switch (method) {
88 case QCryptographicHash::Md4:
89 return 64;
90 case QCryptographicHash::Md5:
91 return 64;
92 case QCryptographicHash::Sha1:
93 return SHA1_Message_Block_Size;
94 case QCryptographicHash::Sha224:
95 return SHA224_Message_Block_Size;
96 case QCryptographicHash::Sha256:
97 return SHA256_Message_Block_Size;
98 case QCryptographicHash::Sha384:
99 return SHA384_Message_Block_Size;
100 case QCryptographicHash::Sha512:
101 return SHA512_Message_Block_Size;
102 case QCryptographicHash::RealSha3_224:
103 case QCryptographicHash::Keccak_224:
104 return 144;
105 case QCryptographicHash::RealSha3_256:
106 case QCryptographicHash::Keccak_256:
107 return 136;
108 case QCryptographicHash::RealSha3_384:
109 case QCryptographicHash::Keccak_384:
110 return 104;
111 case QCryptographicHash::RealSha3_512:
112 case QCryptographicHash::Keccak_512:
113 return 72;
114 }
115 return 0;
116}
117
118class QMessageAuthenticationCodePrivate
119{
120public:
121 QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m)
122 : messageHash(m), method(m), messageHashInited(false)
123 {
124 }
125
126 QByteArray key;
127 QByteArray result;
128 QCryptographicHash messageHash;
129 QCryptographicHash::Algorithm method;
130 bool messageHashInited;
131
132 void initMessageHash();
133};
134
135void QMessageAuthenticationCodePrivate::initMessageHash()
136{
137 if (messageHashInited)
138 return;
139 messageHashInited = true;
140
141 const int blockSize = qt_hash_block_size(method);
142
143 if (key.size() > blockSize) {
144 QCryptographicHash hash(method);
145 hash.addData(key);
146 key = hash.result();
147 hash.reset();
148 }
149
150 if (key.size() < blockSize) {
151 const int size = key.size();
152 key.resize(blockSize);
153 memset(key.data() + size, 0, blockSize - size);
154 }
155
156 QVarLengthArray<char> iKeyPad(blockSize);
157 const char * const keyData = key.constData();
158
159 for (int i = 0; i < blockSize; ++i)
160 iKeyPad[i] = keyData[i] ^ 0x36;
161
162 messageHash.addData(iKeyPad.data(), iKeyPad.size());
163}
164
165/*!
166 \class QMessageAuthenticationCode
167 \inmodule QtCore
168
169 \brief The QMessageAuthenticationCode class provides a way to generate
170 hash-based message authentication codes.
171
172 \since 5.1
173
174 \ingroup tools
175 \reentrant
176
177 QMessageAuthenticationCode supports all cryptographic hashes which are supported by
178 QCryptographicHash.
179
180 To generate message authentication code, pass hash algorithm QCryptographicHash::Algorithm
181 to constructor, then set key and message by setKey() and addData() functions. Result
182 can be acquired by result() function.
183 \snippet qmessageauthenticationcode/main.cpp 0
184 \dots
185 \snippet qmessageauthenticationcode/main.cpp 1
186
187 Alternatively, this effect can be achieved by providing message,
188 key and method to hash() method.
189 \snippet qmessageauthenticationcode/main.cpp 2
190
191 \sa QCryptographicHash
192*/
193
194/*!
195 Constructs an object that can be used to create a cryptographic hash from data
196 using method \a method and key \a key.
197*/
198QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
199 const QByteArray &key)
200 : d(new QMessageAuthenticationCodePrivate(method))
201{
202 d->key = key;
203}
204
205/*!
206 Destroys the object.
207*/
208QMessageAuthenticationCode::~QMessageAuthenticationCode()
209{
210 delete d;
211}
212
213/*!
214 Resets message data. Calling this method doesn't affect the key.
215*/
216void QMessageAuthenticationCode::reset()
217{
218 d->result.clear();
219 d->messageHash.reset();
220 d->messageHashInited = false;
221}
222
223/*!
224 Sets secret \a key. Calling this method automatically resets the object state.
225*/
226void QMessageAuthenticationCode::setKey(const QByteArray &key)
227{
228 reset();
229 d->key = key;
230}
231
232/*!
233 Adds the first \a length chars of \a data to the message.
234*/
235void QMessageAuthenticationCode::addData(const char *data, int length)
236{
237 d->initMessageHash();
238 d->messageHash.addData(data, length);
239}
240
241/*!
242 \overload addData()
243*/
244void QMessageAuthenticationCode::addData(const QByteArray &data)
245{
246 d->initMessageHash();
247 d->messageHash.addData(data);
248}
249
250/*!
251 Reads the data from the open QIODevice \a device until it ends
252 and adds it to message. Returns \c true if reading was successful.
253
254 \note \a device must be already opened.
255 */
256bool QMessageAuthenticationCode::addData(QIODevice *device)
257{
258 d->initMessageHash();
259 return d->messageHash.addData(device);
260}
261
262/*!
263 Returns the final authentication code.
264
265 \sa QByteArray::toHex()
266*/
267QByteArray QMessageAuthenticationCode::result() const
268{
269 if (!d->result.isEmpty())
270 return d->result;
271
272 d->initMessageHash();
273
274 const int blockSize = qt_hash_block_size(d->method);
275
276 QByteArray hashedMessage = d->messageHash.result();
277
278 QVarLengthArray<char> oKeyPad(blockSize);
279 const char * const keyData = d->key.constData();
280
281 for (int i = 0; i < blockSize; ++i)
282 oKeyPad[i] = keyData[i] ^ 0x5c;
283
284 QCryptographicHash hash(d->method);
285 hash.addData(oKeyPad.data(), oKeyPad.size());
286 hash.addData(hashedMessage);
287
288 d->result = hash.result();
289 return d->result;
290}
291
292/*!
293 Returns the authentication code for the message \a message using
294 the key \a key and the method \a method.
295*/
296QByteArray QMessageAuthenticationCode::hash(const QByteArray &message, const QByteArray &key,
297 QCryptographicHash::Algorithm method)
298{
299 QMessageAuthenticationCode mac(method);
300 mac.setKey(key);
301 mac.addData(message);
302 return mac.result();
303}
304
305QT_END_NAMESPACE
306