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 <string.h> /* strncmp */
45 Base6::decrypt( Block
& block
, const char *passphrase
)
51 int exitStatus
= run( PGP6
" +batchmode +language=C -f", passphrase
);
52 if( !output
.isEmpty() )
53 block
.setProcessedText( output
);
54 block
.setError( error
);
56 if(exitStatus
== -1) {
57 errMsg
= i18n("error running PGP");
59 block
.setStatus( status
);
64 if( error
.contains("File is encrypted.") )
66 //kDebug( 5326 ) <<"kpgpbase: message is encrypted";
68 if((index
= error
.indexOf("Key for user ID")) != -1 )
70 // Find out the key for which the phrase is needed
71 index
= error
.indexOf(':', index
) + 2;
72 index2
= error
.indexOf('\n', index
);
73 block
.setRequiredUserId( error
.mid(index
, index2
- index
) );
74 //kDebug( 5326 ) <<"Base: key needed is \"" << block.requiredUserId() <<"\"!";
76 // Test output length to find out, if the passphrase is
77 // bad. If someone knows a better way, please fix this.
78 /// ### This could be done by forcing PGP6 to be more verbose
79 /// by adding an additional '+verbose=2' to the command line
80 if (!passphrase
|| !output
.length())
82 errMsg
= i18n("Bad passphrase; could not decrypt.");
83 //kDebug( 5326 ) <<"Base: passphrase is bad";
88 else if( error
.contains("You do not have the secret key needed to decrypt this file.") )
90 errMsg
= i18n("You do not have the secret key for this message.");
91 //kDebug( 5326 ) <<"Base: no secret key for this message";
99 // Examples (made with PGP 6.5.8)
100 /* Example no. 1 (signed with unknown key):
101 * File is signed. signature not checked.
102 * Signature made 2001/11/25 11:55 GMT
103 * key does not meet validity threshold.
105 * WARNING: Because this public key is not certified with a trusted
106 * signature, it is not known with high confidence that this public key
107 * actually belongs to: "(KeyID: 0x475027BD)".
109 /* Example no. 2 (signed with untrusted key):
110 * File is signed. Good signature from user "Joe User <joe@foo.bar>".
111 * Signature made 2001/12/05 13:09 GMT
113 * WARNING: Because this public key is not certified with a trusted
114 * signature, it is not known with high confidence that this public key
115 * actually belongs to: "Joe User <joe@foo.bar>".
117 /* Example no. 3 (signed with trusted key):
118 * File is signed. Good signature from user "Joe User <joe@foo.bar>".
119 * Signature made 2001/12/05 13:09 GMT
121 if(((index
= error
.indexOf("File is signed.")) != -1 )
122 || (error
.contains("Good signature") ))
124 //kDebug( 5326 ) <<"Base: message is signed";
126 // determine the signature date
127 if( ( index2
= error
.indexOf( "Signature made", index
) ) != -1 )
130 int eol
= error
.indexOf( '\n', index2
);
131 block
.setSignatureDate( error
.mid( index2
, eol
-index2
) );
132 kDebug( 5326 ) <<"Message was signed on '" << block
.signatureDate() <<"'";
135 block
.setSignatureDate( QByteArray() );
136 // determine signature status and signature key
137 if( error
.contains("signature not checked") )
139 index
= error
.indexOf("KeyID:",index
);
140 block
.setSignatureKeyId( error
.mid(index
+9,8) );
141 block
.setSignatureUserId( QString() );
142 status
|= UNKNOWN_SIG
;
145 else if((index
= error
.indexOf("Good signature")) != -1 )
149 index
= error
.indexOf('"',index
)+1;
150 index2
= error
.indexOf('"', index
);
151 block
.setSignatureUserId( error
.mid(index
, index2
-index
) );
153 // get key ID of signer
154 index
= error
.indexOf("KeyID:",index2
);
156 block
.setSignatureKeyId( QByteArray() );
158 block
.setSignatureKeyId( error
.mid(index
+9,8) );
160 else if( error
.contains("Can't find the right public key") )
162 // #### fix this hack
163 // #### This doesn't happen with PGP 6.5.8 because it seems to
164 // #### automatically create an empty pubring if it doesn't exist.
165 status
|= UNKNOWN_SIG
;
166 status
|= GOODSIG
; // this is a hack...
167 block
.setSignatureUserId( i18n("??? (file ~/.pgp/pubring.pkr not found)") );
168 block
.setSignatureKeyId( "???" );
173 block
.setSignatureUserId( QString() );
174 block
.setSignatureKeyId( QByteArray() );
177 //kDebug( 5326 ) <<"status =" << status;
178 block
.setStatus( status
);
184 Base6::readPublicKey( const KeyID
& keyID
,
185 const bool readTrust
/* = false */,
189 int exitStatus
= run( PGP6
" +batchmode -compatible +verbose=0 +language=C -kvvc "
190 "0x" + keyID
, 0, true );
192 if(exitStatus
!= 0) {
197 key
= parseSingleKey( output
, key
);
206 exitStatus
= run( PGP6
" +batchmode -compatible +verbose=0 +language=C -kc "
207 "0x" + keyID
, 0, true );
209 if(exitStatus
!= 0) {
214 parseTrustDataForKey( key
, output
);
222 Base6::publicKeys( const QStringList
& patterns
)
224 return doGetPublicKeys( PGP6
" +batchmode -compatible +verbose=0 "
225 "+language=C -kvvc", patterns
);
235 int compatibleMode = 1;
238 exitStatus = run("pgp +batchmode +language=C -kv -f");
240 if(exitStatus != 0) {
245 //truncate trailing "\n"
246 if (error.length() > 1) error.truncate(error.length()-1);
249 index = error.indexOf("bits/keyID",1); // skip first to "\n"
252 index = error.indexOf("Type bits",1); // skip first to "\n"
259 while( (index = error.indexOf("\n",index)) != -1 )
263 if( (index2 = error.indexOf("\n",index+1)) != -1 )
269 int index_pub = error.indexOf("pub ",index);
270 int index_sec = error.indexOf("sec ",index);
273 else if (index_sec < 0)
276 index3 = (index_pub < index_sec ? index_pub : index_sec);
280 int index_rsa = error.indexOf("RSA ",index);
281 int index_dss = error.indexOf("DSS ",index);
284 else if (index_dss < 0)
287 index3 = (index_rsa < index_dss ? index_rsa : index_dss);
290 if( (index3 >index2) || (index3 == -1) )
292 // second address for the same key
293 line = error.mid(index+1,index2-index-1);
294 line = line.trimmed();
297 int index4 = error.indexOf(QRegExp("/\\d{2}/\\d{2} "), index);
298 line = error.mid(index4+7,index2-index4-7);
300 //kDebug( 5326 ) <<"Base: found key for" << (const char *)line;
302 // don't add PGP's comments to the key list
303 if (strncmp(line.data(),"*** KEY EXPIRED ***",19) &&
304 !line.contains(QRegExp("^expires \\d{4}/\\d{2}/\\d{2}")) &&
305 strncmp(line.data(),"*** DEFAULT SIGNING KEY ***",27)) {
306 publicKeys.append(line);
314 // Also look for pgp key groups
315 exitStatus = run("pgp +batchmode +language=C -gv -f");
317 if(exitStatus != 0) {
323 while ( (index = error.indexOf("\n >", index)) != -1 ) {
326 index2 = error.indexOf(" \"", index);
327 line = error.mid(index, index2-index+1).trimmed();
329 //kDebug( 5326 ) <<"Base6: found key group for" << line;
330 publicKeys.append(line);
339 Base6::secretKeys( const QStringList
& patterns
)
341 return publicKeys( patterns
);
348 int exitStatus
= run( PGP6
, 0, true );
350 if(exitStatus
== -1) {
351 errMsg
= i18n("error running PGP");
356 if( error
.contains("Version 6") )
358 //kDebug( 5326 ) <<"kpgpbase: pgp version 6.x detected";
362 //kDebug( 5326 ) <<"kpgpbase: not pgp version 6.x";
368 Base6::parseKeyData( const QByteArray
& output
, int& offset
, Key
* key
/* = 0 */ )
369 // This function parses the data for a single key which is output by PGP 6
370 // with the following command line arguments:
371 // +batchmode -compatible +verbose=0 +language=C -kvvc
372 // It expects the key data to start at offset and returns the start of
373 // the next key's data in offset.
375 if( ( strncmp( output
.data() + offset
, "DSS", 3 ) != 0 ) &&
376 ( strncmp( output
.data() + offset
, "RSA", 3 ) != 0 ) )
378 kDebug( 5326 ) <<"Unknown key type or corrupt key data.";
383 bool firstLine
= true;
384 bool canSign
= false;
385 bool canEncr
= false;
392 // search the end of the current line
393 if( ( eol
= output
.indexOf( '\n', offset
) ) == -1 )
396 //kDebug( 5326 ) <<"Parsing:" << output.mid(offset, eol-offset);
398 if( firstLine
&& ( !strncmp( output
.data() + offset
, "DSS", 3 ) ||
399 !strncmp( output
.data() + offset
, "RSA", 3 ) ) )
400 { // line contains primary key data
402 // RSA 1024 0xE2D074D3 2001/09/09 Test Key <testkey@xyz>
403 // Example 2 (disabled key):
404 // RSA@ 1024 0x8CCB2C1B 2001/11/04 Disabled Test Key <disabled@xyz>
405 // Example 3 (expired key):
406 // RSA 1024 0x7B94827D 2001/09/09 *** KEY EXPIRED ***
407 // Example 4 (revoked key):
408 // RSA 1024 0x956721F9 2001/09/09 *** KEY REVOKED ***
409 // Example 5 (default signing key):
410 // RSA 1024 0x12345678 2001/09/09 *** DEFAULT SIGNING KEY ***
411 // Example 6 (expiring key):
412 // RSA 2048 0xC11DB2E5 2000/02/24 expires 2001/12/31
413 // Example 7 (complex example):
414 // DSS 1024 0x80E104A7 2000/06/05 expires 2002/05/31
415 // DSS 1024 0x80E104A7 2001/06/27 *** KEY REVOKED ***expires 2002/06/27
416 // DH 1024 0x80E104A7 2000/06/05 *** KEY REVOKED ****** KEY EXPIRED ***
417 //kDebug( 5326 )<<"Primary key data:";
421 // set default key capabilities
422 if( !strncmp( output
.data() + offset
, "DSS", 3 ) )
424 if( !strncmp( output
.data() + offset
, "RSA", 3 ) )
437 subkey
= new Subkey( "", false );
438 key
->addSubkey( subkey
);
439 // expiration date defaults to never
440 subkey
->setExpirationDate( -1 );
443 switch( output
[offset
+3] )
445 case ' ': // nothing special
447 case '@': // disabled key
448 subkey
->setDisabled( true );
449 key
->setDisabled( true );
452 kDebug( 5326 ) <<"Unknown key flag.";
457 while( output
[pos
] == ' ' )
459 pos2
= output
.indexOf( ' ', pos
);
460 subkey
->setKeyLength( output
.mid( pos
, pos2
-pos
).toUInt() );
461 //kDebug( 5326 ) <<"Key Length:"<<subkey->keyLength();
465 while( output
[pos
] == ' ' )
467 pos
+= 2; // skip the '0x'
468 pos2
= output
.indexOf( ' ', pos
);
469 subkey
->setKeyID( output
.mid( pos
, pos2
-pos
) );
470 //kDebug( 5326 ) <<"Key ID:"<<subkey->keyID();
474 while( output
[pos
] == ' ' )
476 pos2
= output
.indexOf( ' ', pos
);
477 int year
= output
.mid( pos
, 4 ).toInt();
478 int month
= output
.mid( pos
+5, 2 ).toInt();
479 int day
= output
.mid( pos
+8, 2 ).toInt();
480 QDateTime
dt( QDate( year
, month
, day
), QTime( 00, 00 ) );
481 QDateTime
epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
482 // The calculated creation date isn't exactly correct because QDateTime
483 // doesn't know anything about timezones and always assumes local time
484 // although epoch is of course UTC. But as PGP 6 anyway doesn't print
485 // the time this doesn't matter too much.
486 subkey
->setCreationDate( epoch
.secsTo( dt
) );
488 // User ID or key properties
490 while( output
[pos
] == ' ' )
493 { // loop over User ID resp. key properties
494 if( !strncmp( output
.data() + pos
, "*** KEY REVOKED ***", 19 ) )
498 subkey
->setRevoked( true );
499 key
->setRevoked( true );
501 //kDebug( 5326 ) <<"Key was revoked.";
503 else if( !strncmp( output
.data() + pos
, "*** KEY EXPIRED ***", 19 ) )
507 subkey
->setExpired( true );
508 key
->setExpired( true );
510 //kDebug( 5326 ) <<"Key has expired.";
512 else if( !strncmp( output
.data() + pos
, "expires ", 8 ) )
515 int year
= output
.mid( pos
, 4 ).toInt();
516 int month
= output
.mid( pos
+5, 2 ).toInt();
517 int day
= output
.mid( pos
+8, 2 ).toInt();
518 QDateTime
dt( QDate( year
, month
, day
), QTime( 00, 00 ) );
519 // Here the same comments as for the creation date are valid.
520 subkey
->setExpirationDate( epoch
.secsTo( dt
) );
522 //kDebug( 5326 ) <<"Key expires...";
524 else if( !strncmp( output
.data() + pos
, "*** DEFAULT SIGNING KEY ***", 27 ) )
527 //kDebug( 5326 ) <<"Key is default signing key.";
531 QByteArray uid
= output
.mid( pos
, eol
-pos
);
532 key
->addUserID( uid
);
534 //kDebug( 5326 ) <<"User ID:"<<uid;
537 // set key capabilities of the primary subkey
538 subkey
->setCanEncrypt( encr
);
539 subkey
->setCanSign( sign
);
540 subkey
->setCanCertify( sign
);
541 // remember the global key capabilities
545 else if( !strncmp( output
.data() + offset
, "DSS", 3 ) ||
546 !strncmp( output
.data() + offset
, " DH", 3 ) ||
547 !strncmp( output
.data() + offset
, "RSA", 3 ) )
548 { // line contains secondary key data (or data for the next key)
550 break; // here begins the next key's data
551 //kDebug( 5326 )<<"Secondary key data:";
559 // set default key capabilities
560 if( !strncmp( output
.data() + offset
, "DSS", 3 ) )
562 if( !strncmp( output
.data() + offset
, " DH", 3 ) )
564 if( !strncmp( output
.data() + offset
, "RSA", 3 ) )
572 // Key Length of secondary key (ignored)
574 while( output
[pos
] == ' ' )
576 pos2
= output
.indexOf( ' ', pos
);
578 // Key ID (ignored as it is anyway equal to the primary key id)
580 while( output
[pos
] == ' ' )
582 pos2
= output
.indexOf( ' ', pos
);
584 // Creation Date of secondary key (ignored)
586 while( output
[pos
] == ' ' )
588 pos2
= output
.indexOf( ' ', pos
);
590 // User ID or key properties
592 while( output
[pos
] == ' ' )
595 { // loop over User ID resp. key properties
596 if( !strncmp( output
.data() + pos
, "*** KEY REVOKED ***", 19 ) )
601 //kDebug( 5326 ) <<"Key was revoked.";
603 else if( !strncmp( output
.data() + pos
, "*** KEY EXPIRED ***", 19 ) )
608 //kDebug( 5326 ) <<"Key has expired.";
610 else if( !strncmp( output
.data() + pos
, "expires ", 8 ) )
612 pos
+= 18; // skip the expiration date
613 //kDebug( 5326 ) <<"Key expires...";
615 else if( !strncmp( output
.data() + pos
, "*** DEFAULT SIGNING KEY ***", 27 ) )
618 //kDebug( 5326 ) <<"Key is default signing key.";
622 QByteArray uid
= output
.mid( pos
, eol
-pos
);
623 key
->addUserID( uid
);
625 //kDebug( 5326 ) <<"User ID:"<<uid;
628 // store the global key capabilities
629 canSign
= canSign
|| sign
;
630 canEncr
= canEncr
|| encr
;
632 else if( !strncmp( output
.data() + offset
, "Unknown type", 12 ) )
633 { // line contains key data of unknown type (ignored)
634 kDebug( 5326 )<<"Unknown key type.";
636 else if( output
[offset
] == ' ' )
637 { // line contains additional key data
640 //kDebug( 5326 )<<"Additional key data:";
642 int pos
= offset
+ 1;
643 while( output
[pos
] == ' ' )
646 if( !strncmp( output
.data() + pos
, "Key fingerprint = ", 18 ) )
647 { // line contains a fingerprint
649 // Key fingerprint = D0 6C BB 3A F5 16 82 C4 F3 A0 8A B3 7B 16 99 70
651 fpr
= true; // we found a fingerprint
654 QByteArray fingerprint
= output
.mid( pos
, eol
-pos
);
655 // remove white space from the fingerprint
656 for ( int idx
= 0 ; (idx
= fingerprint
.indexOf(' ', idx
)) != -1; )
657 fingerprint
.replace( idx
, 1, "" );
659 //kDebug( 5326 )<<"Fingerprint:"<<fingerprint;
660 assert( subkey
!= 0 );
661 subkey
->setFingerprint( fingerprint
);
664 { // line contains an additional user id
666 // Test key (2nd user ID) <abc@xyz>
668 //kDebug( 5326 )<<"User ID:"<<output.mid( pos, eol-pos );
669 key
->addUserID( output
.mid( pos
, eol
-pos
) );
672 else if( !strncmp( output
.data() + offset
, "sig", 3 ) )
673 { // line contains signature data (ignored)
674 //kDebug( 5326 )<<"Signature.";
676 else // end of key data
685 // set the global key capabilities
686 key
->setCanEncrypt( canEncr
);
687 key
->setCanSign( canSign
);
688 key
->setCanCertify( canSign
);
689 //kDebug( 5326 )<<"Key capabilities:"<<(canEncr?"E":"")<<(canSign?"SC":"");
697 Base6::parseSingleKey( const QByteArray
& output
, Key
* key
/* = 0 */ )
701 // search start of header line
702 if( !strncmp( output
.data(), "Type bits", 9 ) )
706 offset
= output
.indexOf( "\nType bits" );
713 // key data begins in the next line
714 offset
= output
.indexOf( '\n', offset
) + 1;
718 key
= parseKeyData( output
, offset
, key
);
720 //kDebug( 5326 ) <<"finished parsing keys";
727 Base6::parseKeyList( const QByteArray
& output
, bool secretKeys
)
729 kDebug( 5326 ) <<"Kpgp::Base6::parseKeyList()";
734 // search start of header line
735 if( !strncmp( output
.data(), "Type bits", 9 ) )
739 offset
= output
.indexOf( "\nType bits" ) + 1;
744 // key data begins in the next line
745 offset
= output
.indexOf( '\n', offset
) + 1;
751 key
= parseKeyData( output
, offset
);
754 key
->setSecret( secretKeys
);
760 //kDebug( 5326 ) <<"finished parsing keys";
767 Base6::parseTrustDataForKey( Key
* key
, const QByteArray
& str
)
769 if( ( key
== 0 ) || str
.isEmpty() )
772 QByteArray keyID
= "0x" + key
->primaryKeyID();
773 UserIDList userIDs
= key
->userIDs();
775 // search the start of the trust data
776 int offset
= str
.indexOf( "\n\n KeyID" );
780 offset
= str
.indexOf( '\n', offset
) + 1;
784 bool ultimateTrust
= false;
785 if( !strncmp( str
.data() + offset
+13, "ultimate", 8 ) )
786 ultimateTrust
= true;
789 { // loop over all trust information about this key
793 // search the end of the current line
794 if( ( eol
= str
.indexOf( '\n', offset
) ) == -1 )
797 if( str
[offset
+23] != ' ' )
798 { // line contains a validity value for a user ID
800 // determine the validity
801 Validity validity
= KPGP_VALIDITY_UNKNOWN
;
802 if( !strncmp( str
.data() + offset
+23, "complete", 8 ) )
804 validity
= KPGP_VALIDITY_ULTIMATE
;
806 validity
= KPGP_VALIDITY_FULL
;
807 else if( !strncmp( str
.data() + offset
+23, "marginal", 8 ) )
808 validity
= KPGP_VALIDITY_MARGINAL
;
809 else if( !strncmp( str
.data() + offset
+23, "invalid", 7 ) )
810 validity
= KPGP_VALIDITY_UNDEFINED
;
812 // determine the user ID
813 int pos
= offset
+ 33;
814 QString uid
= str
.mid( pos
, eol
-pos
);
816 // set the validity of the corresponding user ID
817 for( UserIDList::Iterator it
= userIDs
.begin(); it
!= userIDs
.end(); ++it
)
818 if( (*it
)->text() == uid
)
820 kDebug( 5326 )<<"Setting the validity of"<<uid
<<" to"<<validity
;
821 (*it
)->setValidity( validity
);