1/*
2 * Copyright (C) 2006 Brad Hards <bradh@frogmouth.net>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
17 *
18 */
19#include "hasht.h"
20#include "nss.h"
21#include "pk11func.h"
22
23#include <QtCrypto>
24
25#include <QDebug>
26#include <QStringList>
27#include <QtPlugin>
28
29//-----------------------------------------------------------
30class nssHashContext : public QCA::HashContext
31{
32 Q_OBJECT
33public:
34 nssHashContext(QCA::Provider *p, const QString &type)
35 : QCA::HashContext(p, type)
36 {
37 SECStatus s;
38
39 NSS_NoDB_Init(configdir: ".");
40
41 m_status = 0;
42
43 /* Get a slot to use for the crypto operations */
44 m_slot = PK11_GetInternalKeySlot();
45 if (!m_slot) {
46 qDebug() << "GetInternalKeySlot failed";
47 m_status = 1;
48 return;
49 }
50
51 if (QLatin1String("md2") == type) {
52 m_hashAlgo = SEC_OID_MD2;
53 } else if (QLatin1String("md5") == type) {
54 m_hashAlgo = SEC_OID_MD5;
55 } else if (QLatin1String("sha1") == type) {
56 m_hashAlgo = SEC_OID_SHA1;
57 } else if (QLatin1String("sha256") == type) {
58 m_hashAlgo = SEC_OID_SHA256;
59 } else if (QLatin1String("sha384") == type) {
60 m_hashAlgo = SEC_OID_SHA384;
61 } else if (QLatin1String("sha512") == type) {
62 m_hashAlgo = SEC_OID_SHA512;
63 } else {
64 qDebug() << "Unknown provider type: " << type;
65 return; /* this will probably cause a segfault... */
66 }
67
68 m_context = PK11_CreateDigestContext(hashAlg: m_hashAlgo);
69 if (!m_context) {
70 qDebug() << "CreateDigestContext failed";
71 return;
72 }
73
74 s = PK11_DigestBegin(cx: m_context);
75 if (s != SECSuccess) {
76 qDebug() << "DigestBegin failed";
77 return;
78 }
79 }
80
81 ~nssHashContext() override
82 {
83 PK11_DestroyContext(context: m_context, PR_TRUE);
84 if (m_slot)
85 PK11_FreeSlot(slot: m_slot);
86 }
87
88 Context *clone() const override
89 {
90 return new nssHashContext(provider(), type());
91 }
92
93 void clear() override
94 {
95 SECStatus s;
96
97 PK11_DestroyContext(context: m_context, PR_TRUE);
98
99 m_context = PK11_CreateDigestContext(hashAlg: m_hashAlgo);
100 if (!m_context) {
101 qDebug() << "CreateDigestContext failed";
102 return;
103 }
104
105 s = PK11_DigestBegin(cx: m_context);
106 if (s != SECSuccess) {
107 qDebug() << "DigestBegin failed";
108 return;
109 }
110 }
111
112 void update(const QCA::MemoryRegion &a) override
113 {
114 PK11_DigestOp(context: m_context, in: (const unsigned char *)a.data(), len: a.size());
115 }
116
117 QCA::MemoryRegion final() override
118 {
119 unsigned int len = 0;
120 QCA::SecureArray a(64);
121 PK11_DigestFinal(context: m_context, data: (unsigned char *)a.data(), outLen: &len, length: a.size());
122 a.resize(size: len);
123 return a;
124 }
125
126private:
127 PK11SlotInfo *m_slot;
128 int m_status;
129 PK11Context *m_context;
130 SECOidTag m_hashAlgo;
131};
132
133//-----------------------------------------------------------
134class nssHmacContext : public QCA::MACContext
135{
136 Q_OBJECT
137public:
138 nssHmacContext(QCA::Provider *p, const QString &type)
139 : QCA::MACContext(p, type)
140 {
141 NSS_NoDB_Init(configdir: ".");
142
143 m_context = nullptr;
144 m_status = 0;
145
146 /* Get a slot to use for the crypto operations */
147 m_slot = PK11_GetInternalKeySlot();
148 if (!m_slot) {
149 qDebug() << "GetInternalKeySlot failed";
150 m_status = 1;
151 return;
152 }
153
154 if (QLatin1String("hmac(md5)") == type) {
155 m_macAlgo = CKM_MD5_HMAC;
156 } else if (QLatin1String("hmac(sha1)") == type) {
157 m_macAlgo = CKM_SHA_1_HMAC;
158 } else if (QLatin1String("hmac(sha256)") == type) {
159 m_macAlgo = CKM_SHA256_HMAC;
160 } else if (QLatin1String("hmac(sha384)") == type) {
161 m_macAlgo = CKM_SHA384_HMAC;
162 } else if (QLatin1String("hmac(sha512)") == type) {
163 m_macAlgo = CKM_SHA512_HMAC;
164 } else if (QLatin1String("hmac(ripemd160)") == type) {
165 m_macAlgo = CKM_RIPEMD160_HMAC;
166 } else {
167 qDebug() << "Unknown provider type: " << type;
168 return; /* this will probably cause a segfault... */
169 }
170 }
171
172 ~nssHmacContext() override
173 {
174 if (m_context)
175 PK11_DestroyContext(context: m_context, PR_TRUE);
176 if (m_slot)
177 PK11_FreeSlot(slot: m_slot);
178 }
179
180 Context *clone() const override
181 {
182 return new nssHmacContext(provider(), type());
183 }
184
185 void clear()
186 {
187 PK11_DestroyContext(context: m_context, PR_TRUE);
188
189 SECItem noParams;
190 noParams.data = nullptr;
191 noParams.len = 0;
192
193 m_context = PK11_CreateContextBySymKey(type: m_macAlgo, CKA_SIGN, symKey: m_nssKey, param: &noParams);
194 if (!m_context) {
195 qDebug() << "CreateContextBySymKey failed";
196 return;
197 }
198
199 SECStatus s = PK11_DigestBegin(cx: m_context);
200 if (s != SECSuccess) {
201 qDebug() << "DigestBegin failed";
202 return;
203 }
204 }
205
206 QCA::KeyLength keyLength() const override
207 {
208 return anyKeyLength();
209 }
210
211 void setup(const QCA::SymmetricKey &key) override
212 {
213 /* turn the raw key into a SECItem */
214 SECItem keyItem;
215 keyItem.data = (unsigned char *)key.data();
216 keyItem.len = key.size();
217
218 m_nssKey = PK11_ImportSymKey(slot: m_slot, type: m_macAlgo, origin: PK11_OriginUnwrap, CKA_SIGN, key: &keyItem, wincx: nullptr);
219
220 SECItem noParams;
221 noParams.data = nullptr;
222 noParams.len = 0;
223
224 m_context = PK11_CreateContextBySymKey(type: m_macAlgo, CKA_SIGN, symKey: m_nssKey, param: &noParams);
225 if (!m_context) {
226 qDebug() << "CreateContextBySymKey failed";
227 return;
228 }
229
230 SECStatus s = PK11_DigestBegin(cx: m_context);
231 if (s != SECSuccess) {
232 qDebug() << "DigestBegin failed";
233 return;
234 }
235 }
236
237 void update(const QCA::MemoryRegion &a) override
238 {
239 PK11_DigestOp(context: m_context, in: (const unsigned char *)a.data(), len: a.size());
240 }
241
242 void final(QCA::MemoryRegion *out) override
243 {
244 // NSS doesn't appear to be able to tell us how big the digest will
245 // be for a given algorithm until after we finalise it, so we work
246 // around the problem a bit.
247 QCA::SecureArray sa(HASH_LENGTH_MAX, 0); // assume the biggest hash size we know
248 unsigned int len = 0;
249 PK11_DigestFinal(context: m_context, data: (unsigned char *)sa.data(), outLen: &len, length: sa.size());
250 sa.resize(size: len); // and fix it up later
251 *out = sa;
252 }
253
254private:
255 PK11SlotInfo *m_slot;
256 int m_status;
257 PK11Context *m_context;
258 CK_MECHANISM_TYPE m_macAlgo;
259 PK11SymKey *m_nssKey;
260};
261
262//-----------------------------------------------------------
263class nssCipherContext : public QCA::CipherContext
264{
265 Q_OBJECT
266public:
267 nssCipherContext(QCA::Provider *p, const QString &type)
268 : QCA::CipherContext(p, type)
269 {
270 NSS_NoDB_Init(configdir: ".");
271
272 if (QLatin1String("aes128-ecb") == type) {
273 m_cipherMechanism = CKM_AES_ECB;
274 } else if (QLatin1String("aes128-cbc") == type) {
275 m_cipherMechanism = CKM_AES_CBC;
276 } else if (QLatin1String("des-ecb") == type) {
277 m_cipherMechanism = CKM_DES_ECB;
278 } else if (QLatin1String("des-cbc") == type) {
279 m_cipherMechanism = CKM_DES_CBC;
280 } else if (QLatin1String("des-cbc-pkcs7") == type) {
281 m_cipherMechanism = CKM_DES_CBC_PAD;
282 } else if (QLatin1String("tripledes-ecb") == type) {
283 m_cipherMechanism = CKM_DES3_ECB;
284 } else {
285 qDebug() << "Unknown provider type: " << type;
286 return; /* this will probably cause a segfault... */
287 }
288 }
289
290 ~nssCipherContext() override
291 {
292 }
293
294 void setup(QCA::Direction dir,
295 const QCA::SymmetricKey &key,
296 const QCA::InitializationVector &iv,
297 const QCA::AuthTag &tag) override
298 {
299 Q_UNUSED(tag);
300 /* Get a slot to use for the crypto operations */
301 m_slot = PK11_GetBestSlot(type: m_cipherMechanism, wincx: nullptr);
302 if (!m_slot) {
303 qDebug() << "GetBestSlot failed";
304 return;
305 }
306
307 /* turn the raw key into a SECItem */
308 SECItem keyItem;
309 keyItem.data = (unsigned char *)key.data();
310 keyItem.len = key.size();
311
312 if (QCA::Encode == dir) {
313 m_nssKey = PK11_ImportSymKey(slot: m_slot, type: m_cipherMechanism, origin: PK11_OriginUnwrap, CKA_ENCRYPT, key: &keyItem, wincx: nullptr);
314 } else {
315 // decryption
316 m_nssKey = PK11_ImportSymKey(slot: m_slot, type: m_cipherMechanism, origin: PK11_OriginUnwrap, CKA_DECRYPT, key: &keyItem, wincx: nullptr);
317 }
318
319 SECItem ivItem;
320 ivItem.data = (unsigned char *)iv.data();
321 ivItem.len = iv.size();
322
323 m_params = PK11_ParamFromIV(type: m_cipherMechanism, iv: &ivItem);
324
325 if (QCA::Encode == dir) {
326 m_context = PK11_CreateContextBySymKey(type: m_cipherMechanism, CKA_ENCRYPT, symKey: m_nssKey, param: m_params);
327 } else {
328 // decryption
329 m_context = PK11_CreateContextBySymKey(type: m_cipherMechanism, CKA_DECRYPT, symKey: m_nssKey, param: m_params);
330 }
331
332 if (!m_context) {
333 qDebug() << "CreateContextBySymKey failed";
334 return;
335 }
336 }
337
338 QCA::Provider::Context *clone() const override
339 {
340 return new nssCipherContext(*this);
341 }
342
343 int blockSize() const override
344 {
345 return PK11_GetBlockSize(type: m_cipherMechanism, params: m_params);
346 }
347
348 QCA::AuthTag tag() const override
349 {
350 // For future implementation
351 return QCA::AuthTag();
352 }
353
354 bool update(const QCA::SecureArray &in, QCA::SecureArray *out) override
355 {
356 out->resize(size: in.size() + blockSize());
357 int resultLength;
358
359 PK11_CipherOp(
360 context: m_context, out: (unsigned char *)out->data(), outlen: &resultLength, maxout: out->size(), in: (unsigned char *)in.data(), inlen: in.size());
361 out->resize(size: resultLength);
362
363 return true;
364 }
365
366 bool final(QCA::SecureArray *out) override
367 {
368 out->resize(size: blockSize());
369 unsigned int resultLength;
370
371 PK11_DigestFinal(context: m_context, data: (unsigned char *)out->data(), outLen: &resultLength, length: out->size());
372 out->resize(size: resultLength);
373
374 return true;
375 }
376
377 QCA::KeyLength keyLength() const override
378 {
379 int min = 0;
380 int max = 0;
381 int multiple = 0;
382
383 switch (m_cipherMechanism) {
384 case CKM_AES_ECB:
385 case CKM_AES_CBC:
386 min = max = 16;
387 multiple = 1;
388 break;
389
390 case CKM_DES_ECB:
391 case CKM_DES_CBC:
392 case CKM_DES_CBC_PAD:
393 min = max = 8;
394 multiple = 1;
395 break;
396
397 case CKM_DES3_ECB:
398 min = 16;
399 max = 24;
400 multiple = 1;
401 break;
402 }
403
404 return QCA::KeyLength(min, max, multiple);
405 }
406
407private:
408 PK11SymKey *m_nssKey;
409 CK_MECHANISM_TYPE m_cipherMechanism;
410 PK11SlotInfo *m_slot;
411 PK11Context *m_context;
412 SECItem *m_params;
413};
414
415//==========================================================
416class nssProvider : public QCA::Provider
417{
418public:
419 void init() override
420 {
421 }
422
423 ~nssProvider() override
424 {
425 }
426
427 int qcaVersion() const override
428 {
429 return QCA_VERSION;
430 }
431
432 QString name() const override
433 {
434 return QStringLiteral("qca-nss");
435 }
436
437 QStringList features() const override
438 {
439 QStringList list;
440
441 list += QStringLiteral("md2");
442 list += QStringLiteral("md5");
443 list += QStringLiteral("sha1");
444 list += QStringLiteral("sha256");
445 list += QStringLiteral("sha384");
446 list += QStringLiteral("sha512");
447
448 list += QStringLiteral("hmac(md5)");
449 list += QStringLiteral("hmac(sha1)");
450 list += QStringLiteral("hmac(sha256)");
451 list += QStringLiteral("hmac(sha384)");
452 list += QStringLiteral("hmac(sha512)");
453 // appears to not be implemented in NSS yet
454 // list += QStringLiteral("hmac(ripemd160)");
455
456 list += QStringLiteral("aes128-ecb");
457 list += QStringLiteral("aes128-cbc");
458 list += QStringLiteral("des-ecb");
459 list += QStringLiteral("des-cbc");
460 list += QStringLiteral("des-cbc-pkcs7");
461 list += QStringLiteral("tripledes-ecb");
462
463 return list;
464 }
465
466 Context *createContext(const QString &type) override
467 {
468 if (type == QLatin1String("md2"))
469 return new nssHashContext(this, type);
470 if (type == QLatin1String("md5"))
471 return new nssHashContext(this, type);
472 if (type == QLatin1String("sha1"))
473 return new nssHashContext(this, type);
474 if (type == QLatin1String("sha256"))
475 return new nssHashContext(this, type);
476 if (type == QLatin1String("sha384"))
477 return new nssHashContext(this, type);
478 if (type == QLatin1String("sha512"))
479 return new nssHashContext(this, type);
480
481 if (type == QLatin1String("hmac(md5)"))
482 return new nssHmacContext(this, type);
483 if (type == QLatin1String("hmac(sha1)"))
484 return new nssHmacContext(this, type);
485 if (type == QLatin1String("hmac(sha256)"))
486 return new nssHmacContext(this, type);
487 if (type == QLatin1String("hmac(sha384)"))
488 return new nssHmacContext(this, type);
489 if (type == QLatin1String("hmac(sha512)"))
490 return new nssHmacContext(this, type);
491 if (type == QLatin1String("hmac(ripemd160)"))
492 return new nssHmacContext(this, type);
493
494 if (type == QLatin1String("aes128-ecb"))
495 return new nssCipherContext(this, type);
496 if (type == QLatin1String("aes128-cbc"))
497 return new nssCipherContext(this, type);
498 if (type == QLatin1String("des-ecb"))
499 return new nssCipherContext(this, type);
500 if (type == QLatin1String("des-cbc"))
501 return new nssCipherContext(this, type);
502 if (type == QLatin1String("des-cbc-pkcs7"))
503 return new nssCipherContext(this, type);
504 if (type == QLatin1String("tripledes-ecb"))
505 return new nssCipherContext(this, type);
506 else
507 return nullptr;
508 }
509};
510
511class nssPlugin : public QObject, public QCAPlugin
512{
513 Q_OBJECT
514 Q_PLUGIN_METADATA(IID "com.affinix.qca.Plugin/1.0")
515 Q_INTERFACES(QCAPlugin)
516public:
517 QCA::Provider *createProvider() override
518 {
519 return new nssProvider;
520 }
521};
522
523#include "qca-nss.moc"
524

source code of qca/plugins/qca-nss/qca-nss.cpp