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#include "qtcore-config_p.h"
44
45// Header from rfc6234
46#include "../../3rdparty/rfc6234/sha.h"
47
48#if QT_CONFIG(system_libb2)
49#include <blake2.h>
50#else
51#include "../../3rdparty/blake2/src/blake2.h"
52#endif
53
54QT_BEGIN_NAMESPACE
55
56static int qt_hash_block_size(QCryptographicHash::Algorithm method)
57{
58 switch (method) {
59 case QCryptographicHash::Md4:
60 return 64;
61 case QCryptographicHash::Md5:
62 return 64;
63 case QCryptographicHash::Sha1:
64 return SHA1_Message_Block_Size;
65 case QCryptographicHash::Sha224:
66 return SHA224_Message_Block_Size;
67 case QCryptographicHash::Sha256:
68 return SHA256_Message_Block_Size;
69 case QCryptographicHash::Sha384:
70 return SHA384_Message_Block_Size;
71 case QCryptographicHash::Sha512:
72 return SHA512_Message_Block_Size;
73 case QCryptographicHash::RealSha3_224:
74 case QCryptographicHash::Keccak_224:
75 return 144;
76 case QCryptographicHash::RealSha3_256:
77 case QCryptographicHash::Keccak_256:
78 return 136;
79 case QCryptographicHash::RealSha3_384:
80 case QCryptographicHash::Keccak_384:
81 return 104;
82 case QCryptographicHash::RealSha3_512:
83 case QCryptographicHash::Keccak_512:
84 return 72;
85 case QCryptographicHash::Blake2b_160:
86 case QCryptographicHash::Blake2b_256:
87 case QCryptographicHash::Blake2b_384:
88 case QCryptographicHash::Blake2b_512:
89 return BLAKE2B_BLOCKBYTES;
90 case QCryptographicHash::Blake2s_128:
91 case QCryptographicHash::Blake2s_160:
92 case QCryptographicHash::Blake2s_224:
93 case QCryptographicHash::Blake2s_256:
94 return BLAKE2S_BLOCKBYTES;
95 }
96 return 0;
97}
98
99class QMessageAuthenticationCodePrivate
100{
101public:
102 QMessageAuthenticationCodePrivate(QCryptographicHash::Algorithm m)
103 : messageHash(m), method(m), messageHashInited(false)
104 {
105 }
106
107 QByteArray key;
108 QByteArray result;
109 QCryptographicHash messageHash;
110 QCryptographicHash::Algorithm method;
111 bool messageHashInited;
112
113 void initMessageHash();
114};
115
116void QMessageAuthenticationCodePrivate::initMessageHash()
117{
118 if (messageHashInited)
119 return;
120 messageHashInited = true;
121
122 const int blockSize = qt_hash_block_size(method);
123
124 if (key.size() > blockSize) {
125 QCryptographicHash hash(method);
126 hash.addData(key);
127 key = hash.result();
128 hash.reset();
129 }
130
131 if (key.size() < blockSize) {
132 const int size = key.size();
133 key.resize(blockSize);
134 memset(key.data() + size, 0, blockSize - size);
135 }
136
137 QVarLengthArray<char> iKeyPad(blockSize);
138 const char * const keyData = key.constData();
139
140 for (int i = 0; i < blockSize; ++i)
141 iKeyPad[i] = keyData[i] ^ 0x36;
142
143 messageHash.addData(iKeyPad.data(), iKeyPad.size());
144}
145
146/*!
147 \class QMessageAuthenticationCode
148 \inmodule QtCore
149
150 \brief The QMessageAuthenticationCode class provides a way to generate
151 hash-based message authentication codes.
152
153 \since 5.1
154
155 \ingroup tools
156 \reentrant
157
158 QMessageAuthenticationCode supports all cryptographic hashes which are supported by
159 QCryptographicHash.
160
161 To generate message authentication code, pass hash algorithm QCryptographicHash::Algorithm
162 to constructor, then set key and message by setKey() and addData() functions. Result
163 can be acquired by result() function.
164 \snippet qmessageauthenticationcode/main.cpp 0
165 \dots
166 \snippet qmessageauthenticationcode/main.cpp 1
167
168 Alternatively, this effect can be achieved by providing message,
169 key and method to hash() method.
170 \snippet qmessageauthenticationcode/main.cpp 2
171
172 \sa QCryptographicHash
173*/
174
175/*!
176 Constructs an object that can be used to create a cryptographic hash from data
177 using method \a method and key \a key.
178*/
179QMessageAuthenticationCode::QMessageAuthenticationCode(QCryptographicHash::Algorithm method,
180 const QByteArray &key)
181 : d(new QMessageAuthenticationCodePrivate(method))
182{
183 d->key = key;
184}
185
186/*!
187 Destroys the object.
188*/
189QMessageAuthenticationCode::~QMessageAuthenticationCode()
190{
191 delete d;
192}
193
194/*!
195 Resets message data. Calling this method doesn't affect the key.
196*/
197void QMessageAuthenticationCode::reset()
198{
199 d->result.clear();
200 d->messageHash.reset();
201 d->messageHashInited = false;
202}
203
204/*!
205 Sets secret \a key. Calling this method automatically resets the object state.
206*/
207void QMessageAuthenticationCode::setKey(const QByteArray &key)
208{
209 reset();
210 d->key = key;
211}
212
213/*!
214 Adds the first \a length chars of \a data to the message.
215*/
216void QMessageAuthenticationCode::addData(const char *data, int length)
217{
218 d->initMessageHash();
219 d->messageHash.addData(data, length);
220}
221
222/*!
223 \overload addData()
224*/
225void QMessageAuthenticationCode::addData(const QByteArray &data)
226{
227 d->initMessageHash();
228 d->messageHash.addData(data);
229}
230
231/*!
232 Reads the data from the open QIODevice \a device until it ends
233 and adds it to message. Returns \c true if reading was successful.
234
235 \note \a device must be already opened.
236 */
237bool QMessageAuthenticationCode::addData(QIODevice *device)
238{
239 d->initMessageHash();
240 return d->messageHash.addData(device);
241}
242
243/*!
244 Returns the final authentication code.
245
246 \sa QByteArray::toHex()
247*/
248QByteArray QMessageAuthenticationCode::result() const
249{
250 if (!d->result.isEmpty())
251 return d->result;
252
253 d->initMessageHash();
254
255 const int blockSize = qt_hash_block_size(d->method);
256
257 QByteArray hashedMessage = d->messageHash.result();
258
259 QVarLengthArray<char> oKeyPad(blockSize);
260 const char * const keyData = d->key.constData();
261
262 for (int i = 0; i < blockSize; ++i)
263 oKeyPad[i] = keyData[i] ^ 0x5c;
264
265 QCryptographicHash hash(d->method);
266 hash.addData(oKeyPad.data(), oKeyPad.size());
267 hash.addData(hashedMessage);
268
269 d->result = hash.result();
270 return d->result;
271}
272
273/*!
274 Returns the authentication code for the message \a message using
275 the key \a key and the method \a method.
276*/
277QByteArray QMessageAuthenticationCode::hash(const QByteArray &message, const QByteArray &key,
278 QCryptographicHash::Algorithm method)
279{
280 QMessageAuthenticationCode mac(method);
281 mac.setKey(key);
282 mac.addData(message);
283 return mac.result();
284}
285
286QT_END_NAMESPACE
287