1 | /* |
2 | * Copyright (C) 2003-2007 Justin Karneges <justin@affinix.com> |
3 | * Copyright (C) 2004,2005,2007 Brad Hards <bradh@frogmouth.net> |
4 | * |
5 | * This library is free software; you can redistribute it and/or |
6 | * modify it under the terms of the GNU Lesser General Public |
7 | * License as published by the Free Software Foundation; either |
8 | * version 2.1 of the License, or (at your option) any later version. |
9 | * |
10 | * This library is distributed in the hope that it will be useful, |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | * Lesser General Public License for more details. |
14 | * |
15 | * You should have received a copy of the GNU Lesser General Public |
16 | * License along with this library; if not, write to the Free Software |
17 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | * 02110-1301 USA |
19 | * |
20 | */ |
21 | |
22 | #include "qca_basic.h" |
23 | |
24 | #include "qcaprovider.h" |
25 | |
26 | #include <QMutexLocker> |
27 | #include <QtGlobal> |
28 | |
29 | namespace QCA { |
30 | |
31 | // from qca_core.cpp |
32 | QMutex *global_random_mutex(); |
33 | Random *global_random(); |
34 | Provider::Context *getContext(const QString &type, Provider *p); |
35 | |
36 | // from qca_publickey.cpp |
37 | ProviderList allProviders(); |
38 | Provider *providerForName(const QString &name); |
39 | |
40 | static void mergeList(QStringList *a, const QStringList &b) |
41 | { |
42 | foreach (const QString &s, b) { |
43 | if (!a->contains(str: s)) |
44 | a->append(t: s); |
45 | } |
46 | } |
47 | |
48 | static QStringList get_hash_types(Provider *p) |
49 | { |
50 | QStringList out; |
51 | InfoContext *c = static_cast<InfoContext *>(getContext(QStringLiteral("info" ), p)); |
52 | if (!c) |
53 | return out; |
54 | out = c->supportedHashTypes(); |
55 | delete c; |
56 | return out; |
57 | } |
58 | |
59 | static QStringList get_cipher_types(Provider *p) |
60 | { |
61 | QStringList out; |
62 | InfoContext *c = static_cast<InfoContext *>(getContext(QStringLiteral("info" ), p)); |
63 | if (!c) |
64 | return out; |
65 | out = c->supportedCipherTypes(); |
66 | delete c; |
67 | return out; |
68 | } |
69 | |
70 | static QStringList get_mac_types(Provider *p) |
71 | { |
72 | QStringList out; |
73 | InfoContext *c = static_cast<InfoContext *>(getContext(QStringLiteral("info" ), p)); |
74 | if (!c) |
75 | return out; |
76 | out = c->supportedMACTypes(); |
77 | delete c; |
78 | return out; |
79 | } |
80 | |
81 | static QStringList get_types(QStringList (*get_func)(Provider *p), const QString &provider) |
82 | { |
83 | QStringList out; |
84 | if (!provider.isEmpty()) { |
85 | Provider *p = providerForName(name: provider); |
86 | if (p) |
87 | out = get_func(p); |
88 | } else { |
89 | const ProviderList pl = allProviders(); |
90 | foreach (Provider *p, pl) |
91 | mergeList(a: &out, b: get_func(p)); |
92 | } |
93 | return out; |
94 | } |
95 | |
96 | static QStringList supportedHashTypes(const QString &provider) |
97 | { |
98 | return get_types(get_func: get_hash_types, provider); |
99 | } |
100 | |
101 | static QStringList supportedCipherTypes(const QString &provider) |
102 | { |
103 | return get_types(get_func: get_cipher_types, provider); |
104 | } |
105 | |
106 | static QStringList supportedMACTypes(const QString &provider) |
107 | { |
108 | return get_types(get_func: get_mac_types, provider); |
109 | } |
110 | |
111 | //---------------------------------------------------------------------------- |
112 | // Random |
113 | //---------------------------------------------------------------------------- |
114 | Random::Random(const QString &provider) |
115 | : Algorithm(QStringLiteral("random" ), provider) |
116 | { |
117 | } |
118 | |
119 | Random::Random(const Random &from) |
120 | : Algorithm(from) |
121 | { |
122 | } |
123 | |
124 | Random::~Random() |
125 | { |
126 | } |
127 | |
128 | Random &Random::operator=(const Random &from) |
129 | { |
130 | Algorithm::operator=(from); |
131 | return *this; |
132 | } |
133 | |
134 | uchar Random::nextByte() |
135 | { |
136 | return (uchar)(nextBytes(size: 1)[0]); |
137 | } |
138 | |
139 | SecureArray Random::nextBytes(int size) |
140 | { |
141 | return static_cast<RandomContext *>(context())->nextBytes(size); |
142 | } |
143 | |
144 | uchar Random::randomChar() |
145 | { |
146 | QMutexLocker locker(global_random_mutex()); |
147 | return global_random()->nextByte(); |
148 | } |
149 | |
150 | int Random::randomInt() |
151 | { |
152 | QMutexLocker locker(global_random_mutex()); |
153 | const SecureArray a = global_random()->nextBytes(size: sizeof(int)); |
154 | int x; |
155 | memcpy(dest: &x, src: a.data(), n: a.size()); |
156 | return x; |
157 | } |
158 | |
159 | SecureArray Random::randomArray(int size) |
160 | { |
161 | QMutexLocker locker(global_random_mutex()); |
162 | return global_random()->nextBytes(size); |
163 | } |
164 | |
165 | //---------------------------------------------------------------------------- |
166 | // Hash |
167 | //---------------------------------------------------------------------------- |
168 | Hash::Hash(const QString &type, const QString &provider) |
169 | : Algorithm(type, provider) |
170 | { |
171 | } |
172 | |
173 | Hash::Hash(const Hash &from) |
174 | : Algorithm(from) |
175 | , BufferedComputation(from) |
176 | { |
177 | } |
178 | |
179 | Hash::~Hash() |
180 | { |
181 | } |
182 | |
183 | Hash &Hash::operator=(const Hash &from) |
184 | { |
185 | Algorithm::operator=(from); |
186 | return *this; |
187 | } |
188 | |
189 | QStringList Hash::supportedTypes(const QString &provider) |
190 | { |
191 | return supportedHashTypes(provider); |
192 | } |
193 | |
194 | QString Hash::type() const |
195 | { |
196 | // algorithm type is the same as the hash type |
197 | return Algorithm::type(); |
198 | } |
199 | |
200 | void Hash::clear() |
201 | { |
202 | static_cast<HashContext *>(context())->clear(); |
203 | } |
204 | |
205 | void Hash::update(const MemoryRegion &a) |
206 | { |
207 | static_cast<HashContext *>(context())->update(a); |
208 | } |
209 | |
210 | void Hash::update(const QByteArray &a) |
211 | { |
212 | update(a: MemoryRegion(a)); |
213 | } |
214 | |
215 | void Hash::update(const char *data, int len) |
216 | { |
217 | if (len < 0) |
218 | len = qstrlen(str: data); |
219 | if (len == 0) |
220 | return; |
221 | |
222 | update(a: MemoryRegion(QByteArray::fromRawData(data, size: len))); |
223 | } |
224 | |
225 | // Reworked from KMD5, from KDE's kdelibs |
226 | void Hash::update(QIODevice *file) |
227 | { |
228 | char buffer[1024]; |
229 | int len; |
230 | |
231 | while ((len = file->read(data: reinterpret_cast<char *>(buffer), maxlen: sizeof(buffer))) > 0) |
232 | update(data: buffer, len); |
233 | } |
234 | |
235 | MemoryRegion Hash::final() |
236 | { |
237 | return static_cast<HashContext *>(context())->final(); |
238 | } |
239 | |
240 | MemoryRegion Hash::hash(const MemoryRegion &a) |
241 | { |
242 | return process(a); |
243 | } |
244 | |
245 | QString Hash::hashToString(const MemoryRegion &a) |
246 | { |
247 | return arrayToHex(array: hash(a).toByteArray()); |
248 | } |
249 | |
250 | //---------------------------------------------------------------------------- |
251 | // Cipher |
252 | //---------------------------------------------------------------------------- |
253 | class Cipher::Private |
254 | { |
255 | public: |
256 | QString type; |
257 | Cipher::Mode mode; |
258 | Cipher::Padding pad; |
259 | Direction dir; |
260 | SymmetricKey key; |
261 | InitializationVector iv; |
262 | AuthTag tag; |
263 | |
264 | bool ok, done; |
265 | }; |
266 | |
267 | Cipher::Cipher(const QString &type, |
268 | Mode mode, |
269 | Padding pad, |
270 | Direction dir, |
271 | const SymmetricKey &key, |
272 | const InitializationVector &iv, |
273 | const QString &provider) |
274 | : Algorithm(withAlgorithms(cipherType: type, modeType: mode, paddingType: pad), provider) |
275 | { |
276 | d = new Private; |
277 | d->type = type; |
278 | d->mode = mode; |
279 | d->pad = pad; |
280 | if (!key.isEmpty()) |
281 | setup(dir, key, iv); |
282 | } |
283 | |
284 | Cipher::Cipher(const QString &type, |
285 | Cipher::Mode mode, |
286 | Cipher::Padding pad, |
287 | Direction dir, |
288 | const SymmetricKey &key, |
289 | const InitializationVector &iv, |
290 | const AuthTag &tag, |
291 | const QString &provider) |
292 | : Algorithm(withAlgorithms(cipherType: type, modeType: mode, paddingType: pad), provider) |
293 | { |
294 | d = new Private; |
295 | d->type = type; |
296 | d->mode = mode; |
297 | d->pad = pad; |
298 | d->tag = tag; |
299 | if (!key.isEmpty()) |
300 | setup(dir, key, iv, tag); |
301 | } |
302 | |
303 | Cipher::Cipher(const Cipher &from) |
304 | : Algorithm(from) |
305 | , Filter(from) |
306 | { |
307 | d = new Private(*from.d); |
308 | } |
309 | |
310 | Cipher::~Cipher() |
311 | { |
312 | delete d; |
313 | } |
314 | |
315 | Cipher &Cipher::operator=(const Cipher &from) |
316 | { |
317 | Algorithm::operator=(from); |
318 | *d = *from.d; |
319 | return *this; |
320 | } |
321 | |
322 | QStringList Cipher::supportedTypes(const QString &provider) |
323 | { |
324 | return supportedCipherTypes(provider); |
325 | } |
326 | |
327 | QString Cipher::type() const |
328 | { |
329 | return d->type; |
330 | } |
331 | |
332 | Cipher::Mode Cipher::mode() const |
333 | { |
334 | return d->mode; |
335 | } |
336 | |
337 | Cipher::Padding Cipher::padding() const |
338 | { |
339 | return d->pad; |
340 | } |
341 | |
342 | Direction Cipher::direction() const |
343 | { |
344 | return d->dir; |
345 | } |
346 | |
347 | KeyLength Cipher::keyLength() const |
348 | { |
349 | return static_cast<const CipherContext *>(context())->keyLength(); |
350 | } |
351 | |
352 | bool Cipher::validKeyLength(int n) const |
353 | { |
354 | const KeyLength len = keyLength(); |
355 | return ((n >= len.minimum()) && (n <= len.maximum()) && (n % len.multiple() == 0)); |
356 | } |
357 | |
358 | int Cipher::blockSize() const |
359 | { |
360 | return static_cast<const CipherContext *>(context())->blockSize(); |
361 | } |
362 | |
363 | AuthTag Cipher::tag() const |
364 | { |
365 | return static_cast<const CipherContext *>(context())->tag(); |
366 | } |
367 | |
368 | void Cipher::clear() |
369 | { |
370 | d->done = false; |
371 | static_cast<CipherContext *>(context())->setup(dir: d->dir, key: d->key, iv: d->iv, tag: d->tag); |
372 | } |
373 | |
374 | MemoryRegion Cipher::update(const MemoryRegion &a) |
375 | { |
376 | SecureArray out; |
377 | if (d->done) |
378 | return out; |
379 | d->ok = static_cast<CipherContext *>(context())->update(in: a, out: &out); |
380 | return out; |
381 | } |
382 | |
383 | MemoryRegion Cipher::final() |
384 | { |
385 | SecureArray out; |
386 | if (d->done) |
387 | return out; |
388 | d->done = true; |
389 | d->ok = static_cast<CipherContext *>(context())->final(out: &out); |
390 | return out; |
391 | } |
392 | |
393 | bool Cipher::ok() const |
394 | { |
395 | return d->ok; |
396 | } |
397 | |
398 | void Cipher::setup(Direction dir, const SymmetricKey &key, const InitializationVector &iv) |
399 | { |
400 | setup(dir, key, iv, tag: AuthTag()); |
401 | } |
402 | |
403 | void Cipher::setup(Direction dir, const SymmetricKey &key, const InitializationVector &iv, const AuthTag &tag) |
404 | { |
405 | d->dir = dir; |
406 | d->key = key; |
407 | d->iv = iv; |
408 | d->tag = tag; |
409 | clear(); |
410 | } |
411 | |
412 | QString Cipher::withAlgorithms(const QString &cipherType, Mode modeType, Padding paddingType) |
413 | { |
414 | QString mode; |
415 | switch (modeType) { |
416 | case CBC: |
417 | mode = QStringLiteral("cbc" ); |
418 | break; |
419 | case CFB: |
420 | mode = QStringLiteral("cfb" ); |
421 | break; |
422 | case OFB: |
423 | mode = QStringLiteral("ofb" ); |
424 | break; |
425 | case ECB: |
426 | mode = QStringLiteral("ecb" ); |
427 | break; |
428 | case CTR: |
429 | mode = QStringLiteral("ctr" ); |
430 | break; |
431 | case GCM: |
432 | mode = QStringLiteral("gcm" ); |
433 | break; |
434 | case CCM: |
435 | mode = QStringLiteral("ccm" ); |
436 | break; |
437 | default: |
438 | Q_ASSERT(0); |
439 | } |
440 | |
441 | // do the default |
442 | if (paddingType == DefaultPadding) { |
443 | // logic from Botan |
444 | if (modeType == CBC) |
445 | paddingType = PKCS7; |
446 | else |
447 | paddingType = NoPadding; |
448 | } |
449 | |
450 | QString pad; |
451 | if (paddingType == NoPadding) |
452 | pad = QLatin1String("" ); |
453 | else |
454 | pad = QStringLiteral("pkcs7" ); |
455 | |
456 | QString result = cipherType + QLatin1Char('-') + mode; |
457 | if (!pad.isEmpty()) |
458 | result += QStringLiteral("-" ) + pad; |
459 | |
460 | return result; |
461 | } |
462 | |
463 | //---------------------------------------------------------------------------- |
464 | // MessageAuthenticationCode |
465 | //---------------------------------------------------------------------------- |
466 | class MessageAuthenticationCode::Private |
467 | { |
468 | public: |
469 | SymmetricKey key; |
470 | |
471 | bool done; |
472 | MemoryRegion buf; |
473 | }; |
474 | |
475 | MessageAuthenticationCode::MessageAuthenticationCode(const QString &type, |
476 | const SymmetricKey &key, |
477 | const QString &provider) |
478 | : Algorithm(type, provider) |
479 | { |
480 | d = new Private; |
481 | setup(key); |
482 | } |
483 | |
484 | MessageAuthenticationCode::MessageAuthenticationCode(const MessageAuthenticationCode &from) |
485 | : Algorithm(from) |
486 | , BufferedComputation(from) |
487 | { |
488 | d = new Private(*from.d); |
489 | } |
490 | |
491 | MessageAuthenticationCode::~MessageAuthenticationCode() |
492 | { |
493 | delete d; |
494 | } |
495 | |
496 | MessageAuthenticationCode &MessageAuthenticationCode::operator=(const MessageAuthenticationCode &from) |
497 | { |
498 | Algorithm::operator=(from); |
499 | *d = *from.d; |
500 | return *this; |
501 | } |
502 | |
503 | QStringList MessageAuthenticationCode::supportedTypes(const QString &provider) |
504 | { |
505 | return supportedMACTypes(provider); |
506 | } |
507 | |
508 | QString MessageAuthenticationCode::type() const |
509 | { |
510 | // algorithm type is the same as the mac type |
511 | return Algorithm::type(); |
512 | } |
513 | |
514 | KeyLength MessageAuthenticationCode::keyLength() const |
515 | { |
516 | return static_cast<const MACContext *>(context())->keyLength(); |
517 | } |
518 | |
519 | bool MessageAuthenticationCode::validKeyLength(int n) const |
520 | { |
521 | const KeyLength len = keyLength(); |
522 | return ((n >= len.minimum()) && (n <= len.maximum()) && (n % len.multiple() == 0)); |
523 | } |
524 | |
525 | void MessageAuthenticationCode::clear() |
526 | { |
527 | d->done = false; |
528 | static_cast<MACContext *>(context())->setup(d->key); |
529 | } |
530 | |
531 | void MessageAuthenticationCode::update(const MemoryRegion &a) |
532 | { |
533 | if (d->done) |
534 | return; |
535 | static_cast<MACContext *>(context())->update(in: a); |
536 | } |
537 | |
538 | MemoryRegion MessageAuthenticationCode::final() |
539 | { |
540 | if (!d->done) { |
541 | d->done = true; |
542 | static_cast<MACContext *>(context())->final(out: &d->buf); |
543 | } |
544 | return d->buf; |
545 | } |
546 | |
547 | void MessageAuthenticationCode::setup(const SymmetricKey &key) |
548 | { |
549 | d->key = key; |
550 | clear(); |
551 | } |
552 | |
553 | //---------------------------------------------------------------------------- |
554 | // Key Derivation Function |
555 | //---------------------------------------------------------------------------- |
556 | KeyDerivationFunction::KeyDerivationFunction(const QString &type, const QString &provider) |
557 | : Algorithm(type, provider) |
558 | { |
559 | } |
560 | |
561 | KeyDerivationFunction::KeyDerivationFunction(const KeyDerivationFunction &from) |
562 | : Algorithm(from) |
563 | { |
564 | } |
565 | |
566 | KeyDerivationFunction::~KeyDerivationFunction() |
567 | { |
568 | } |
569 | |
570 | KeyDerivationFunction &KeyDerivationFunction::operator=(const KeyDerivationFunction &from) |
571 | { |
572 | Algorithm::operator=(from); |
573 | return *this; |
574 | } |
575 | |
576 | SymmetricKey KeyDerivationFunction::makeKey(const SecureArray &secret, |
577 | const InitializationVector &salt, |
578 | unsigned int keyLength, |
579 | unsigned int iterationCount) |
580 | { |
581 | return static_cast<KDFContext *>(context())->makeKey(secret, salt, keyLength, iterationCount); |
582 | } |
583 | |
584 | SymmetricKey KeyDerivationFunction::makeKey(const SecureArray &secret, |
585 | const InitializationVector &salt, |
586 | unsigned int keyLength, |
587 | int msecInterval, |
588 | unsigned int *iterationCount) |
589 | { |
590 | return static_cast<KDFContext *>(context())->makeKey(secret, salt, keyLength, msecInterval, iterationCount); |
591 | } |
592 | |
593 | QString KeyDerivationFunction::withAlgorithm(const QString &kdfType, const QString &algType) |
594 | { |
595 | return (kdfType + QLatin1Char('(') + algType + QLatin1Char(')')); |
596 | } |
597 | |
598 | //---------------------------------------------------------------------------- |
599 | // HKDF |
600 | //---------------------------------------------------------------------------- |
601 | HKDF::HKDF(const QString &algorithm, const QString &provider) |
602 | : Algorithm(QStringLiteral("hkdf(" ) + algorithm + QLatin1Char(')'), provider) |
603 | { |
604 | } |
605 | |
606 | HKDF::HKDF(const HKDF &from) |
607 | : Algorithm(from) |
608 | { |
609 | } |
610 | |
611 | HKDF::~HKDF() |
612 | { |
613 | } |
614 | |
615 | HKDF &HKDF::operator=(const HKDF &from) |
616 | { |
617 | Algorithm::operator=(from); |
618 | return *this; |
619 | } |
620 | |
621 | SymmetricKey HKDF::makeKey(const SecureArray &secret, |
622 | const InitializationVector &salt, |
623 | const InitializationVector &info, |
624 | unsigned int keyLength) |
625 | { |
626 | return static_cast<HKDFContext *>(context())->makeKey(secret, salt, info, keyLength); |
627 | } |
628 | |
629 | } |
630 | |