4 Copyright (C) 2001,2002 the KPGP authors
5 See file AUTHORS.kpgp for details
7 This file is part of KPGP, the KDE PGP/GnuPG support library.
9 KPGP is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software Foundation,
16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
21 #include "kpgp_debug.h"
22 #include <KLocalizedString>
31 #include <string.h> /* strncmp */
39 // determine the version of gpg (the method is equivalent to gpgme's method)
40 runGpg("--version", 0);
41 int eol
= output
.indexOf('\n');
43 int pos
= output
.lastIndexOf(' ', eol
- 1);
45 mVersion
= output
.mid(pos
+ 1, eol
- pos
- 1);
46 qCDebug(KPGP_LOG
) << "found GnuPG" << mVersion
;
56 BaseG::encrypt(Block
&block
, const KeyIDList
&recipients
)
58 return encsign(block
, recipients
, 0);
62 BaseG::clearsign(Block
&block
, const char *passphrase
)
64 return encsign(block
, KeyIDList(), passphrase
);
68 BaseG::encsign(Block
&block
, const KeyIDList
&recipients
,
69 const char *passphrase
)
74 if (!recipients
.isEmpty() && passphrase
!= 0) {
75 cmd
= "--batch --armor --sign --encrypt --textmode";
76 } else if (!recipients
.isEmpty()) {
77 cmd
= "--batch --armor --encrypt --textmode";
78 } else if (passphrase
!= 0) {
79 cmd
= "--batch --escape-from --clearsign";
81 qCDebug(KPGP_LOG
) << "kpgpbase: Neither recipients nor passphrase specified.";
85 if (passphrase
!= 0) {
89 if (!recipients
.isEmpty()) {
90 cmd
+= " --set-filename stdin";
92 QByteArray pgpUser
= Module::getKpgp()->user();
93 if (Module::getKpgp()->encryptToSelf() && !pgpUser
.isEmpty()) {
98 for (KeyIDList::ConstIterator it
= recipients
.begin();
99 it
!= recipients
.end(); ++it
) {
106 input
= block
.text();
107 exitStatus
= runGpg(cmd
.data(), passphrase
);
108 if (!output
.isEmpty()) {
109 block
.setProcessedText(output
);
111 block
.setError(error
);
113 if (exitStatus
!= 0) {
114 // this error message is later hopefully overwritten
115 errMsg
= i18n("Unknown error.");
120 // #### FIXME: As we check the keys ourselves the following problems
121 // shouldn't occur. Therefore I don't handle them for now.
123 if (!recipients
.isEmpty()) {
126 unsigned int num
= 0;
127 QByteArray badkeys
= "";
129 // gpg: 0x12345678: skipped: public key not found
130 // gpg: 0x12345678: skipped: public key is disabled
131 // gpg: 0x12345678: skipped: unusable public key
132 // (expired or revoked key)
133 // gpg: 23456789: no info to calculate a trust probability
134 // (untrusted key, 23456789 is the key Id of the encryption sub key)
135 while ((index
= error
.indexOf("skipped: ", index
)) != -1) {
137 index
= error
.indexOf('\'', index
);
138 int index2
= error
.indexOf('\'', index
+ 1);
139 badkeys
+= error
.mid(index
, index2
- index
+ 1) + ", ";
144 if (num
== recipients
.count())
145 errMsg
= i18n("Could not find public keys matching the userid(s)\n"
147 "the message is not encrypted.",
150 errMsg
= i18n("Could not find public keys matching the userid(s)\n"
152 "these persons will not be able to read the message.",
154 status
|= MISSINGKEY
;
159 if (passphrase
!= 0) {
160 // Example 1 (bad passphrase, clearsign only):
161 // gpg: skipped `0x12345678': bad passphrase
162 // gpg: [stdin]: clearsign failed: bad passphrase
163 // Example 2 (bad passphrase, sign & encrypt):
164 // gpg: skipped `0x12345678': bad passphrase
165 // gpg: [stdin]: sign+encrypt failed: bad passphrase
166 // Example 3 (unusable secret key, clearsign only):
167 // gpg: skipped `0x12345678': unusable secret key
168 // gpg: [stdin]: clearsign failed: unusable secret key
169 // Example 4 (unusable secret key, sign & encrypt):
170 // gpg: skipped `0xAC0EB35D': unusable secret key
171 // gpg: [stdin]: sign+encrypt failed: unusable secret key
172 if (error
.contains("bad passphrase")) {
173 errMsg
= i18n("Signing failed because the passphrase is wrong.");
175 status
|= ERR_SIGNING
;
177 } else if (error
.contains("unusable secret key")) {
178 errMsg
= i18n("Signing failed because your secret key is unusable.");
179 status
|= ERR_SIGNING
;
181 } else if (!(status
& ERROR
)) {
182 //qCDebug(KPGP_LOG) <<"Base: Good Passphrase!";
187 //qCDebug(KPGP_LOG) <<"status =" << status;
188 block
.setStatus(status
);
193 BaseG::decrypt(Block
&block
, const char *passphrase
)
199 input
= block
.text();
200 exitStatus
= runGpg("--batch --decrypt", passphrase
);
201 if (!output
.isEmpty() && (!error
.contains("gpg: quoted printable"))) {
202 block
.setProcessedText(output
);
204 block
.setError(error
);
206 if (exitStatus
== -1) {
207 errMsg
= i18n("Error running gpg");
209 block
.setStatus(status
);
213 // Example 1 (good passphrase, decryption successful):
214 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
215 // "Foo Bar <foo@bar.xyz>"
217 // Example 2 (bad passphrase):
218 // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
219 // "Foo Bar <foo@bar.xyz>"
220 // gpg: public key decryption failed: bad passphrase
221 // gpg: decryption failed: secret key not available
223 // Example 3 (no secret key available):
224 // gpg: encrypted with RSA key, ID 12345678
225 // gpg: decryption failed: secret key not available
227 // Example 4 (good passphrase for second key, decryption successful):
228 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
229 // "Foo Bar (work) <foo@bar.xyz>"
230 // gpg: public key decryption failed: bad passphrase
231 // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
232 // "Foo Bar (home) <foo@bar.xyz>"
234 // Example 5: passphrase dialog cancelled by user
235 // gpg: cancelled by user
236 // gpg: encrypted with 2048-bit ELG key, ID XXXXXXXX, created 2006-11-16
237 // "Foo Bar <foobar@foo.org>"
238 // gpg: public key decryption failed: General error [..]
239 if (error
.contains("gpg: encrypted with")) {
240 //qCDebug(KPGP_LOG) <<"kpgpbase: message is encrypted";
242 if (error
.contains("\ngpg: decryption failed")) {
243 if ((index
= error
.indexOf("bad passphrase")) != -1) {
244 if (passphrase
!= 0) {
245 errMsg
= i18n("Bad passphrase; could not decrypt.");
246 qCDebug(KPGP_LOG
) << "Base: passphrase is bad";
250 // Search backwards the user ID of the needed key
251 index2
= error
.lastIndexOf('"', index
) - 1;
252 index
= error
.lastIndexOf(" \"", index2
) + 7;
253 // The conversion from UTF8 is necessary because gpg stores and
254 // prints user IDs in UTF8
255 block
.setRequiredUserId(QString::fromUtf8(error
.mid(index
, index2
- index
+ 1)));
256 qCDebug(KPGP_LOG
) << "Base: key needed is \"" << block
.requiredUserId() << "\"!";
258 } else if (error
.contains("secret key not available")) {
259 // no secret key fitting this message
260 status
|= NO_SEC_KEY
;
262 errMsg
= i18n("You do not have the secret key needed to decrypt this message.");
263 qCDebug(KPGP_LOG
) << "Base: no secret key for this message";
264 } else if (error
.contains("cancelled by user")) {
267 errMsg
= i18n("The passphrase dialog was cancelled.");
268 qCDebug(KPGP_LOG
) << errMsg
;
273 // ##### FIXME: This information is anyway currently not used
274 // I'll change it to always determine the recipients.
275 index
= error
.indexOf("can only be read by:");
277 index
= error
.indexOf('\n', index
);
278 int end
= error
.indexOf("\n\n", index
);
281 while ((index2
= error
.indexOf('\n', index
+ 1)) <= end
) {
282 QByteArray item
= error
.mid(index
+ 1, index2
- index
- 1);
284 mRecipients
.append(item
);
291 // Example 1 (unknown signature key):
292 // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
293 // gpg: Can't check signature: public key not found
294 if ((index
= error
.indexOf("Signature made")) != -1) {
295 //qCDebug(KPGP_LOG) <<"Base: message is signed";
297 // get signature date and signature key ID
298 // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
299 index2
= error
.indexOf("using", index
+ 15);
300 block
.setSignatureDate(error
.mid(index
+ 15, index2
- (index
+ 15) - 1));
301 qCDebug(KPGP_LOG
) << "Message was signed on '" << block
.signatureDate() << "'";
302 index2
= error
.indexOf("key ID ", index2
) + 7;
303 block
.setSignatureKeyId(error
.mid(index2
, 8));
304 qCDebug(KPGP_LOG
) << "Message was signed with key '" << block
.signatureKeyId() << "'";
305 // move index to start of next line
306 index
= error
.indexOf('\n', index2
) + 1;
308 if ((error
.indexOf("Key matching expected", index
) != -1)
309 || (error
.indexOf("Can't check signature", index
) != -1)) {
310 status
|= UNKNOWN_SIG
;
312 block
.setSignatureUserId(QString());
313 } else if (error
.indexOf("Good signature", index
) != -1) {
315 // get the primary user ID of the signer
316 index
= error
.indexOf('"', index
);
317 index2
= error
.indexOf('\n', index
+ 1);
318 index2
= error
.lastIndexOf('"', index2
- 1);
319 block
.setSignatureUserId(QLatin1String(error
.mid(index
+ 1, index2
- index
- 1)));
320 } else if (error
.indexOf("BAD signature", index
) != -1) {
321 //qCDebug(KPGP_LOG) <<"BAD signature";
323 // get the primary user ID of the signer
324 index
= error
.indexOf('"', index
);
325 index2
= error
.indexOf('\n', index
+ 1);
326 index2
= error
.lastIndexOf('"', index2
- 1);
327 block
.setSignatureUserId(QLatin1String(error
.mid(index
+ 1, index2
- index
- 1)));
328 } else if (error
.indexOf("Can't find the right public key", index
) != -1) {
329 // #### fix this hack
330 // I think this can't happen anymore because if the pubring is missing
331 // the current GnuPG creates a new empty one.
332 status
|= UNKNOWN_SIG
;
333 status
|= GOODSIG
; // this is a hack...
334 block
.setSignatureUserId(i18n("??? (file ~/.gnupg/pubring.gpg not found)"));
337 block
.setSignatureUserId(QString());
340 //qCDebug(KPGP_LOG) <<"status =" << status;
341 block
.setStatus(status
);
346 BaseG::readPublicKey(const KeyID
&keyID
,
347 const bool readTrust
/* = false */,
354 exitStatus
= runGpg(QByteArray(QByteArray("--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x") + keyID
), 0, true);
356 exitStatus
= runGpg(QByteArray(QByteArray("--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x") + keyID
), 0, true);
359 if (exitStatus
!= 0) {
365 // search start of key data
366 if (!strncmp(output
.data(), "pub:", 4)) {
369 offset
= output
.indexOf("\npub:");
377 key
= parseKeyData(output
, offset
, key
);
383 BaseG::publicKeys(const QStringList
&patterns
)
387 // the option --with-colons should be used for interprocess communication
388 // with gpg (according to Werner Koch)
389 QByteArray cmd
= "--batch --list-public-keys --with-fingerprint --with-colons "
390 "--fixed-list-mode --no-expensive-trust-checks";
391 for (QStringList::ConstIterator it
= patterns
.begin();
392 it
!= patterns
.end(); ++it
) {
394 cmd
+= KShell::quoteArg(*it
).toLocal8Bit();
397 exitStatus
= runGpg(cmd
, 0, true);
399 if (exitStatus
!= 0) {
404 // now we need to parse the output for public keys
405 KeyList publicKeys
= parseKeyList(output
, false);
407 // sort the list of public keys
408 std::sort(publicKeys
.begin(), publicKeys
.end(), KeyCompare
);
414 BaseG::secretKeys(const QStringList
&patterns
)
418 // the option --with-colons should be used for interprocess communication
419 // with gpg (according to Werner Koch)
420 QByteArray cmd
= "--batch --list-secret-keys --with-fingerprint --with-colons "
422 for (QStringList::ConstIterator it
= patterns
.begin();
423 it
!= patterns
.end(); ++it
) {
425 cmd
+= KShell::quoteArg(*it
).toLocal8Bit();
428 exitStatus
= runGpg(cmd
, 0, true);
430 if (exitStatus
!= 0) {
435 // now we need to parse the output for secret keys
436 KeyList secretKeys
= parseKeyList(output
, true);
438 // sort the list of secret keys
439 std::sort(secretKeys
.begin(), secretKeys
.end(), KeyCompare
);
445 BaseG::signKey(const KeyID
&keyID
, const char *passphrase
)
452 cmd
+= " --sign-key 0x";
456 exitStatus
= runGpg(cmd
.data(), passphrase
);
458 if (exitStatus
!= 0) {
466 BaseG::getAsciiPublicKey(const KeyID
&keyID
)
470 if (keyID
.isEmpty()) {
475 exitStatus
= runGpg(QByteArray(QByteArray("--batch --armor --export 0x") + keyID
), 0, true);
477 if (exitStatus
!= 0) {
486 BaseG::parseKeyData(const QByteArray
&output
, int &offset
, Key
*key
/* = 0 */)
487 // This function parses the data for a single key which is output by GnuPG
488 // with the following command line arguments:
489 // --batch --list-public-keys --with-fingerprint --with-colons
490 // --fixed-list-mode [--no-expensive-trust-checks]
491 // It expects the key data to start at offset and returns the start of
492 // the next key's data in offset.
493 // Subkeys are currently ignored.
497 if ((strncmp(output
.data() + offset
, "pub:", 4) != 0)
498 && (strncmp(output
.data() + offset
, "sec:", 4) != 0)) {
509 bool firstKey
= true;
513 // search the end of the current line
514 if ((eol
= output
.indexOf('\n', index
)) == -1) {
518 bool bIsPublicKey
= false;
519 if ((bIsPublicKey
= !strncmp(output
.data() + index
, "pub:", 4))
520 || !strncmp(output
.data() + index
, "sec:", 4)) {
521 // line contains primary key data
522 // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
524 // abort parsing if we found the start of the next key
530 key
->setSecret(!bIsPublicKey
);
532 Subkey
*subkey
= new Subkey(QByteArray(), !bIsPublicKey
);
534 int pos
= index
+ 4; // begin of 2nd field
535 int pos2
= output
.indexOf(':', pos
);
536 for (int field
= 2; field
<= 12; field
++) {
538 case 2: // the calculated trust
540 switch (output
[pos
]) {
541 case 'o': // unknown (this key is new to the system)
543 case 'i': // the key is invalid, e.g. missing self-signature
544 subkey
->setInvalid(true);
545 key
->setInvalid(true);
547 case 'd': // the key has been disabled
548 subkey
->setDisabled(true);
549 key
->setDisabled(true);
551 case 'r': // the key has been revoked
552 subkey
->setRevoked(true);
553 key
->setRevoked(true);
555 case 'e': // the key has expired
556 subkey
->setExpired(true);
557 key
->setExpired(true);
559 case '-': // undefined (no path leads to the key)
560 case 'q': // undefined (no trusted path leads to the key)
561 case 'n': // don't trust this key at all
562 case 'm': // the key is marginally trusted
563 case 'f': // the key is fully trusted
564 case 'u': // the key is ultimately trusted (secret key available)
565 // These values are ignored since we determine the key trust
566 // from the trust values of the user ids.
569 qCDebug(KPGP_LOG
) << "Unknown trust value";
573 case 3: // length of key in bits
575 subkey
->setKeyLength(output
.mid(pos
, pos2
- pos
).toUInt());
578 case 4: // the key algorithm
580 subkey
->setKeyAlgorithm(output
.mid(pos
, pos2
- pos
).toUInt());
583 case 5: // the long key id
584 keyID
= output
.mid(pos
, pos2
- pos
);
585 subkey
->setKeyID(keyID
);
587 case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
589 subkey
->setCreationDate(QString(QLatin1String(output
.mid(pos
, pos2
- pos
))).toLong());
592 case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
594 subkey
->setExpirationDate(QString(QLatin1String(output
.mid(pos
, pos2
- pos
))).toLong());
596 subkey
->setExpirationDate(-1); // key expires never
599 case 8: // local ID (ignored)
600 case 9: // Ownertrust (ignored for now)
601 case 10: // User-ID (always empty in --fixed-list-mode)
602 case 11: // signature class (always empty except for key signatures)
604 case 12: // key capabilities
605 for (int i
= pos
; i
< pos2
; ++i
)
608 subkey
->setCanEncrypt(true);
611 subkey
->setCanSign(true);
614 subkey
->setCanCertify(true);
617 key
->setCanEncrypt(true);
620 key
->setCanSign(true);
623 key
->setCanCertify(true);
626 qCDebug(KPGP_LOG
) << "Unknown key capability";
631 pos2
= output
.indexOf(':', pos
);
633 key
->addSubkey(subkey
);
634 } else if (!strncmp(output
.data() + index
, "uid:", 4)) {
635 // line contains a user id
636 // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
638 UserID
*userID
= new UserID(QLatin1String(""));
640 int pos
= index
+ 4; // begin of 2nd field
641 int pos2
= output
.indexOf(':', pos
);
642 for (int field
= 2; field
<= 10; field
++) {
644 case 2: // the calculated trust
646 switch (output
[pos
]) {
647 case 'i': // the user id is invalid, e.g. missing self-signature
648 userID
->setInvalid(true);
650 case 'r': // the user id has been revoked
651 userID
->setRevoked(true);
653 case '-': // undefined (no path leads to the key)
654 case 'q': // undefined (no trusted path leads to the key)
655 userID
->setValidity(KPGP_VALIDITY_UNDEFINED
);
657 case 'n': // don't trust this key at all
658 userID
->setValidity(KPGP_VALIDITY_NEVER
);
660 case 'm': // the key is marginally trusted
661 userID
->setValidity(KPGP_VALIDITY_MARGINAL
);
663 case 'f': // the key is fully trusted
664 userID
->setValidity(KPGP_VALIDITY_FULL
);
666 case 'u': // the key is ultimately trusted (secret key available)
667 userID
->setValidity(KPGP_VALIDITY_ULTIMATE
);
670 qCDebug(KPGP_LOG
) << "Unknown trust value";
674 case 3: // these fields are empty
683 QByteArray uid
= output
.mid(pos
, pos2
- pos
);
684 // replace "\xXX" with the corresponding character;
685 // other escaped characters, i.e. \n, \r etc., are ignored
686 // because they shouldn't appear in user IDs
687 for (int idx
= 0 ; (idx
= uid
.indexOf("\\x", idx
) != -1) ; ++idx
) {
689 str
[0] = (char) QString(QLatin1String(uid
.mid(idx
+ 2, 2))).toShort(0, 16);
690 uid
.replace(idx
, 4, str
);
692 QString uidString
= QString::fromUtf8(uid
.data());
693 // check whether uid was utf-8 encoded
695 for (int i
= 0; i
+ 1 < uidString
.length(); ++i
) {
696 if (uidString
[i
].unicode() == 0xdbff &&
697 uidString
[i
+ 1].row() == 0xde) {
698 // we found a non-Unicode character (see QString::fromUtf8())
704 // The user id isn't utf-8 encoded. It was most likely
705 // created with PGP which either used latin1 or koi8-r.
706 qCDebug(KPGP_LOG
) << "User Id '" << uid
707 << "' doesn't seem to be utf-8 encoded.";
709 // We determine the ratio between non-ASCII and ASCII chars.
710 // A koi8-r user id should have lots of non-ASCII chars.
711 int nonAsciiCount
= 0, asciiCount
= 0;
713 // We only look at the first part of the user id (i. e. everything
714 // before the email address resp. before a comment)
715 for (signed char *ch
= (signed char *)uid
.data();
716 *ch
&& (*ch
!= '(') && (*ch
!= '<');
718 if (((*ch
>= 'A') && (*ch
<= 'Z'))
719 || ((*ch
>= 'a') && (*ch
<= 'z'))) {
721 } else if (*ch
< 0) {
725 qCDebug(KPGP_LOG
) << "ascii-nonAscii ratio :" << asciiCount
726 << ":" << nonAsciiCount
;
727 if (nonAsciiCount
> asciiCount
) {
728 // assume koi8-r encoding
729 qCDebug(KPGP_LOG
) << "Assume koi8-r encoding.";
730 QTextCodec
*codec
= QTextCodec::codecForName("KOI8-R");
731 uidString
= codec
->toUnicode(uid
.data());
732 // check the case of the first two characters to find out
733 // whether the user id is probably CP1251 encoded (for some
734 // reason in CP1251 the lower case characters have smaller
735 // codes than the upper case characters, so if the first char
736 // of the koi8-r decoded user id is lower case and the second
737 // char is upper case then it's likely that the user id is
739 if ((uidString
.length() >= 2)
740 && (uidString
[0].toLower() == uidString
[0])
741 && (uidString
[1].toUpper() == uidString
[1])) {
742 // koi8-r decoded user id has inverted case, so assume
744 qCDebug(KPGP_LOG
) << "No, it doesn't seem to be koi8-r."
745 "Use CP 1251 instead.";
746 QTextCodec
*codec
= QTextCodec::codecForName("CP1251");
747 uidString
= codec
->toUnicode(uid
.data());
750 // assume latin1 encoding
751 qCDebug(KPGP_LOG
) << "Assume latin1 encoding.";
752 uidString
= QString::fromLatin1(uid
.data());
755 userID
->setText(uidString
);
759 pos2
= output
.indexOf(':', pos
);
762 // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
763 key
->addUserID(userID
);
764 } else if (!strncmp(output
.data() + index
, "fpr:", 4)) {
765 // line contains a fingerprint
766 // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
768 if (key
== 0) { // invalid key data
772 // search the fingerprint (it's in the 10th field)
774 for (int i
= 0; i
< 8; ++i
) {
775 pos
= output
.indexOf(':', pos
) + 1;
777 int pos2
= output
.indexOf(':', pos
);
779 key
->setFingerprint(keyID
, output
.mid(pos
, pos2
- pos
));
784 //qCDebug(KPGP_LOG) <<"finished parsing key data";
792 BaseG::parseKeyList(const QByteArray
&output
, bool secretKeys
)
798 // search start of key data
799 if (!strncmp(output
.data(), "pub:", 4)
800 || !strncmp(output
.data(), "sec:", 4)) {
804 offset
= output
.indexOf("\nsec:");
806 offset
= output
.indexOf("\npub:");
816 key
= parseKeyData(output
, offset
);
823 //qCDebug(KPGP_LOG) <<"finished parsing keys";