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
30 #include <string.h> /* strncmp */
37 // determine the version of gpg (the method is equivalent to gpgme's method)
38 runGpg( "--version", 0 );
39 int eol
= output
.indexOf( '\n' );
41 int pos
= output
.lastIndexOf( ' ', eol
- 1 );
43 mVersion
= output
.mid( pos
+ 1, eol
- pos
- 1 );
44 kDebug( 5326 ) <<"found GnuPG" << mVersion
;
56 BaseG::encrypt( Block
& block
, const KeyIDList
& recipients
)
58 return encsign( block
, recipients
, 0 );
63 BaseG::clearsign( Block
& block
, const char *passphrase
)
65 return encsign( block
, KeyIDList(), passphrase
);
70 BaseG::encsign( Block
& block
, const KeyIDList
& recipients
,
71 const char *passphrase
)
76 if(!recipients
.isEmpty() && passphrase
!= 0)
77 cmd
= "--batch --armor --sign --encrypt --textmode";
78 else if(!recipients
.isEmpty())
79 cmd
= "--batch --armor --encrypt --textmode";
80 else if(passphrase
!= 0)
81 cmd
= "--batch --escape-from --clearsign";
84 kDebug( 5326 ) <<"kpgpbase: Neither recipients nor passphrase specified.";
91 if(!recipients
.isEmpty())
93 cmd
+= " --set-filename stdin";
95 QByteArray pgpUser
= Module::getKpgp()->user();
96 if(Module::getKpgp()->encryptToSelf() && !pgpUser
.isEmpty()) {
101 for( KeyIDList::ConstIterator it
= recipients
.begin();
102 it
!= recipients
.end(); ++it
) {
109 input
= block
.text();
110 exitStatus
= runGpg(cmd
.data(), passphrase
);
111 if( !output
.isEmpty() )
112 block
.setProcessedText( output
);
113 block
.setError( error
);
115 if( exitStatus
!= 0 )
117 // this error message is later hopefully overwritten
118 errMsg
= i18n( "Unknown error." );
123 // #### FIXME: As we check the keys ourselves the following problems
124 // shouldn't occur. Therefore I don't handle them for now.
126 if(!recipients
.isEmpty())
130 unsigned int num
= 0;
131 QByteArray badkeys
= "";
133 // gpg: 0x12345678: skipped: public key not found
134 // gpg: 0x12345678: skipped: public key is disabled
135 // gpg: 0x12345678: skipped: unusable public key
136 // (expired or revoked key)
137 // gpg: 23456789: no info to calculate a trust probability
138 // (untrusted key, 23456789 is the key Id of the encryption sub key)
139 while((index
= error
.indexOf("skipped: ",index
) ) != -1 )
142 index
= error
.indexOf('\'',index
);
143 int index2
= error
.indexOf('\'',index
+1);
144 badkeys
+= error
.mid(index
, index2
-index
+1) + ", ";
150 if(num
== recipients
.count())
151 errMsg
= i18n("Could not find public keys matching the userid(s)\n"
153 "the message is not encrypted.",
156 errMsg
= i18n("Could not find public keys matching the userid(s)\n"
158 "these persons will not be able to read the message.",
160 status
|= MISSINGKEY
;
165 if( passphrase
!= 0 )
167 // Example 1 (bad passphrase, clearsign only):
168 // gpg: skipped `0x12345678': bad passphrase
169 // gpg: [stdin]: clearsign failed: bad passphrase
170 // Example 2 (bad passphrase, sign & encrypt):
171 // gpg: skipped `0x12345678': bad passphrase
172 // gpg: [stdin]: sign+encrypt failed: bad passphrase
173 // Example 3 (unusable secret key, clearsign only):
174 // gpg: skipped `0x12345678': unusable secret key
175 // gpg: [stdin]: clearsign failed: unusable secret key
176 // Example 4 (unusable secret key, sign & encrypt):
177 // gpg: skipped `0xAC0EB35D': unusable secret key
178 // gpg: [stdin]: sign+encrypt failed: unusable secret key
179 if( error
.contains("bad passphrase") )
181 errMsg
= i18n("Signing failed because the passphrase is wrong.");
183 status
|= ERR_SIGNING
;
186 else if( error
.contains("unusable secret key") )
188 errMsg
= i18n("Signing failed because your secret key is unusable.");
189 status
|= ERR_SIGNING
;
192 else if( !( status
& ERROR
) )
194 //kDebug( 5326 ) <<"Base: Good Passphrase!";
199 //kDebug( 5326 ) <<"status =" << status;
200 block
.setStatus( status
);
206 BaseG::decrypt( Block
& block
, const char *passphrase
)
212 input
= block
.text();
213 exitStatus
= runGpg("--batch --decrypt", passphrase
);
214 if( !output
.isEmpty() && ( !error
.contains( "gpg: quoted printable" ) ) )
215 block
.setProcessedText( output
);
216 block
.setError( error
);
218 if(exitStatus
== -1) {
219 errMsg
= i18n("Error running gpg");
221 block
.setStatus( status
);
225 // Example 1 (good passphrase, decryption successful):
226 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-11-11
227 // "Foo Bar <foo@bar.xyz>"
229 // Example 2 (bad passphrase):
230 // gpg: encrypted with 1024-bit RSA key, ID 12345678, created 1991-01-01
231 // "Foo Bar <foo@bar.xyz>"
232 // gpg: public key decryption failed: bad passphrase
233 // gpg: decryption failed: secret key not available
235 // Example 3 (no secret key available):
236 // gpg: encrypted with RSA key, ID 12345678
237 // gpg: decryption failed: secret key not available
239 // Example 4 (good passphrase for second key, decryption successful):
240 // gpg: encrypted with 2048-bit ELG-E key, ID 12345678, created 2000-01-01
241 // "Foo Bar (work) <foo@bar.xyz>"
242 // gpg: public key decryption failed: bad passphrase
243 // gpg: encrypted with 2048-bit ELG-E key, ID 23456789, created 2000-02-02
244 // "Foo Bar (home) <foo@bar.xyz>"
246 // Example 5: passphrase dialog cancelled by user
247 // gpg: cancelled by user
248 // gpg: encrypted with 2048-bit ELG key, ID XXXXXXXX, created 2006-11-16
249 // "Foo Bar <foobar@foo.org>"
250 // gpg: public key decryption failed: General error [..]
251 if( error
.contains( "gpg: encrypted with" ) )
253 //kDebug( 5326 ) <<"kpgpbase: message is encrypted";
255 if( error
.contains( "\ngpg: decryption failed" ) )
257 if( ( index
= error
.indexOf( "bad passphrase" ) ) != -1 )
259 if( passphrase
!= 0 )
261 errMsg
= i18n( "Bad passphrase; could not decrypt." );
262 kDebug( 5326 ) <<"Base: passphrase is bad";
268 // Search backwards the user ID of the needed key
269 index2
= error
.lastIndexOf('"', index
) - 1;
270 index
= error
.lastIndexOf(" \"", index2
) + 7;
271 // The conversion from UTF8 is necessary because gpg stores and
272 // prints user IDs in UTF8
273 block
.setRequiredUserId( QString::fromUtf8( error
.mid( index
, index2
- index
+ 1 ) ) );
274 kDebug( 5326 ) <<"Base: key needed is \"" << block
.requiredUserId() <<"\"!";
277 else if( error
.contains( "secret key not available" ) )
279 // no secret key fitting this message
280 status
|= NO_SEC_KEY
;
282 errMsg
= i18n("You do not have the secret key needed to decrypt this message.");
283 kDebug( 5326 ) <<"Base: no secret key for this message";
285 else if( error
.contains( "cancelled by user" ) )
289 errMsg
= i18n("The passphrase dialog was cancelled.");
290 kDebug( 5326 ) << errMsg
;
295 // ##### FIXME: This information is anyway currently not used
296 // I'll change it to always determine the recipients.
297 index
= error
.indexOf("can only be read by:");
300 index
= error
.indexOf('\n',index
);
301 int end
= error
.indexOf("\n\n",index
);
304 while( (index2
= error
.indexOf('\n',index
+1)) <= end
)
306 QByteArray item
= error
.mid(index
+1,index2
-index
-1);
308 mRecipients
.append(item
);
315 // Example 1 (unknown signature key):
316 // gpg: Signature made Wed 02 Jan 2002 11:26:33 AM CET using DSA key ID 2E250C64
317 // gpg: Can't check signature: public key not found
318 if( ( index
= error
.indexOf("Signature made") ) != -1 )
320 //kDebug( 5326 ) <<"Base: message is signed";
322 // get signature date and signature key ID
323 // Example: Signature made Sun 06 May 2001 03:49:27 PM CEST using DSA key ID 12345678
324 index2
= error
.indexOf("using", index
+15);
325 block
.setSignatureDate( error
.mid(index
+15, index2
-(index
+15)-1) );
326 kDebug( 5326 ) <<"Message was signed on '" << block
.signatureDate() <<"'";
327 index2
= error
.indexOf("key ID ", index2
) + 7;
328 block
.setSignatureKeyId( error
.mid(index2
,8) );
329 kDebug( 5326 ) <<"Message was signed with key '" << block
.signatureKeyId() <<"'";
330 // move index to start of next line
331 index
= error
.indexOf('\n', index2
)+1;
333 if ((error
.indexOf("Key matching expected", index
) != -1 )
334 || (error
.indexOf("Can't check signature", index
) != -1 ))
336 status
|= UNKNOWN_SIG
;
338 block
.setSignatureUserId( QString() );
340 else if( error
.indexOf("Good signature", index
) != -1 )
343 // get the primary user ID of the signer
344 index
= error
.indexOf('"',index
);
345 index2
= error
.indexOf('\n',index
+1);
346 index2
= error
.lastIndexOf('"', index2
-1);
347 block
.setSignatureUserId( error
.mid( index
+1, index2
-index
-1 ) );
349 else if( error
.indexOf("BAD signature", index
) != -1 )
351 //kDebug( 5326 ) <<"BAD signature";
353 // get the primary user ID of the signer
354 index
= error
.indexOf('"',index
);
355 index2
= error
.indexOf('\n',index
+1);
356 index2
= error
.lastIndexOf('"', index2
-1);
357 block
.setSignatureUserId( error
.mid( index
+1, index2
-index
-1 ) );
359 else if( error
.indexOf("Can't find the right public key", index
) != -1 )
361 // #### fix this hack
362 // I think this can't happen anymore because if the pubring is missing
363 // the current GnuPG creates a new empty one.
364 status
|= UNKNOWN_SIG
;
365 status
|= GOODSIG
; // this is a hack...
366 block
.setSignatureUserId( i18n("??? (file ~/.gnupg/pubring.gpg not found)") );
371 block
.setSignatureUserId( QString() );
374 //kDebug( 5326 ) <<"status =" << status;
375 block
.setStatus( status
);
381 BaseG::readPublicKey( const KeyID
& keyID
,
382 const bool readTrust
/* = false */,
389 exitStatus
= runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode 0x" + keyID
, 0, true );
391 exitStatus
= runGpg( "--batch --list-public-keys --with-fingerprint --with-colons --fixed-list-mode --no-expensive-trust-checks 0x" + keyID
, 0, true );
393 if(exitStatus
!= 0) {
399 // search start of key data
400 if( !strncmp( output
.data(), "pub:", 4 ) )
403 offset
= output
.indexOf( "\npub:" );
410 key
= parseKeyData( output
, offset
, key
);
417 BaseG::publicKeys( const QStringList
& patterns
)
421 // the option --with-colons should be used for interprocess communication
422 // with gpg (according to Werner Koch)
423 QByteArray cmd
= "--batch --list-public-keys --with-fingerprint --with-colons "
424 "--fixed-list-mode --no-expensive-trust-checks";
425 for ( QStringList::ConstIterator it
= patterns
.begin();
426 it
!= patterns
.end(); ++it
) {
428 cmd
+= KShell::quoteArg( *it
).toLocal8Bit();
431 exitStatus
= runGpg( cmd
, 0, true );
433 if(exitStatus
!= 0) {
438 // now we need to parse the output for public keys
439 KeyList publicKeys
= parseKeyList(output
, false);
441 // sort the list of public keys
442 std::sort( publicKeys
.begin(), publicKeys
.end(), KeyCompare
);
449 BaseG::secretKeys( const QStringList
& patterns
)
453 // the option --with-colons should be used for interprocess communication
454 // with gpg (according to Werner Koch)
455 QByteArray cmd
= "--batch --list-secret-keys --with-fingerprint --with-colons "
457 for ( QStringList::ConstIterator it
= patterns
.begin();
458 it
!= patterns
.end(); ++it
) {
460 cmd
+= KShell::quoteArg( *it
).toLocal8Bit();
463 exitStatus
= runGpg( cmd
, 0, true );
465 if(exitStatus
!= 0) {
470 // now we need to parse the output for secret keys
471 KeyList secretKeys
= parseKeyList(output
, true);
473 // sort the list of secret keys
474 std::sort( secretKeys
.begin(), secretKeys
.end(), KeyCompare
);
481 BaseG::signKey(const KeyID
& keyID
, const char *passphrase
)
488 cmd
+= " --sign-key 0x";
492 exitStatus
= runGpg(cmd
.data(), passphrase
);
502 BaseG::getAsciiPublicKey(const KeyID
& keyID
)
510 exitStatus
= runGpg("--batch --armor --export 0x" + keyID
, 0, true);
512 if(exitStatus
!= 0) {
522 BaseG::parseKeyData( const QByteArray
& output
, int& offset
, Key
* key
/* = 0 */ )
523 // This function parses the data for a single key which is output by GnuPG
524 // with the following command line arguments:
525 // --batch --list-public-keys --with-fingerprint --with-colons
526 // --fixed-list-mode [--no-expensive-trust-checks]
527 // It expects the key data to start at offset and returns the start of
528 // the next key's data in offset.
529 // Subkeys are currently ignored.
533 if( ( strncmp( output
.data() + offset
, "pub:", 4 ) != 0 )
534 && ( strncmp( output
.data() + offset
, "sec:", 4 ) != 0 ) ) {
544 bool firstKey
= true;
549 // search the end of the current line
550 if( ( eol
= output
.indexOf( '\n', index
) ) == -1 )
553 bool bIsPublicKey
= false;
554 if( ( bIsPublicKey
= !strncmp( output
.data() + index
, "pub:", 4 ) )
555 || !strncmp( output
.data() + index
, "sec:", 4 ) )
556 { // line contains primary key data
557 // Example: pub:f:1024:17:63CB691DFAEBD5FC:860451781::379:-:::scESC:
559 // abort parsing if we found the start of the next key
564 key
->setSecret( !bIsPublicKey
);
566 Subkey
*subkey
= new Subkey( QByteArray(), !bIsPublicKey
);
568 int pos
= index
+ 4; // begin of 2nd field
569 int pos2
= output
.indexOf( ':', pos
);
570 for( int field
= 2; field
<= 12; field
++ )
574 case 2: // the calculated trust
577 switch( output
[pos
] )
579 case 'o': // unknown (this key is new to the system)
581 case 'i': // the key is invalid, e.g. missing self-signature
582 subkey
->setInvalid( true );
583 key
->setInvalid( true );
585 case 'd': // the key has been disabled
586 subkey
->setDisabled( true );
587 key
->setDisabled( true );
589 case 'r': // the key has been revoked
590 subkey
->setRevoked( true );
591 key
->setRevoked( true );
593 case 'e': // the key has expired
594 subkey
->setExpired( true );
595 key
->setExpired( true );
597 case '-': // undefined (no path leads to the key)
598 case 'q': // undefined (no trusted path leads to the key)
599 case 'n': // don't trust this key at all
600 case 'm': // the key is marginally trusted
601 case 'f': // the key is fully trusted
602 case 'u': // the key is ultimately trusted (secret key available)
603 // These values are ignored since we determine the key trust
604 // from the trust values of the user ids.
607 kDebug( 5326 ) <<"Unknown trust value";
611 case 3: // length of key in bits
613 subkey
->setKeyLength( output
.mid( pos
, pos2
-pos
).toUInt() );
615 case 4: // the key algorithm
617 subkey
->setKeyAlgorithm( output
.mid( pos
, pos2
-pos
).toUInt() );
619 case 5: // the long key id
620 keyID
= output
.mid( pos
, pos2
-pos
);
621 subkey
->setKeyID( keyID
);
623 case 6: // the creation date (in seconds since 1970-01-01 00:00:00)
625 subkey
->setCreationDate( QString(output
.mid( pos
, pos2
-pos
)).toLong() );
627 case 7: // the expiration date (in seconds since 1970-01-01 00:00:00)
629 subkey
->setExpirationDate( QString(output
.mid( pos
, pos2
-pos
)).toLong() );
631 subkey
->setExpirationDate( -1 ); // key expires never
633 case 8: // local ID (ignored)
634 case 9: // Ownertrust (ignored for now)
635 case 10: // User-ID (always empty in --fixed-list-mode)
636 case 11: // signature class (always empty except for key signatures)
638 case 12: // key capabilities
639 for( int i
=pos
; i
<pos2
; i
++ )
643 subkey
->setCanEncrypt( true );
646 subkey
->setCanSign( true );
649 subkey
->setCanCertify( true );
652 key
->setCanEncrypt( true );
655 key
->setCanSign( true );
658 key
->setCanCertify( true );
661 kDebug( 5326 ) <<"Unknown key capability";
666 pos2
= output
.indexOf( ':', pos
);
668 key
->addSubkey( subkey
);
670 else if( !strncmp( output
.data() + index
, "uid:", 4 ) )
671 { // line contains a user id
672 // Example: uid:f::::::::Philip R. Zimmermann <prz@pgp.com>:
674 UserID
*userID
= new UserID( "" );
676 int pos
= index
+ 4; // begin of 2nd field
677 int pos2
= output
.indexOf( ':', pos
);
678 for( int field
=2; field
<= 10; field
++ )
682 case 2: // the calculated trust
685 switch( output
[pos
] )
687 case 'i': // the user id is invalid, e.g. missing self-signature
688 userID
->setInvalid( true );
690 case 'r': // the user id has been revoked
691 userID
->setRevoked( true );
693 case '-': // undefined (no path leads to the key)
694 case 'q': // undefined (no trusted path leads to the key)
695 userID
->setValidity( KPGP_VALIDITY_UNDEFINED
);
697 case 'n': // don't trust this key at all
698 userID
->setValidity( KPGP_VALIDITY_NEVER
);
700 case 'm': // the key is marginally trusted
701 userID
->setValidity( KPGP_VALIDITY_MARGINAL
);
703 case 'f': // the key is fully trusted
704 userID
->setValidity( KPGP_VALIDITY_FULL
);
706 case 'u': // the key is ultimately trusted (secret key available)
707 userID
->setValidity( KPGP_VALIDITY_ULTIMATE
);
710 kDebug( 5326 ) <<"Unknown trust value";
714 case 3: // these fields are empty
723 QByteArray uid
= output
.mid( pos
, pos2
-pos
);
724 // replace "\xXX" with the corresponding character;
725 // other escaped characters, i.e. \n, \r etc., are ignored
726 // because they shouldn't appear in user IDs
727 for ( int idx
= 0 ; (idx
= uid
.indexOf( "\\x", idx
) != -1) ; ++idx
) {
729 str
[0] = (char) QString( uid
.mid( idx
+ 2, 2 ) ).toShort( 0, 16 );
730 uid
.replace( idx
, 4, str
);
732 QString uidString
= QString::fromUtf8( uid
.data() );
733 // check whether uid was utf-8 encoded
735 for ( int i
= 0; i
+ 1 < uidString
.length(); ++i
) {
736 if ( uidString
[i
].unicode() == 0xdbff &&
737 uidString
[i
+1].row() == 0xde ) {
738 // we found a non-Unicode character (see QString::fromUtf8())
744 // The user id isn't utf-8 encoded. It was most likely
745 // created with PGP which either used latin1 or koi8-r.
746 kDebug( 5326 ) <<"User Id '" << uid
747 << "' doesn't seem to be utf-8 encoded.";
749 // We determine the ratio between non-ASCII and ASCII chars.
750 // A koi8-r user id should have lots of non-ASCII chars.
751 int nonAsciiCount
= 0, asciiCount
= 0;
753 // We only look at the first part of the user id (i. e. everything
754 // before the email address resp. before a comment)
755 for( signed char* ch
= (signed char*)uid
.data();
756 *ch
&& ( *ch
!= '(' ) && ( *ch
!= '<' );
758 if( ( ( *ch
>= 'A' ) && ( *ch
<= 'Z' ) )
759 || ( ( *ch
>= 'a' ) && ( *ch
<= 'z' ) ) )
764 kDebug( 5326 ) <<"ascii-nonAscii ratio :" << asciiCount
765 << ":" << nonAsciiCount
;
766 if( nonAsciiCount
> asciiCount
) {
767 // assume koi8-r encoding
768 kDebug( 5326 ) <<"Assume koi8-r encoding.";
769 QTextCodec
*codec
= QTextCodec::codecForName("KOI8-R");
770 uidString
= codec
->toUnicode( uid
.data() );
771 // check the case of the first two characters to find out
772 // whether the user id is probably CP1251 encoded (for some
773 // reason in CP1251 the lower case characters have smaller
774 // codes than the upper case characters, so if the first char
775 // of the koi8-r decoded user id is lower case and the second
776 // char is upper case then it's likely that the user id is
778 if( ( uidString
.length() >= 2 )
779 && ( uidString
[0].toLower() == uidString
[0] )
780 && ( uidString
[1].toUpper() == uidString
[1] ) ) {
781 // koi8-r decoded user id has inverted case, so assume
783 kDebug( 5326 ) <<"No, it doesn't seem to be koi8-r."
784 "Use CP 1251 instead.";
785 QTextCodec
*codec
= QTextCodec::codecForName("CP1251");
786 uidString
= codec
->toUnicode( uid
.data() );
790 // assume latin1 encoding
791 kDebug( 5326 ) <<"Assume latin1 encoding.";
792 uidString
= QString::fromLatin1( uid
.data() );
795 userID
->setText( uidString
);
799 pos2
= output
.indexOf( ':', pos
);
802 // user IDs are printed in UTF-8 by gpg (if one uses --with-colons)
803 key
->addUserID( userID
);
805 else if( !strncmp( output
.data() + index
, "fpr:", 4 ) )
806 { // line contains a fingerprint
807 // Example: fpr:::::::::17AFBAAF21064E513F037E6E63CB691DFAEBD5FC:
809 if (key
== 0) // invalid key data
812 // search the fingerprint (it's in the 10th field)
814 for( int i
= 0; i
< 8; i
++ )
815 pos
= output
.indexOf( ':', pos
) + 1;
816 int pos2
= output
.indexOf( ':', pos
);
818 key
->setFingerprint( keyID
, output
.mid( pos
, pos2
-pos
) );
823 //kDebug( 5326 ) <<"finished parsing key data";
832 BaseG::parseKeyList( const QByteArray
& output
, bool secretKeys
)
838 // search start of key data
839 if( !strncmp( output
.data(), "pub:", 4 )
840 || !strncmp( output
.data(), "sec:", 4 ) )
844 offset
= output
.indexOf( "\nsec:" );
846 offset
= output
.indexOf( "\npub:" );
854 key
= parseKeyData( output
, offset
);
861 //kDebug( 5326 ) <<"finished parsing keys";