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
23 #include <string.h> /* strncmp */
50 Base5::encrypt( Block
& block
, const KeyIDList
& recipients
)
52 return encsign( block
, recipients
, 0 );
57 Base5::clearsign( Block
& block
, const char *passphrase
)
59 return encsign( block
, KeyIDList(), passphrase
);
64 Base5::encsign( Block
& block
, const KeyIDList
& recipients
,
65 const char *passphrase
)
70 // used to work around a bug in pgp5. pgp5 treats files
71 // with non ascii chars (umlauts, etc...) as binary files, but
72 // we want a clear signature
73 bool signonly
= false;
75 if(!recipients
.isEmpty() && passphrase
!= 0)
76 cmd
= "pgpe +batchmode -afts ";
77 else if(!recipients
.isEmpty())
78 cmd
= "pgpe +batchmode -aft ";
79 else if(passphrase
!= 0)
81 cmd
= "pgps +batchmode -abft ";
86 errMsg
= i18n("Neither recipients nor passphrase specified.");
93 if(!recipients
.isEmpty())
95 if(Module::getKpgp()->encryptToSelf())
98 cmd
+= Module::getKpgp()->user();
100 KeyIDList::ConstIterator
end( recipients
.constEnd() );
102 for( KeyIDList::ConstIterator it
= recipients
.constBegin();
110 input
= block
.text();
115 //input.replace(QRegExp("[ \t]+\n"), "\n"); //strip trailing whitespace
116 input
= input
.trimmed(); // PORT: check if that change was ok!
118 //We have to do this otherwise it's all in vain
120 exitStatus
= run(cmd
.data(), passphrase
);
121 block
.setError( error
);
126 // now parse the returned info
127 if(error
.contains("Cannot unlock private key") )
129 errMsg
= i18n("The passphrase you entered is invalid.");
133 //if(!ignoreUntrusted)
137 while((index
= error
.indexOf("WARNING: The above key",index
+1)) != -1 )
139 int index2
= error
.indexOf("But you previously",index
);
140 int index3
= error
.indexOf("WARNING: The above key",index
+1);
141 if(index2
== -1 || (index2
> index3
&& index3
!= -1))
143 // the key wasn't valid, no encryption to this person
144 // extract the person
145 index2
= error
.indexOf('\n',index
);
146 index3
= error
.indexOf('\n',index2
+1);
147 aStr
+= error
.mid(index2
+1, index3
-index2
-1);
153 aStr
.truncate(aStr
.length()-2);
154 if(error
.contains("No valid keys found") )
155 errMsg
= i18n("The key(s) you want to encrypt your message "
156 "to are not trusted. No encryption done.");
158 errMsg
= i18n("The following key(s) are not trusted:\n%1\n"
159 "Their owner(s) will not be able to decrypt the message.",
160 QString::fromLocal8Bit( aStr
));
165 if((index
= error
.indexOf("No encryption keys found for")) != -1 )
167 index
= error
.indexOf(':',index
);
168 int index2
= error
.indexOf('\n',index
);
170 errMsg
= i18n("Missing encryption key(s) for:\n%1",
171 QString::fromLocal8Bit(error
.mid(index
,index2
-index
)));
172 // errMsg = QString("Missing encryption key(s) for: %1")
173 // .arg(error.mid(index,index2-index));
175 status
|= MISSINGKEY
;
179 // dash-escape the input
181 input
= "- " + input
;
182 for ( int idx
= 0 ; (idx
= input
.indexOf("\n-", idx
)) != -1 ; idx
+= 4 )
183 input
.replace(idx
, 2, "\n- -");
185 output
= "-----BEGIN PGP SIGNED MESSAGE-----\n\n" + input
+ '\n' + output
;
188 block
.setProcessedText( output
);
189 block
.setStatus( status
);
195 Base5::decrypt( Block
& block
, const char *passphrase
)
198 input
= block
.text();
199 int exitStatus
= run("pgpv -f +batchmode=1", passphrase
);
200 if( !output
.isEmpty() )
201 block
.setProcessedText( output
);
202 block
.setError( error
);
204 if(exitStatus
== -1) {
205 errMsg
= i18n("Error running PGP");
207 block
.setStatus( status
);
211 // lets parse the returned information.
212 int index
= error
.indexOf("Cannot decrypt message");
215 //kDebug( 5326 ) <<"message is encrypted";
218 // ok. we have an encrypted message. Is the passphrase bad,
219 // or do we not have the secret key?
220 if(error
.contains("Need a pass phrase") )
224 errMsg
= i18n("Bad passphrase; could not decrypt.");
225 kDebug( 5326 ) <<"Base: passphrase is bad";
232 // we don't have the secret key
233 status
|= NO_SEC_KEY
;
235 errMsg
= i18n("You do not have the secret key needed to decrypt this message.");
236 kDebug( 5326 ) <<"Base: no secret key for this message";
240 // ##### FIXME: This information is anyway currently not used
241 // I'll change it to always determine the recipients.
242 index
= error
.indexOf("can only be decrypted by:");
245 index
= error
.indexOf('\n',index
);
246 int end
= error
.indexOf("\n\n",index
);
250 while( (index2
= error
.indexOf('\n',index
+1)) <= end
)
252 QByteArray item
= error
.mid(index
+1,index2
-index
-1);
254 mRecipients
.append(item
);
260 index
= error
.indexOf("Good signature");
263 //kDebug( 5326 ) <<"good signature";
267 // get key ID of signer
268 index
= error
.indexOf("Key ID ", index
) + 7;
269 block
.setSignatureKeyId( error
.mid(index
, 8) );
272 index
= error
.indexOf('"',index
) + 1;
273 int index2
= error
.indexOf('"', index
);
274 block
.setSignatureUserId( error
.mid(index
, index2
-index
) );
276 /// ### FIXME get signature date
277 block
.setSignatureDate( "" );
279 index
= error
.indexOf("BAD signature");
282 //kDebug( 5326 ) <<"BAD signature";
286 // get key ID of signer
287 index
= error
.indexOf("Key ID ", index
) + 7;
288 block
.setSignatureKeyId( error
.mid(index
, 8) );
291 index
= error
.indexOf('"',index
) + 1;
292 int index2
= error
.indexOf('"', index
);
293 block
.setSignatureUserId( error
.mid(index
, index2
-index
) );
295 /// ### FIXME get signature date
296 block
.setSignatureDate( "" );
298 index
= error
.indexOf("Signature by unknown key");
301 index
= error
.indexOf("keyid: 0x",index
) + 9;
302 block
.setSignatureKeyId( error
.mid(index
, 8) );
303 block
.setSignatureUserId( QString() );
304 // FIXME: not a very good solution...
308 /// ### FIXME get signature date
309 block
.setSignatureDate( "" );
312 //kDebug( 5326 ) <<"status =" << status;
313 block
.setStatus( status
);
319 Base5::readPublicKey( const KeyID
& keyId
, const bool readTrust
, Key
* key
)
322 int exitStatus
= run( "pgpk -ll 0x" + keyId
, 0, true );
324 if(exitStatus
!= 0) {
329 key
= parseSingleKey( output
, key
);
338 exitStatus
= run( "pgpk -c 0x" + keyId
, 0, true );
340 if(exitStatus
!= 0) {
345 parseTrustDataForKey( key
, output
);
353 Base5::publicKeys( const QStringList
& patterns
)
357 QByteArray cmd
= "pgpk -ll";
359 QStringList::ConstIterator
end( patterns
.end() );
362 for ( QStringList::ConstIterator it
= patterns
.constBegin();
365 cmd
+= KShell::quoteArg( *it
).toLocal8Bit();
368 exitStatus
= run( cmd
, 0, true );
370 if(exitStatus
!= 0) {
375 // now we need to parse the output for public keys
376 KeyList keys
= parseKeyList( output
, false );
378 // sort the list of public keys
379 std::sort( keys
.begin(), keys
.end(), KeyCompare
);
386 Base5::secretKeys( const QStringList
& patterns
)
388 QByteArray cmd
= "pgpk -ll";
389 QStringList::ConstIterator
end( patterns
.constEnd() );
390 for ( QStringList::ConstIterator it
= patterns
.constBegin();
393 cmd
+= KShell::quoteArg( *it
).toLocal8Bit();
396 int exitStatus
= run( cmd
, 0, true );
398 if(exitStatus
!= 0) {
403 // now we need to parse the output for secret keys
404 KeyList keys
= parseKeyList( output
, true );
406 // sort the list of public keys
407 std::sort( keys
.begin(), keys
.end(), KeyCompare
);
413 QByteArray
Base5::getAsciiPublicKey(const KeyID
& keyID
)
419 int exitStatus
= run( "pgpk -xa 0x" + keyID
, 0, true );
421 if(exitStatus
!= 0) {
431 Base5::signKey(const KeyID
& keyID
, const char *passphrase
)
433 if(passphrase
== 0) return false;
436 cmd
= "pgpk -s -f +batchmode=1 0x";
441 int exitStatus
= run(cmd
.data(), passphrase
);
449 //-- private functions --------------------------------------------------------
452 Base5::parseKeyData( const QByteArray
& output
, int& offset
, Key
* key
/* = 0 */ )
453 // This function parses the data for a single key which is output by PGP 5
454 // with the following command line:
456 // It expects the key data to start at offset and returns the start of
457 // the next key's data in offset.
459 if( ( strncmp( output
.data() + offset
, "pub", 3 ) != 0 ) &&
460 ( strncmp( output
.data() + offset
, "sec", 3 ) != 0 ) )
462 kDebug( 5326 ) <<"Unknown key type or corrupt key data.";
472 bool primaryKey
= true;
478 // search the end of the current line
479 eol
= output
.indexOf( '\n', offset
);
480 if( ( eol
== -1 ) || ( eol
== offset
) )
483 //kDebug( 5326 ) <<"Parsing:" << output.mid(offset, eol-offset);
485 if( !strncmp( output
.data() + offset
, "pub", 3 ) ||
486 !strncmp( output
.data() + offset
, "sec", 3 ) ||
487 !strncmp( output
.data() + offset
, "sub", 3 ) )
488 { // line contains key data
489 //kDebug( 5326 )<<"Key data:";
492 subkey
= new Subkey( "", false );
493 key
->addSubkey( subkey
);
496 /* From the PGP 5 manual page for pgpk:
497 Following this column is a single character which
498 describes other attributes of the object:
500 @ The object is disabled
501 + The object is axiomatically trusted (i.e., it's
504 switch( output
[offset
+3] )
506 case ' ': // nothing special
508 case '@': // disabled key
509 subkey
->setDisabled( true );
510 key
->setDisabled( true );
512 default: // all other flags are ignored
513 //kDebug( 5326 ) <<"Unknown key flag.";
519 while( output
[pos
] == ' ' )
521 pos2
= output
.indexOf( ' ', pos
);
522 subkey
->setKeyLength( output
.mid( pos
, pos2
-pos
).toUInt() );
523 //kDebug( 5326 ) <<"Key Length:"<<subkey->keyLength();
527 while( output
[pos
] == ' ' )
529 pos
+= 2; // skip the '0x'
530 pos2
= output
.indexOf( ' ', pos
);
531 subkey
->setKeyID( output
.mid( pos
, pos2
-pos
) );
532 //kDebug( 5326 ) <<"Key ID:"<<subkey->keyID();
536 while( output
[pos
] == ' ' )
538 pos2
= output
.indexOf( ' ', pos
);
539 int year
= output
.mid( pos
, 4 ).toInt();
540 int month
= output
.mid( pos
+5, 2 ).toInt();
541 int day
= output
.mid( pos
+8, 2 ).toInt();
542 QDateTime
dt( QDate( year
, month
, day
), QTime( 00, 00 ) );
543 QDateTime
epoch( QDate( 1970, 01, 01 ), QTime( 00, 00 ) );
544 // The calculated creation date isn't exactly correct because QDateTime
545 // doesn't know anything about timezones and always assumes local time
546 // although epoch is of course UTC. But as PGP 5 anyway doesn't print
547 // the time this doesn't matter too much.
548 subkey
->setCreationDate( epoch
.secsTo( dt
) );
551 // if the primary key has been revoked the expiration date is not printed
552 if( primaryKey
|| !key
->revoked() )
555 while( output
[pos
] == ' ' )
557 pos2
= output
.indexOf( ' ', pos
);
558 if( output
[pos
] == '-' )
559 { // key doesn't expire
560 subkey
->setExpirationDate( -1 );
562 else if( !strncmp( output
.data() + pos
, "*REVOKED*", 9 ) )
563 { // key has been revoked
564 subkey
->setRevoked( true );
565 key
->setRevoked( true );
569 int year
= output
.mid( pos
, 4 ).toInt();
570 int month
= output
.mid( pos
+5, 2 ).toInt();
571 int day
= output
.mid( pos
+8, 2 ).toInt();
572 QDateTime
dt( QDate( year
, month
, day
), QTime( 00, 00 ) );
573 subkey
->setCreationDate( epoch
.secsTo( dt
) );
574 // has the key already expired?
575 if( QDateTime::currentDateTime() >= dt
)
577 subkey
->setExpired( true );
578 key
->setExpired( true );
582 else if( key
->revoked() )
583 subkey
->setRevoked( true );
585 // Key algorithm (RSA, DSS, Diffie-Hellman)
589 while( output
[pos
] == ' ' )
591 pos2
= output
.indexOf( ' ', pos
);
592 if( !strncmp( output
.data() + pos
, "RSA", 3 ) )
597 else if( !strncmp( output
.data() + pos
, "DSS", 3 ) )
599 else if( !strncmp( output
.data() + pos
, "Diffie-Hellman", 14 ) )
602 kDebug( 5326 )<<"Unknown key algorithm";
604 // set key capabilities of the subkey
605 subkey
->setCanEncrypt( encr
);
606 subkey
->setCanSign( sign
);
607 subkey
->setCanCertify( sign
);
611 // Global key capabilities
612 bool canSign
= false;
613 bool canEncr
= false;
615 while( output
[pos
] == ' ' )
618 if( !strncmp( output
.data() + pos
, "Sign & Encrypt", 14 ) )
623 else if( !strncmp( output
.data() + pos
, "Sign only", 9 ) )
625 else if( !strncmp( output
.data() + pos
, "Encrypt only", 12 ) )
628 kDebug( 5326 )<<"Unknown key capability";
630 // set the global key capabilities
631 if( !key
->expired() && !key
->revoked() )
633 key
->setCanEncrypt( canEncr
);
634 key
->setCanSign( canSign
);
635 key
->setCanCertify( canSign
);
637 //kDebug( 5326 )<<"Key capabilities:"<<(key->canEncrypt()?"E":"")<<(key->canSign()?"SC":"");
641 else if( !strncmp( output
.data() + offset
, "f16", 3 ) ||
642 !strncmp( output
.data() + offset
, "f20", 3 ) )
643 { // line contains a fingerprint
645 f16 Fingerprint16 = DE 2A 77 08 78 64 7C 42 72 75 B1 A7 3E 42 3F 79
646 f20 Fingerprint20 = 226F 4B63 6DA2 7389 91D1 2A49 D58A 3EC1 5214 181E
649 int pos
= output
.indexOf( '=', offset
+3 ) + 2;
650 QByteArray fingerprint
= output
.mid( pos
, eol
-pos
);
651 // remove white space from the fingerprint
652 for ( int idx
= 0 ; (idx
= fingerprint
.indexOf(' ', idx
)) != -1 ; )
653 fingerprint
.replace( idx
, 1, "" );
654 assert( subkey
!= 0 );
655 subkey
->setFingerprint( fingerprint
);
656 //kDebug( 5326 )<<"Fingerprint:"<<fingerprint;
658 else if( !strncmp( output
.data() + offset
, "uid", 3 ) )
659 { // line contains a uid
661 QByteArray uid
= output
.mid( pos
, eol
-pos
);
662 key
->addUserID( uid
);
663 // displaying of uids which contain non-ASCII characters is broken in
664 // PGP 5.0i; it shows these characters as \ooo and truncates the uid
665 // because it doesn't take the 3 extra characters per non-ASCII char
666 // into account. Example (with an UTF-8 encoded ö):
667 // uid Ingo Kl\303\266cker <ingo.kloecker@epo
668 // because of this and because we anyway don't know which charset was
669 // used to encode the uid we don't try to decode it
671 else if ( !strncmp( output
.data() + offset
, "sig", 3 ) ||
672 !strncmp( output
.data() + offset
, "SIG", 3 ) ||
673 !strncmp( output
.data() + offset
, "ret", 3 ) )
674 { // line contains a signature
675 // SIG = sig with own key; ret = sig with revoked key
676 // we ignore it for now
687 Base5::parseSingleKey( const QByteArray
& output
, Key
* key
/* = 0 */ )
691 // search start of header line
692 if( !strncmp( output
.data(), "Type Bits", 9 ) )
696 offset
= output
.indexOf( "\nType Bits" ) + 1;
701 // key data begins in the next line
702 offset
= output
.indexOf( '\n', offset
) + 1;
706 key
= parseKeyData( output
, offset
, key
);
708 //kDebug( 5326 ) <<"finished parsing keys";
715 Base5::parseKeyList( const QByteArray
& output
, bool onlySecretKeys
)
721 // search start of header line
722 if( !strncmp( output
.data(), "Type Bits", 9 ) )
726 offset
= output
.indexOf( "\nType Bits" ) + 1;
731 // key data begins in the next line
732 offset
= output
.indexOf( '\n', offset
) + 1;
738 key
= parseKeyData( output
, offset
);
741 // if only secret keys should be read test if the key is secret
742 if( !onlySecretKeys
|| !key
->secret() )
744 // skip the blank line which separates the keys
750 //kDebug( 5326 ) <<"finished parsing keys";
757 Base5::parseTrustDataForKey( Key
* key
, const QByteArray
& str
)
759 if( ( key
== 0 ) || str
.isEmpty() )
762 QByteArray keyID
= "0x" + key
->primaryKeyID();
763 UserIDList userIDs
= key
->userIDs();
765 // search the start of the trust data
766 int offset
= str
.indexOf( "\n\n KeyID" ) + 9;
767 if( offset
== -1 + 9 )
770 offset
= str
.indexOf( '\n', offset
) + 1;
771 if( offset
== -1 + 1 )
774 bool ultimateTrust
= false;
775 if( !strncmp( str
.data() + offset
+13, "ultimate", 8 ) )
776 ultimateTrust
= true;
779 { // loop over all trust information about this key
783 // search the end of the current line
784 if( ( eol
= str
.indexOf( '\n', offset
) ) == -1 )
787 if( str
[offset
+23] != ' ' )
788 { // line contains a validity value for a user ID
790 // determine the validity
791 Validity validity
= KPGP_VALIDITY_UNKNOWN
;
792 if( !strncmp( str
.data() + offset
+23, "complete", 8 ) )
794 validity
= KPGP_VALIDITY_ULTIMATE
;
796 validity
= KPGP_VALIDITY_FULL
;
797 else if( !strncmp( str
.data() + offset
+23, "marginal", 8 ) )
798 validity
= KPGP_VALIDITY_MARGINAL
;
799 else if( !strncmp( str
.data() + offset
+23, "invalid", 7 ) )
800 validity
= KPGP_VALIDITY_UNDEFINED
;
802 // determine the user ID
803 int pos
= offset
+ 33;
804 QString uid
= str
.mid( pos
, eol
-pos
);
806 // set the validity of the corresponding user ID
807 for( UserIDList::Iterator it
= userIDs
.begin(); it
!= userIDs
.end(); ++it
)
808 if( (*it
)->text() == uid
)
810 kDebug( 5326 )<<"Setting the validity of"<<uid
<<" to"<<validity
;
811 (*it
)->setValidity( validity
);