1/****************************************************************************
2**
3** Copyright (C) 2018 The Qt Company Ltd.
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 "qpassworddigestor.h"
41
42#include <QtCore/QDebug>
43#include <QtCore/QMessageAuthenticationCode>
44#include <QtCore/QtEndian>
45
46#include <limits>
47
48QT_BEGIN_NAMESPACE
49namespace QPasswordDigestor {
50
51/*!
52 \namespace QPasswordDigestor
53 \inmodule QtNetwork
54
55 \brief The QPasswordDigestor namespace contains functions which you can use
56 to generate hashes or keys.
57*/
58
59/*!
60 \since 5.12
61
62 Returns a hash computed using the PBKDF1-algorithm as defined in
63 \l {https://tools.ietf.org/html/rfc8018#section-5.1} {RFC 8018}.
64
65 The function takes the \a data and \a salt, and then hashes it repeatedly
66 for \a iterations iterations using the specified hash \a algorithm. If the
67 resulting hash is longer than \a dkLen then it is truncated before it is
68 returned.
69
70 This function only supports SHA-1 and MD5! The max output size is 160 bits
71 (20 bytes) when using SHA-1, or 128 bits (16 bytes) when using MD5.
72 Specifying a value for \a dkLen which is greater than this results in a
73 warning and an empty QByteArray is returned. To programmatically check this
74 limit you can use \l {QCryptographicHash::hashLength}. Furthermore: the
75 \a salt must always be 8 bytes long!
76
77 \note This function is provided for use with legacy applications and all
78 new applications are recommended to use \l {deriveKeyPbkdf2} {PBKDF2}.
79
80 \sa deriveKeyPbkdf2, QCryptographicHash, QCryptographicHash::hashLength
81*/
82Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf1(QCryptographicHash::Algorithm algorithm,
83 const QByteArray &data, const QByteArray &salt,
84 int iterations, quint64 dkLen)
85{
86 // https://tools.ietf.org/html/rfc8018#section-5.1
87
88 if (algorithm != QCryptographicHash::Sha1
89#ifndef QT_CRYPTOGRAPHICHASH_ONLY_SHA1
90 && algorithm != QCryptographicHash::Md5
91#endif
92 ) {
93 qWarning(msg: "The only supported algorithms for pbkdf1 are SHA-1 and MD5!");
94 return QByteArray();
95 }
96
97 if (salt.size() != 8) {
98 qWarning(msg: "The salt must be 8 bytes long!");
99 return QByteArray();
100 }
101 if (iterations < 1 || dkLen < 1)
102 return QByteArray();
103
104 if (dkLen > quint64(QCryptographicHash::hashLength(method: algorithm))) {
105 qWarning() << "Derived key too long:\n"
106 << algorithm << "was chosen which produces output of length"
107 << QCryptographicHash::hashLength(method: algorithm) << "but" << dkLen
108 << "was requested.";
109 return QByteArray();
110 }
111
112 QCryptographicHash hash(algorithm);
113 hash.addData(data);
114 hash.addData(data: salt);
115 QByteArray key = hash.result();
116
117 for (int i = 1; i < iterations; i++) {
118 hash.reset();
119 hash.addData(data: key);
120 key = hash.result();
121 }
122 return key.left(len: dkLen);
123}
124
125/*!
126 \since 5.12
127
128 Derive a key using the PBKDF2-algorithm as defined in
129 \l {https://tools.ietf.org/html/rfc8018#section-5.2} {RFC 8018}.
130
131 This function takes the \a data and \a salt, and then applies HMAC-X, where
132 the X is \a algorithm, repeatedly. It internally concatenates intermediate
133 results to the final output until at least \a dkLen amount of bytes have
134 been computed and it will execute HMAC-X \a iterations times each time a
135 concatenation is required. The total number of times it will execute HMAC-X
136 depends on \a iterations, \a dkLen and \a algorithm and can be calculated
137 as
138 \c{iterations * ceil(dkLen / QCryptographicHash::hashLength(algorithm))}.
139
140 \sa deriveKeyPbkdf1, QMessageAuthenticationCode, QCryptographicHash
141*/
142Q_NETWORK_EXPORT QByteArray deriveKeyPbkdf2(QCryptographicHash::Algorithm algorithm,
143 const QByteArray &data, const QByteArray &salt,
144 int iterations, quint64 dkLen)
145{
146 // https://tools.ietf.org/html/rfc8018#section-5.2
147
148 // The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
149 int hashLen = QCryptographicHash::hashLength(method: algorithm);
150 const quint64 maxLen = quint64(std::numeric_limits<quint32>::max() - 1) * hashLen;
151 if (dkLen > maxLen) {
152 qWarning().nospace() << "Derived key too long:\n"
153 << algorithm << " was chosen which produces output of length "
154 << maxLen << " but " << dkLen << " was requested.";
155 return QByteArray();
156 }
157
158 if (iterations < 1 || dkLen < 1)
159 return QByteArray();
160
161 QByteArray key;
162 quint32 currentIteration = 1;
163 QMessageAuthenticationCode hmac(algorithm, data);
164 QByteArray index(4, Qt::Uninitialized);
165 while (quint64(key.length()) < dkLen) {
166 hmac.addData(data: salt);
167
168 qToBigEndian(src: currentIteration, dest: index.data());
169 hmac.addData(data: index);
170
171 QByteArray u = hmac.result();
172 hmac.reset();
173 QByteArray tkey = u;
174 for (int iter = 1; iter < iterations; iter++) {
175 hmac.addData(data: u);
176 u = hmac.result();
177 hmac.reset();
178 std::transform(first1: tkey.cbegin(), last1: tkey.cend(), first2: u.cbegin(), result: tkey.begin(),
179 binary_op: std::bit_xor<char>());
180 }
181 key += tkey;
182 currentIteration++;
183 }
184 return key.left(len: dkLen);
185}
186} // namespace QPasswordDigestor
187QT_END_NAMESPACE
188

source code of qtbase/src/network/ssl/qpassworddigestor.cpp