00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "KoXmlWriter.h"
00021 #include <KoXmlReader.h>
00022 #include "KoZipStore.h"
00023 #include <QString>
00024 #include <QByteArray>
00025 #include <QIODevice>
00026 #include <QWidget>
00027 #include <QtCrypto>
00028 #include <kpassworddialog.h>
00029 #include "KoEncryptedStore.h"
00030 #include <kwallet.h>
00031 #include <klocale.h>
00032 #include <kfilterdev.h>
00033 #include <kmessage.h>
00034 #include <kmessagebox.h>
00035
00036 struct KoEncryptedStore_EncryptionData {
00037
00038 QSecureArray salt;
00039 unsigned int iterationCount;
00040
00041
00042 QSecureArray initVector;
00043
00044
00045 QSecureArray checksum;
00046
00047 bool checksumShort;
00048
00049
00050 qint64 filesize;
00051 };
00052
00053 namespace {
00054 const char* MANIFEST_FILE = "META-INF/manifest.xml";
00055 const char* META_FILE = "meta.xml";
00056 const char* THUMBNAIL_FILE = "Thumbnails/thumbnail.png";
00057 }
00058
00059 KoEncryptedStore::KoEncryptedStore( const QString & filename, Mode mode, const QByteArray & appIdentification )
00060 : m_init_url( NULL ), m_init_dev( NULL ), m_init_deferred( false ), m_init_appIdentification( appIdentification ), m_qcaInit( QCA::Initializer() ), m_password( QSecureArray() ), m_window( NULL ), m_filename( QString( filename ) ), m_manifestBuffer( QByteArray() ) {
00061 if( mode == Write ) {
00062
00063 m_bGood = QCA::isSupported( "sha1" ) && QCA::isSupported( "pbkdf2(sha1)" ) && init( mode, appIdentification );
00064 m_store = NULL;
00065 m_init_deferred = true;
00066 return;
00067 }
00068 m_store = new KoZipStore( filename, mode, appIdentification );
00069 m_bGood = m_store && !m_store->bad( );
00070
00071 if( m_bGood ) {
00072 m_bGood = init( mode, appIdentification );
00073 }
00074 }
00075
00076 KoEncryptedStore::KoEncryptedStore( QIODevice *dev, Mode mode, const QByteArray & appIdentification )
00077 : m_init_url( NULL ), m_init_dev( dev ), m_init_deferred( false ), m_init_appIdentification( appIdentification ), m_qcaInit( QCA::Initializer() ), m_password( QSecureArray() ), m_window( NULL ), m_filename( QString( ) ), m_manifestBuffer( QByteArray() ) {
00078 if( mode == Write ) {
00079
00080 m_bGood = QCA::isSupported( "sha1" ) && QCA::isSupported( "pbkdf2(sha1)" ) && init( mode, appIdentification );
00081 m_store = NULL;
00082 m_init_deferred = true;
00083 return;
00084 }
00085 m_store = new KoZipStore( dev, mode, appIdentification );
00086 m_bGood = m_store && !m_store->bad( );
00087
00088 if( m_bGood ) {
00089 m_bGood = init( mode, appIdentification );
00090 }
00091 }
00092
00093 KoEncryptedStore::KoEncryptedStore( QWidget* window, const KUrl& url, const QString & filename, Mode mode, const QByteArray & appIdentification )
00094 : m_init_url( url ), m_init_dev( NULL ), m_init_deferred( false ), m_init_appIdentification( appIdentification ), m_qcaInit( QCA::Initializer() ), m_password( QSecureArray() ), m_window( window ), m_filename( QString( url.url( ) ) ), m_manifestBuffer( QByteArray() ) {
00095 if( mode == Write ) {
00096
00097 m_bGood = QCA::isSupported( "sha1" ) && QCA::isSupported( "pbkdf2(sha1)" ) && init( mode, appIdentification );
00098 m_store = NULL;
00099 m_init_deferred = true;
00100 return;
00101 }
00102 m_store = new KoZipStore( window, url, filename, mode, appIdentification );
00103 m_bGood = m_store && !m_store->bad( );
00104
00105 if( m_bGood ) {
00106 m_bGood = init( mode, appIdentification );
00107 }
00108 }
00109
00110 KoEncryptedStore::~KoEncryptedStore() {
00111 if( isOpen( ) ) {
00112 close( );
00113 }
00114 if( m_store ) {
00115 if( m_store->isOpen( ) ) {
00116 m_store->close( );
00117 }
00118 if( m_mode == Write ) {
00119
00120
00121
00122
00123 QDomDocument document;
00124 if( m_manifestBuffer.isEmpty( ) ) {
00125
00126 document = QDomDocument::QDomDocument( );
00127 QDomElement rootElement = document.createElement( "manifest:manifest" );
00128 rootElement.setAttribute( "xmlns:manifest", "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" );
00129 document.appendChild( rootElement );
00130 }
00131 if( !m_manifestBuffer.isEmpty( ) && !document.setContent( m_manifestBuffer ) ) {
00132
00133
00134 KMessage::message( KMessage::Error, i18n( "The manifest file seems to be corrupted. It cannot be modified and the document will remain unreadable. Please try and save the document again to prevent losing your work." ) );
00135 }
00136 else {
00137 QDomElement documentElement = document.documentElement( );
00138 QDomNodeList fileElements = documentElement.elementsByTagName( "manifest:file-entry" );
00139
00140 QStringList foundFiles;
00141 for( int i = 0; i < fileElements.size( ); i++ ) {
00142 printf( "Tussen, nummertje %d:\n\n%s\n\n", i, document.toByteArray( ).data( ) );
00143 QDomElement fileElement = fileElements.item( i ).toElement( );
00144 QString fullpath = fileElement.toElement( ).attribute( "manifest:full-path" );
00145
00146 if( fullpath.isEmpty( ) || !m_encryptionData.contains( normalizedFullPath( fullpath ) ) ) {
00147 continue;
00148 }
00149 foundFiles += normalizedFullPath( fullpath );
00150 KoEncryptedStore_EncryptionData encData = m_encryptionData.value( normalizedFullPath( fullpath ) );
00151
00152 fileElement.setAttribute( "manifest:size", encData.filesize );
00153
00154 QDomNodeList childElements = fileElement.elementsByTagName( "manifest:encryption-data" );
00155 QDomElement encryptionElement;
00156 QDomElement algorithmElement;
00157 QDomElement keyDerivationElement;
00158 if( childElements.isEmpty( ) ) {
00159 encryptionElement = document.createElement( "manifest:encryption-data" );
00160 fileElement.appendChild( encryptionElement );
00161 }
00162 else {
00163 encryptionElement = childElements.item( 0 ).toElement( );
00164 }
00165 childElements = encryptionElement.elementsByTagName( "manifest:algorithm" );
00166 if( childElements.isEmpty( ) ) {
00167 algorithmElement = document.createElement( "manifest:algorithm" );
00168 encryptionElement.appendChild( algorithmElement );
00169 }
00170 else {
00171 algorithmElement = childElements.item( 0 ).toElement( );
00172 }
00173 childElements = encryptionElement.elementsByTagName( "manifest:key-derivation" );
00174 if( childElements.isEmpty( ) ) {
00175 keyDerivationElement = document.createElement( "manifest:key-derivation" );
00176 encryptionElement.appendChild( keyDerivationElement );
00177 }
00178 else {
00179 keyDerivationElement = childElements.item( 0 ).toElement( );
00180 }
00181
00182 QCA::Base64 encoder;
00183 QSecureArray checksum = encoder.update( encData.checksum );
00184 checksum += encoder.final( );
00185
00186 if( encData.checksumShort ) {
00187
00188 }
00189 else {
00190
00191 }
00192 encoder.clear( );
00193 QSecureArray initVector = encoder.update( encData.initVector );
00194 initVector += encoder.final( );
00195 algorithmElement.setAttribute( "manifest:initialisation-vector", QString( initVector.toByteArray( ) ) );
00196 algorithmElement.setAttribute( "manifest:algorithm-name", "Blowfish CFB" );
00197 encoder.clear( );
00198 QSecureArray salt = encoder.update( encData.salt );
00199 salt += encoder.final( );
00200 keyDerivationElement.setAttribute( "manifest:salt", QString( salt.toByteArray( ) ) );
00201 keyDerivationElement.setAttribute( "manifest:key-derivation-name", "PBKDF2" );
00202 }
00203 if( foundFiles.size( ) < m_encryptionData.size( ) ) {
00204 QList<QString> keys = m_encryptionData.keys( );
00205 for( int i = 0; i < keys.size( ); i++ ) {
00206 if( !foundFiles.contains( normalizedFullPath( keys.value( i ) ) ) ) {
00207 KoEncryptedStore_EncryptionData encData = m_encryptionData.value( normalizedFullPath( keys.value( i ) ) );
00208 QDomElement fileElement = document.createElement( "manifest:file-entry" );
00209 fileElement.setAttribute( "manifest:full-path", normalizedFullPath( keys.value( i ) ).mid( 1 ) );
00210 fileElement.setAttribute( "manifest:size", encData.filesize );
00211 fileElement.setAttribute( "manifest:media-type", "" );
00212 documentElement.appendChild( fileElement );
00213 QDomElement encryptionElement = document.createElement( "manifest:encryption-data" );
00214 QCA::Base64 encoder;
00215 QSecureArray checksum = encoder.update( encData.checksum );
00216 checksum += encoder.final( );
00217 encoder.clear( );
00218 QSecureArray initVector = encoder.update( encData.initVector );
00219 initVector += encoder.final( );
00220 encoder.clear( );
00221 QSecureArray salt = encoder.update( encData.salt );
00222 salt += encoder.final( );
00223 encryptionElement.setAttribute( "manifest:checksum", QString( checksum.toByteArray( ) ) );
00224 if( encData.checksumShort ) {
00225 encryptionElement.setAttribute( "manifest:checksum-type", "SHA1/1K" );
00226 }
00227 else {
00228 encryptionElement.setAttribute( "manifest:checksum-type", "SHA1" );
00229 }
00230 fileElement.appendChild( encryptionElement );
00231 QDomElement algorithmElement = document.createElement( "manifest:algorithm" );
00232 algorithmElement.setAttribute( "manifest:algorithm-name", "Blowfish CFB" );
00233 algorithmElement.setAttribute( "manifest:initialisation-vector", QString( initVector.toByteArray( ) ) );
00234 encryptionElement.appendChild( algorithmElement );
00235 QDomElement keyDerivationElement = document.createElement( "manifest:key-derivation" );
00236 keyDerivationElement.setAttribute( "manifest:key-derivation-name", "PBKDF2" );
00237 keyDerivationElement.setAttribute( "manifest:salt", QString( salt.toByteArray( ) ) );
00238 encryptionElement.appendChild( keyDerivationElement );
00239 }
00240 }
00241 }
00242 printf( "Voor:\n\n%s\n", m_manifestBuffer.data( ) );
00243 m_manifestBuffer = document.toByteArray( );
00244 printf( "\nNa:\n\n%s\n\n", m_manifestBuffer.data( ) );
00245 if( !m_store->open( MANIFEST_FILE ) ) {
00246 KMessage::message( KMessage::Error, i18n( "The manifest file cannot be opened. The document will remain unreadable. Please try and save the document again to prevent losing your work." ) );
00247 }
00248 else {
00249 if( static_cast<KoStore *>( m_store )->write( m_manifestBuffer ) != m_manifestBuffer.size( ) ) {
00250 KMessage::message( KMessage::Error, i18n( "The manifest file cannot be opened. The document will remain unreadable. Please try and save the document again to prevent losing your work." ) );
00251 }
00252 m_store->close( );
00253 }
00254 }
00255 }
00256
00257 delete m_store;
00258 }
00259 }
00260
00261 KoZipStore* KoEncryptedStore::ripZipStore( ) {
00262 if( !initBackend( ) ) {
00263 return NULL;
00264 }
00265 if( m_store ) {
00266 KoZipStore *store = m_store;
00267 m_store = NULL;
00268 return store;
00269 }
00270 return NULL;
00271 }
00272
00273 bool KoEncryptedStore::initBackend( ) {
00274 if( m_init_deferred ) {
00275 if( m_init_dev ) {
00276 m_store = new KoZipStore( m_init_dev, m_mode, m_init_appIdentification );
00277 m_init_dev = NULL;
00278 }
00279 else if( !m_init_url.isEmpty( ) ) {
00280 m_store = new KoZipStore( m_window, m_init_url, m_filename, m_mode, m_init_appIdentification );
00281 m_init_url = KUrl( );
00282 }
00283 else {
00284 m_store = new KoZipStore( m_filename, m_mode, m_init_appIdentification );
00285 }
00286 m_init_deferred = false;
00287 if( !m_store || m_store->bad( ) ) {
00288 m_bGood = false;
00289 if( isOpen( ) ) {
00290 close( );
00291 }
00292 return false;
00293 }
00294 if( isOpen( ) ) {
00295 if( !m_store->open( normalizedFullPath( m_sName ) ) ) {
00296 m_bGood = false;
00297 close( );
00298 return false;
00299 }
00300 }
00301 m_init_deferred = false;
00302 }
00303 return true;
00304 }
00305
00306 KoStore* KoEncryptedStore::createEncryptedStoreReader( const QString & filename, const QByteArray & appIdentification ) {
00307 KoEncryptedStore *encStore = new KoEncryptedStore( filename, Read, appIdentification );
00308 if( encStore->isEncrypted( ) ) {
00309 return encStore;
00310 }
00311 KoZipStore *zipStore = encStore->ripZipStore( );
00312 delete encStore;
00313 return zipStore;
00314 }
00315
00316 KoStore* KoEncryptedStore::createEncryptedStoreReader( QIODevice *dev, const QByteArray & appIdentification ) {
00317 KoEncryptedStore *encStore = new KoEncryptedStore( dev, Read, appIdentification );
00318 if( encStore->isEncrypted( ) ) {
00319 return encStore;
00320 }
00321 KoZipStore *zipStore = encStore->ripZipStore( );
00322 delete encStore;
00323 return zipStore;
00324 }
00325
00326 KoStore* KoEncryptedStore::createEncryptedStoreReader( QWidget* window, const KUrl& url, const QString & filename, const QByteArray & appIdentification ) {
00327 KoEncryptedStore *encStore = new KoEncryptedStore( window, url, filename, Read, appIdentification );
00328 if( encStore->isEncrypted( ) ) {
00329 return encStore;
00330 }
00331 KoZipStore *zipStore = encStore->ripZipStore( );
00332 delete encStore;
00333 return zipStore;
00334 }
00335
00336 bool KoEncryptedStore::init( Mode mode, const QByteArray& ) {
00337 KoStore::init( mode );
00338 m_mode = mode;
00339 if( mode == Read ) {
00340
00341 m_store->pushDirectory( );
00342 bool ok = m_store->open( "tar:/META-INF/manifest.xml" );
00343 if( !ok ) {
00344 if( !m_store->bad( ) && !m_store->hasFile( "tar:/META-INF/manifest.xml" ) ) {
00345 return true;
00346 }
00347 m_bGood = false;
00348 return false;
00349 }
00350 QIODevice *dev = m_store->device( );
00351
00352 KoXmlDocument xmldoc;
00353 if( !xmldoc.setContent( dev ) ) {
00354 KMessage::message( KMessage::Warning, i18n( "The manifest file seems to be corrupted. The document could not be opened." ) );
00355 m_store->close( );
00356 m_bGood = false;
00357 return false;
00358 }
00359 KoXmlElement xmlroot = xmldoc.documentElement( );
00360 if( xmlroot.tagName( ) != "manifest:manifest" ) {
00361 KMessage::message( KMessage::Warning, i18n( "The manifest file seems to be corrupted. The document could not be opened." ) );
00362 m_store->close( );
00363 m_bGood = false;
00364 return false;
00365 }
00366
00367 if( xmlroot.hasChildNodes( ) ) {
00368 QCA::Base64 base64decoder( QCA::Decode );
00369 KoXmlNode xmlnode = xmlroot.firstChild( );
00370 while( !xmlnode.isNull( ) ) {
00371
00372 if( !xmlnode.isElement( ) || xmlnode.toElement( ).tagName( ) != "manifest:file-entry" || !xmlnode.hasChildNodes( ) || !xmlnode.toElement( ).hasAttribute( "manifest:full-path" ) ) {
00373 xmlnode = xmlnode.nextSibling( );
00374 continue;
00375 }
00376
00377
00378 KoEncryptedStore_EncryptionData encData;
00379 encData.filesize = 0;
00380 encData.checksum = QSecureArray();
00381 encData.checksumShort = false;
00382 encData.salt = QSecureArray();
00383 encData.iterationCount = 0;
00384 encData.initVector = QSecureArray();
00385
00386
00387 QString fullpath = xmlnode.toElement( ).attribute( "manifest:full-path" );
00388 if( xmlnode.toElement( ).hasAttribute( "manifest:size" ) ) {
00389 encData.filesize = xmlnode.toElement( ).attribute( "manifest:size" ).toUInt( );
00390 }
00391
00392
00393 KoXmlNode xmlencnode = xmlnode.firstChild( );
00394 while( !xmlencnode.isNull( ) && ( !xmlencnode.isElement( ) || xmlencnode.toElement( ).tagName( ) != "manifest:encryption-data" || !xmlencnode.hasChildNodes( ) ) ) {
00395 xmlencnode = xmlencnode.nextSibling( );
00396 }
00397 if( xmlencnode.isNull( ) ) {
00398 xmlnode = xmlnode.nextSibling( );
00399 continue;
00400 }
00401
00402
00403 if( xmlencnode.toElement( ).hasAttribute( "manifest:checksum" ) ) {
00404 base64decoder.clear( );
00405 encData.checksum = base64decoder.update( QSecureArray( xmlencnode.toElement( ).attribute( "manifest:checksum" ).toAscii( ) ) );
00406 encData.checksum += base64decoder.final( );
00407 if( xmlencnode.toElement( ).hasAttribute( "manifest:checksum-type" ) ) {
00408 QString checksumType = xmlencnode.toElement( ).attribute( "manifest:checksum-type" );
00409 if( checksumType == "SHA1" ) {
00410 encData.checksumShort = false;
00411 }
00412
00413 else if( checksumType == "SHA1/1K" ) {
00414 encData.checksumShort = true;
00415 }
00416 else {
00417
00418 KMessage::message( KMessage::Warning, i18n( "This document contains an unknown checksum. When you give a password it might not be verified." ) );
00419 encData.checksum = QSecureArray();
00420 }
00421 }
00422 else {
00423 encData.checksumShort = false;
00424 }
00425 }
00426
00427 KoXmlNode xmlencattr = xmlencnode.firstChild( );
00428 bool algorithmFound = false;
00429 bool keyDerivationFound = false;
00430
00431 while( !xmlencattr.isNull( ) ) {
00432 if( !xmlencattr.isElement( ) ) {
00433 xmlencattr = xmlencattr.nextSibling( );
00434 continue;
00435 }
00436
00437
00438 if( xmlencattr.toElement( ).tagName( ) == "manifest:algorithm" && xmlencattr.toElement( ).hasAttribute( "manifest:initialisation-vector" ) ) {
00439 algorithmFound = true;
00440 base64decoder.clear( );
00441 encData.initVector = base64decoder.update( QSecureArray( xmlencattr.toElement( ).attribute( "manifest:initialisation-vector" ).toAscii( ) ) );
00442 encData.initVector += base64decoder.final( );
00443 if( xmlencattr.toElement( ).hasAttribute( "manifest:algorithm-name" ) && xmlencattr.toElement( ).attribute( "manifest:algorithm-name" ) != "Blowfish CFB" ) {
00444 KMessage::message( KMessage::Warning, i18n( "This document contains an unknown encryption method. Some parts may be unreadable." ) );
00445 encData.initVector = QSecureArray();
00446 }
00447 }
00448
00449
00450 if( xmlencattr.toElement( ).tagName( ) == "manifest:key-derivation" && xmlencattr.toElement( ).hasAttribute( "manifest:salt" ) ) {
00451 keyDerivationFound = true;
00452 base64decoder.clear( );
00453 encData.salt = base64decoder.update( QSecureArray( xmlencattr.toElement( ).attribute( "manifest:salt" ).toAscii( ) ) );
00454 encData.salt += base64decoder.final( );
00455 encData.iterationCount = 1024;
00456 if( xmlencattr.toElement( ).hasAttribute( "manifest:iteration-count" ) ) {
00457 encData.iterationCount = xmlencattr.toElement( ).attribute( "manifest:iteration-count" ).toUInt( );
00458 }
00459 if( xmlencattr.toElement( ).hasAttribute( "manifest:key-derivation-name" ) && xmlencattr.toElement( ).attribute( "manifest:key-derivation-name" ) != "PBKDF2" ) {
00460 KMessage::message( KMessage::Warning, i18n( "This document contains an unknown encryption method. Some parts may be unreadable." ) );
00461 encData.salt = QSecureArray();
00462 }
00463 }
00464
00465 xmlencattr = xmlencattr.nextSibling( );
00466 }
00467
00468
00469 if( !( encData.salt.isEmpty() || encData.initVector.isEmpty() ) ) {
00470 m_encryptionData.insert( normalizedFullPath( fullpath ), encData );
00471 if( !( algorithmFound && keyDerivationFound ) ) {
00472 KMessage::message( KMessage::Warning, i18n( "This document contains incomplete encryption data. Some parts may be unreadable." ) );
00473 }
00474 }
00475
00476 xmlnode = xmlnode.nextSibling( );
00477 }
00478 }
00479
00480
00481 m_store->close( );
00482 m_store->popDirectory( );
00483 }
00484
00485 return true;
00486 }
00487
00488 bool KoEncryptedStore::isEncrypted( ) {
00489 if( m_mode == Read ) {
00490 return !m_encryptionData.isEmpty( );
00491 }
00492 return true;
00493 }
00494
00495 bool KoEncryptedStore::openWrite( const QString& name ) {
00496 if( normalizedFullPath( name ) != MANIFEST_FILE ) {
00497 if( !m_store && !m_init_deferred ) {
00498 return false;
00499 }
00500 if( m_store && !m_store->open( normalizedFullPath( name ) ) ) {
00501 return false;
00502 }
00503 }
00504 m_stream = new QBuffer( );
00505 if( !m_stream->open( QIODevice::WriteOnly ) ) {
00506 return false;
00507 }
00508 return true;
00509 }
00510
00511 bool KoEncryptedStore::openRead( const QString& name ) {
00512 if( !m_store || !m_store->open( name ) ) {
00513 return false;
00514 }
00515 if( !m_encryptionData.contains( normalizedFullPath( name ) ) ) {
00516
00517 if( m_stream ) {
00518 delete m_stream;
00519 }
00520 m_stream = m_store->device( );
00521 m_iSize = m_store->size( );
00522 }
00523 else {
00524 QSecureArray encryptedFile( m_store->device( )->readAll( ) );
00525 if( encryptedFile.size( ) != m_store->size( ) ) {
00526
00527 m_store->close( );
00528 return false;
00529 }
00530 m_store->close( );
00531 KoEncryptedStore_EncryptionData encData = m_encryptionData.value( normalizedFullPath( name ) );
00532 QSecureArray decrypted;
00533
00534
00535 if( m_password.isEmpty( ) ) {
00536 findPasswordInKWallet( );
00537 }
00538
00539
00540 while( true ) {
00541 QByteArray pass;
00542 QSecureArray password;
00543 int keepPass = 0;
00544
00545 if( !m_password.isEmpty( ) ) {
00546 password = m_password;
00547 m_password = QSecureArray();
00548 }
00549 else {
00550 QByteArray pass;
00551 if( !m_filename.isNull( ) )
00552 keepPass = 1;
00553 if( KPasswordDialog::getPassword( m_window, pass, i18n( "Please enter the password to open this file." ), &keepPass ) == KPasswordDialog::Rejected ) {
00554 return false;
00555 }
00556 password = QSecureArray( pass );
00557 if( password.isEmpty( ) ) {
00558 continue;
00559 }
00560 }
00561
00562 decrypted = decryptFile( encryptedFile, encData, password );
00563 if( decrypted.isEmpty() ) {
00564 return false;
00565 }
00566
00567 if( !encData.checksum.isEmpty() ) {
00568 QSecureArray checksum;
00569 if( encData.checksumShort && decrypted.size( ) > 1024 ) {
00570
00571 checksum = QCA::Hash( "sha1" ).hash( QSecureArray( decrypted.toByteArray( ).left( 1024 ) ) );
00572 }
00573 else {
00574 checksum = QCA::Hash( "sha1" ).hash( decrypted );
00575 }
00576 if( checksum != encData.checksum ) {
00577 continue;
00578 }
00579 }
00580
00581
00582 m_password = password;
00583
00584 if( keepPass ) {
00585 savePasswordInKWallet( );
00586 }
00587
00588 break;
00589 }
00590
00591 QByteArray *resultArray = new QByteArray( decrypted.toByteArray( ) );
00592 QIODevice *resultDevice = KFilterDev::device( new QBuffer( resultArray, NULL ), "application/x-gzip" );
00593 if( !resultDevice ) {
00594 delete resultArray;
00595 return false;
00596 }
00597 static_cast<KFilterDev*>( resultDevice )->setSkipHeaders( );
00598 m_stream = resultDevice;
00599 m_iSize = encData.filesize;
00600 }
00601
00602 return true;
00603 }
00604
00605 void KoEncryptedStore::findPasswordInKWallet( ) {
00606
00607
00608
00609
00610
00611
00612
00613
00614 if( !m_filename.isNull( ) && !KWallet::Wallet::folderDoesNotExist( KWallet::Wallet::LocalWallet( ), KWallet::Wallet::PasswordFolder( ) ) && !KWallet::Wallet::keyDoesNotExist( KWallet::Wallet::LocalWallet( ), KWallet::Wallet::PasswordFolder( ), m_filename + "/opendocument" ) ) {
00615 KWallet::Wallet *wallet = KWallet::Wallet::openWallet( KWallet::Wallet::LocalWallet( ), m_window ? m_window->winId( ) : 0 );
00616 if( wallet ) {
00617 if( wallet->setFolder( KWallet::Wallet::PasswordFolder( ) ) ) {
00618 QString pass;
00619 wallet->readPassword( m_filename + "/opendocument", pass );
00620 m_password = QSecureArray( pass.toUtf8( ) );
00621 }
00622 delete wallet;
00623 }
00624 }
00625 }
00626
00627 void KoEncryptedStore::savePasswordInKWallet( ) {
00628 KWallet::Wallet *wallet = KWallet::Wallet::openWallet( KWallet::Wallet::LocalWallet( ), m_window ? m_window->winId( ) : 0 );
00629 if( wallet ) {
00630 if( !wallet->hasFolder( KWallet::Wallet::PasswordFolder( ) ) ) {
00631 wallet->createFolder( KWallet::Wallet::PasswordFolder( ) );
00632 }
00633 if( wallet->setFolder( KWallet::Wallet::PasswordFolder( ) ) ) {
00634 if( wallet->hasEntry( m_filename + "/opendocument" ) ) {
00635 wallet->removeEntry( m_filename + "/opendocument" );
00636 }
00637 wallet->writePassword( m_filename + "/opendocument", m_password.toByteArray( ).data( ) );
00638 }
00639 delete wallet;
00640 }
00641 }
00642
00643 QSecureArray KoEncryptedStore::decryptFile( QSecureArray & encryptedFile, KoEncryptedStore_EncryptionData & encData, QSecureArray & password ) {
00644 if( !QCA::isSupported( "sha1" ) || !QCA::isSupported( "pbkdf2(sha1)" ) ) {
00645 return QSecureArray( );
00646 }
00647 QSecureArray keyhash = QCA::Hash( "sha1" ).hash( password );
00648 QCA::SymmetricKey key = QCA::PBKDF2( "sha1" ).makeKey( keyhash, QCA::InitializationVector( encData.salt ), 16, encData.iterationCount );
00649 QCA::Cipher decrypter( "blowfish", QCA::Cipher::CFB, QCA::Cipher::DefaultPadding, QCA::Decode, key, QCA::InitializationVector( encData.initVector ) );
00650 QSecureArray result = decrypter.update( encryptedFile );
00651 result += decrypter.final( );
00652 return result;
00653 }
00654
00655 bool KoEncryptedStore::setPassword( const QString& password ) {
00656 if( !m_password.isEmpty() || password.isEmpty() ) {
00657 return false;
00658 }
00659 m_password = QSecureArray( password.toUtf8() );
00660 initBackend( );
00661 return true;
00662 }
00663
00664 bool KoEncryptedStore::closeWrite() {
00665 bool passWasAsked = false;
00666 if( normalizedFullPath( m_sName ) == MANIFEST_FILE ) {
00667 m_manifestBuffer = static_cast<QBuffer*>( m_stream )->buffer( );
00668 return true;
00669 }
00670
00671 if( !m_store || !m_store->isOpen( ) ) {
00672 return true;
00673 }
00674
00675
00676 if( m_password.isEmpty() ) {
00677 findPasswordInKWallet( );
00678 }
00679 while( m_password.isEmpty( ) ) {
00680 QByteArray pass;
00681 if( KPasswordDialog::getNewPassword( m_window ? m_window : NULL, pass, i18n( "Please enter the password to encrypt the document with." ) ) == KPasswordDialog::Rejected ) {
00682 return false;
00683 }
00684 m_password = QSecureArray( pass );
00685 passWasAsked = true;
00686 }
00687
00688 if( !initBackend( ) ) {
00689 return false;
00690 }
00691
00692
00693 if( passWasAsked && KMessageBox::questionYesNo( m_window ? m_window : NULL, i18n( "Do you want to save the password?" ) ) == KMessageBox::Yes ) {
00694 savePasswordInKWallet( );
00695 }
00696
00697 QByteArray resultData;
00698 if( normalizedFullPath( m_sName ) == META_FILE ) {
00699
00700 resultData = static_cast<QBuffer*>( m_stream )->buffer( );
00701 }
00702 else if( normalizedFullPath( m_sName ) == THUMBNAIL_FILE ) {
00703
00704 resultData = static_cast<QBuffer*>( m_stream )->buffer( );
00705 }
00706 else {
00707
00708 QSecureArray passwordHash = QCA::Hash( "sha1" ).hash( m_password );
00709 QCA::Random random;
00710 KoEncryptedStore_EncryptionData encData;
00711 encData.initVector = random.randomArray( 8, QCA::Random::LongTermKey );
00712 encData.salt = random.randomArray( 16, QCA::Random::LongTermKey );
00713 encData.iterationCount = 1024;
00714 QCA::SymmetricKey key = QCA::PBKDF2( "sha1" ).makeKey( passwordHash, QCA::InitializationVector( encData.salt ), 16, encData.iterationCount );
00715 QCA::Cipher encrypter( "blowfish", QCA::Cipher::CFB, QCA::Cipher::DefaultPadding, QCA::Encode, key, QCA::InitializationVector( encData.initVector ) );
00716
00717
00718 QByteArray data = static_cast<QBuffer*>( m_stream )->buffer( );
00719 encData.filesize = data.size( );
00720
00721
00722 QBuffer compressedData;
00723 QIODevice *compressDevice = KFilterDev::device( &compressedData, "application/x-gzip", false );
00724 if( !compressDevice) {
00725 return false;
00726 }
00727 static_cast<KFilterDev*>( compressDevice )->setSkipHeaders( );
00728 if( !compressDevice->open( QIODevice::WriteOnly ) ) {
00729 delete compressDevice;
00730 return false;
00731 }
00732 if( compressDevice->write( data ) != data.size( ) ) {
00733 delete compressDevice;
00734 return false;
00735 }
00736 compressDevice->close( );
00737 delete compressDevice;
00738
00739
00740
00741
00742 if( data.size( ) > 1024 ) {
00743 QByteArray datatmp = compressedData.buffer( ).left( 1024 );
00744 encData.checksum = QCA::Hash( "sha1" ).hash( QSecureArray( datatmp ) );
00745 }
00746 else {
00747 encData.checksum = QCA::Hash( "sha1" ).hash( QSecureArray( compressedData.buffer( ) ) );
00748 }
00749 encData.checksumShort = true;
00750
00751
00752 QSecureArray result = encrypter.update( QSecureArray( compressedData.buffer( ) ) );
00753 result += encrypter.final( );
00754 resultData = result.toByteArray( );
00755
00756 m_encryptionData.insert( normalizedFullPath( m_sName ), encData );
00757 }
00758
00759
00760
00761 static_cast<KoStore *>( m_store )->write( resultData );
00762
00763 return m_store->close( );
00764 }
00765
00766 bool KoEncryptedStore::closeRead() {
00767 if( m_store && m_store->isOpen( ) ) {
00768 if( !m_store->close( ) ) {
00769 return false;
00770 }
00771
00772 m_stream = NULL;
00773 }
00774 else if( m_stream ) {
00775 delete m_stream;
00776 m_stream = NULL;
00777 }
00778 return true;
00779 }
00780
00781
00782 bool KoEncryptedStore::enterRelativeDirectory( const QString& dirName ) {
00783 if( m_store ) {
00784 m_store->pushDirectory( );
00785 bool res = m_store->enterDirectory( currentDirectory( ) ) && m_store->enterDirectory( dirName );
00786 m_store->popDirectory( );
00787 return res;
00788 }
00789 return m_init_deferred;
00790 }
00791
00792 bool KoEncryptedStore::enterAbsoluteDirectory( const QString& path ) {
00793 if( m_store ) {
00794 m_store->pushDirectory( );
00795 bool res = m_store->enterDirectory( path );
00796 m_store->popDirectory( );
00797 return res;
00798 }
00799 return m_init_deferred;
00800 }
00801
00802 bool KoEncryptedStore::fileExists( const QString& absPath ) const {
00803 if( m_mode == Write ) {
00804 return m_strFiles.contains( absPath );
00805 }
00806 if( m_store ) {
00807 int pos;
00808 QString tmp( absPath );
00809
00810
00811 if( tmp.left( 5 ) == "tar:/" )
00812 tmp = tmp.mid( 5 );
00813 if( tmp[0] == '/' )
00814 tmp = tmp.mid( 1 );
00815
00816 m_store->pushDirectory( );
00817 if( !m_store->enterDirectory( "tar:/" ) ) {
00818 m_store->popDirectory( );
00819 return false;
00820 }
00821
00822 while( ( pos = tmp.indexOf( '/' ) ) != -1 ) {
00823 if( !m_store->enterDirectory( tmp.left( pos ) ) ) {
00824 m_store->popDirectory( );
00825 return false;
00826 }
00827 tmp = tmp.mid( pos + 1 );
00828 }
00829
00830 bool result = m_store->hasFile( absPath );
00831 m_store->popDirectory( );
00832 return result;
00833 }
00834 return false;
00835 }
00836
00837
00838 QString KoEncryptedStore::normalizedFullPath( const QString& fullpath ) {
00839 if( fullpath.startsWith( "/" ) ) {
00840 return fullpath.mid( 1 );
00841 }
00842 return fullpath;
00843 }