Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qpassworddigestor.cpp
Go to the documentation of this file.
1
// Copyright (C) 2018 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
// Qt-Security score:critical reason:cryptography
4
5
#
include
"qpassworddigestor.h"
6
7
#
include
<
QtCore
/
QDebug
>
8
#
include
<
QtCore
/
QMessageAuthenticationCode
>
9
#
include
<
QtCore
/
QtEndian
>
10
#
include
<
QtCore
/
QList
>
11
12
#
include
"qtcore-config_p.h"
13
14
#
include
<
limits
>
15
16
#
if
QT_CONFIG
(
opensslv30
)
&&
QT_CONFIG
(
openssl_linked
)
17
#
define
USING_OPENSSL30
18
#
include
<
openssl
/
core_names
.
h
>
19
#
include
<
openssl
/
kdf
.
h
>
20
#
include
<
openssl
/
params
.
h
>
21
#
include
<
openssl
/
provider
.
h
>
22
#
endif
23
24
QT_BEGIN_NAMESPACE
25
namespace
QPasswordDigestor
{
26
27
/*!
28
\namespace QPasswordDigestor
29
\inmodule QtNetwork
30
31
\brief The QPasswordDigestor namespace contains functions which you can use
32
to generate hashes or keys.
33
*/
34
35
/*!
36
\since 5.12
37
38
Returns a hash computed using the PBKDF1-algorithm as defined in
39
\l {RFC 8018, section 5.1}.
40
41
The function takes the \a data and \a salt, and then hashes it repeatedly
42
for \a iterations iterations using the specified hash \a algorithm. If the
43
resulting hash is longer than \a dkLen then it is truncated before it is
44
returned.
45
46
This function only supports SHA-1 and MD5! The max output size is 160 bits
47
(20 bytes) when using SHA-1, or 128 bits (16 bytes) when using MD5.
48
Specifying a value for \a dkLen which is greater than this results in a
49
warning and an empty QByteArray is returned. To programmatically check this
50
limit you can use \l {QCryptographicHash::hashLength}. Furthermore: the
51
\a salt must always be 8 bytes long!
52
53
\note This function is provided for use with legacy applications and all
54
new applications are recommended to use \l {deriveKeyPbkdf2} {PBKDF2}.
55
56
\sa deriveKeyPbkdf2, QCryptographicHash, QCryptographicHash::hashLength
57
*/
58
Q_NETWORK_EXPORT QByteArray
deriveKeyPbkdf1
(
QCryptographicHash
::
Algorithm
algorithm
,
59
const
QByteArray
&
data
,
const
QByteArray
&
salt
,
60
int
iterations
,
quint64
dkLen
)
61
{
62
// https://tools.ietf.org/html/rfc8018#section-5.1
63
64
if
(
algorithm
!=
QCryptographicHash
::
Sha1
65
&&
algorithm
!=
QCryptographicHash
::
Md5
66
) {
67
qWarning
(
"The only supported algorithms for pbkdf1 are SHA-1 and MD5!"
);
68
return
QByteArray
();
69
}
70
71
if
(
salt
.
size
() != 8) {
72
qWarning
(
"The salt must be 8 bytes long!"
);
73
return
QByteArray
();
74
}
75
if
(
iterations
< 1 ||
dkLen
< 1)
76
return
QByteArray
();
77
78
if
(
dkLen
>
quint64
(
QCryptographicHash
::
hashLength
(
algorithm
))) {
79
qWarning
() <<
"Derived key too long:\n"
80
<<
algorithm
<<
"was chosen which produces output of length"
81
<<
QCryptographicHash
::
hashLength
(
algorithm
) <<
"but"
<<
dkLen
82
<<
"was requested."
;
83
return
QByteArray
();
84
}
85
86
QCryptographicHash
hash
(
algorithm
);
87
hash
.
addData
(
data
);
88
hash
.
addData
(
salt
);
89
QByteArray
key
=
hash
.
result
();
90
91
for
(
int
i
= 1;
i
<
iterations
;
i
++) {
92
hash
.
reset
();
93
hash
.
addData
(
key
);
94
key
=
hash
.
result
();
95
}
96
return
key
.
left
(
dkLen
);
97
}
98
99
#
ifdef
USING_OPENSSL30
100
// Copied from QCryptographicHashPrivate
101
static
constexpr
const
char
*
methodToName
(
QCryptographicHash
::
Algorithm
method
)
noexcept
102
{
103
switch
(
method
) {
104
#
define
CASE
(
Enum
,
Name
)
105
case
QCryptographicHash
::
Enum
:
106
return
Name
107
/*end*/
108
CASE
(
Sha1
,
"SHA1"
);
109
CASE
(
Md4
,
"MD4"
);
110
CASE
(
Md5
,
"MD5"
);
111
CASE
(
Sha224
,
"SHA224"
);
112
CASE
(
Sha256
,
"SHA256"
);
113
CASE
(
Sha384
,
"SHA384"
);
114
CASE
(
Sha512
,
"SHA512"
);
115
CASE
(
RealSha3_224
,
"SHA3-224"
);
116
CASE
(
RealSha3_256
,
"SHA3-256"
);
117
CASE
(
RealSha3_384
,
"SHA3-384"
);
118
CASE
(
RealSha3_512
,
"SHA3-512"
);
119
CASE
(
Keccak_224
,
"SHA3-224"
);
120
CASE
(
Keccak_256
,
"SHA3-256"
);
121
CASE
(
Keccak_384
,
"SHA3-384"
);
122
CASE
(
Keccak_512
,
"SHA3-512"
);
123
CASE
(
Blake2b_512
,
"BLAKE2B512"
);
124
CASE
(
Blake2s_256
,
"BLAKE2S256"
);
125
#
undef
CASE
126
default
:
return
nullptr
;
127
}
128
}
129
130
static
QByteArray
opensslDeriveKeyPbkdf2
(
QCryptographicHash
::
Algorithm
algorithm
,
131
const
QByteArray
&
data
,
const
QByteArray
&
salt
,
132
uint64_t
iterations
,
quint64
dkLen
)
133
{
134
EVP_KDF
*
kdf
=
EVP_KDF_fetch
(
nullptr
,
"PBKDF2"
,
nullptr
);
135
136
if
(!
kdf
)
137
return
QByteArray
();
138
139
auto
cleanUpKdf
=
qScopeGuard
([
kdf
] {
140
EVP_KDF_free
(
kdf
);
141
});
142
143
EVP_KDF_CTX
*
ctx
=
EVP_KDF_CTX_new
(
kdf
);
144
145
if
(!
ctx
)
146
return
QByteArray
();
147
148
auto
cleanUpCtx
=
qScopeGuard
([
ctx
] {
149
EVP_KDF_CTX_free
(
ctx
);
150
});
151
152
// Do not enable SP800-132 compliance check, otherwise we will require:
153
// - the iteration count is at least 1000
154
// - the salt length is at least 128 bits
155
// - the derived key length is at least 112 bits
156
// This would be a different behavior from the original implementation.
157
int
checkDisabled
= 1;
158
QList
<
OSSL_PARAM
>
params
;
159
params
.
append
(
OSSL_PARAM_construct_utf8_string
(
OSSL_KDF_PARAM_DIGEST
,
const_cast
<
char
*>(
methodToName
(
algorithm
)), 0));
160
params
.
append
(
OSSL_PARAM_construct_octet_string
(
OSSL_KDF_PARAM_SALT
,
const_cast
<
char
*>(
salt
.
data
()),
salt
.
size
()));
161
params
.
append
(
OSSL_PARAM_construct_octet_string
(
OSSL_KDF_PARAM_PASSWORD
,
const_cast
<
char
*>(
data
.
data
()),
data
.
size
()));
162
params
.
append
(
OSSL_PARAM_construct_uint64
(
OSSL_KDF_PARAM_ITER
, &
iterations
));
163
params
.
append
(
OSSL_PARAM_construct_int
(
OSSL_KDF_PARAM_PKCS5
, &
checkDisabled
));
164
params
.
append
(
OSSL_PARAM_construct_end
());
165
166
if
(
EVP_KDF_CTX_set_params
(
ctx
,
params
.
data
()) <= 0)
167
return
QByteArray
();
168
169
QByteArray
derived
(
dkLen
,
'\0'
);
170
171
if
(!
EVP_KDF_derive
(
ctx
,
reinterpret_cast
<
unsigned
char
*>(
derived
.
data
()),
derived
.
size
(),
nullptr
))
172
return
QByteArray
();
173
174
return
derived
;
175
}
176
#
endif
177
178
/*!
179
\since 5.12
180
181
Derive a key using the PBKDF2-algorithm as defined in
182
\l {RFC 8018, section 5.2}.
183
184
This function takes the \a data and \a salt, and then applies HMAC-X, where
185
the X is \a algorithm, repeatedly. It internally concatenates intermediate
186
results to the final output until at least \a dkLen amount of bytes have
187
been computed and it will execute HMAC-X \a iterations times each time a
188
concatenation is required. The total number of times it will execute HMAC-X
189
depends on \a iterations, \a dkLen and \a algorithm and can be calculated
190
as
191
\c{iterations * ceil(dkLen / QCryptographicHash::hashLength(algorithm))}.
192
193
\sa deriveKeyPbkdf1, QMessageAuthenticationCode, QCryptographicHash
194
*/
195
Q_NETWORK_EXPORT QByteArray
deriveKeyPbkdf2
(
QCryptographicHash
::
Algorithm
algorithm
,
196
const
QByteArray
&
data
,
const
QByteArray
&
salt
,
197
int
iterations
,
quint64
dkLen
)
198
{
199
// The RFC recommends checking that 'dkLen' is not greater than '(2^32 - 1) * hLen'
200
int
hashLen
=
QCryptographicHash
::
hashLength
(
algorithm
);
201
const
quint64
maxLen
=
quint64
(
std
::
numeric_limits
<
quint32
>::
max
() - 1) *
hashLen
;
202
if
(
dkLen
>
maxLen
) {
203
qWarning
().
nospace
() <<
"Derived key too long:\n"
204
<<
algorithm
<<
" was chosen which produces output of length "
205
<<
maxLen
<<
" but "
<<
dkLen
<<
" was requested."
;
206
return
QByteArray
();
207
}
208
209
if
(
iterations
< 1 ||
dkLen
< 1)
210
return
QByteArray
();
211
212
#
ifdef
USING_OPENSSL30
213
if
(
methodToName
(
algorithm
))
214
return
opensslDeriveKeyPbkdf2
(
algorithm
,
data
,
salt
,
iterations
,
dkLen
);
215
#
endif
216
217
// https://tools.ietf.org/html/rfc8018#section-5.2
218
QByteArray
key
;
219
quint32
currentIteration
= 1;
220
QMessageAuthenticationCode
hmac
(
algorithm
,
data
);
221
QByteArray
index
(4,
Qt
::
Uninitialized
);
222
while
(
quint64
(
key
.
size
()) <
dkLen
) {
223
hmac
.
addData
(
salt
);
224
225
qToBigEndian
(
currentIteration
,
index
.
data
());
226
hmac
.
addData
(
index
);
227
228
QByteArray
u
=
hmac
.
result
();
229
hmac
.
reset
();
230
QByteArray
tkey
=
u
;
231
for
(
int
iter
= 1;
iter
<
iterations
;
iter
++) {
232
hmac
.
addData
(
u
);
233
u
=
hmac
.
result
();
234
hmac
.
reset
();
235
std
::
transform
(
tkey
.
cbegin
(),
tkey
.
cend
(),
u
.
cbegin
(),
tkey
.
begin
(),
236
std
::
bit_xor
<
char
>());
237
}
238
key
+=
tkey
;
239
currentIteration
++;
240
}
241
return
key
.
left
(
dkLen
);
242
}
243
}
// namespace QPasswordDigestor
244
QT_END_NAMESPACE
QPasswordDigestor
\inmodule QtNetwork
Definition
qpassworddigestor.cpp:25
qtbase
src
network
ssl
qpassworddigestor.cpp
Generated on
for Qt by
1.14.0