2014-11-18 12:18:24 +08:00
/*
2014-12-04 02:00:37 +08:00
Copyright ( C ) 2014 by Project Tox < https : //tox.im>
2014-11-18 12:18:24 +08:00
2014-12-04 02:00:37 +08:00
This file is part of qTox , a Qt - based graphical interface for Tox .
2014-11-18 12:18:24 +08:00
2014-12-04 02:00:37 +08:00
This program is libre software : you can redistribute it and / or modify
2014-11-18 12:18:24 +08:00
it under the terms of the GNU General Public License as published by
the Free Software Foundation , either version 3 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE .
See the COPYING file for more details .
*/
/* This file is part of the Core class, but was separated for my sanity */
2014-12-04 02:00:37 +08:00
/* The load function delegates to loadEncrypted here, and the save function */
/* was permanently moved here to handle encryption */
2014-11-18 12:18:24 +08:00
# include "core.h"
2015-02-07 02:01:31 +08:00
# include "src/widget/gui.h"
2014-11-18 12:18:24 +08:00
# include "src/misc/settings.h"
2015-04-24 08:32:09 +08:00
# include "src/misc/cstring.h"
# include "src/historykeeper.h"
2015-04-20 05:12:44 +08:00
# include <tox/tox.h>
# include <tox/toxencryptsave.h>
2014-11-18 12:18:24 +08:00
# include <QApplication>
# include <QDebug>
# include <QSaveFile>
# include <QFile>
2015-01-28 08:28:44 +08:00
# include <QThread>
2015-04-20 05:12:44 +08:00
# include <algorithm>
# include <cassert>
2014-11-18 12:18:24 +08:00
void Core : : setPassword ( QString & password , PasswordType passtype , uint8_t * salt )
{
2014-12-09 06:40:20 +08:00
clearPassword ( passtype ) ;
2014-11-18 12:18:24 +08:00
if ( password . isEmpty ( ) )
return ;
2014-12-09 06:40:20 +08:00
2015-04-20 05:12:44 +08:00
pwsaltedkeys [ passtype ] = new TOX_PASS_KEY ;
2014-11-18 12:18:24 +08:00
CString str ( password ) ;
if ( salt )
2015-04-20 05:12:44 +08:00
tox_derive_key_with_salt ( str . data ( ) , str . size ( ) , salt , pwsaltedkeys [ passtype ] , nullptr ) ;
2014-11-18 12:18:24 +08:00
else
2015-04-20 05:12:44 +08:00
tox_derive_key_from_pass ( str . data ( ) , str . size ( ) , pwsaltedkeys [ passtype ] , nullptr ) ;
2014-11-18 12:18:24 +08:00
password . clear ( ) ;
}
2014-11-27 23:38:23 +08:00
void Core : : useOtherPassword ( PasswordType type )
{
clearPassword ( type ) ;
2015-04-20 05:12:44 +08:00
pwsaltedkeys [ type ] = new TOX_PASS_KEY ;
2015-01-16 11:41:10 +08:00
PasswordType other = ( type = = ptMain ) ? ptHistory : ptMain ;
2015-04-24 04:59:12 +08:00
std : : copy ( pwsaltedkeys [ other ] , pwsaltedkeys [ other ] + 1 , pwsaltedkeys [ type ] ) ;
2014-11-27 23:38:23 +08:00
}
2014-11-18 12:18:24 +08:00
void Core : : clearPassword ( PasswordType passtype )
{
2015-04-24 04:59:12 +08:00
delete pwsaltedkeys [ passtype ] ;
2014-12-10 09:06:09 +08:00
pwsaltedkeys [ passtype ] = nullptr ;
2014-11-18 12:18:24 +08:00
}
2015-01-28 04:16:28 +08:00
// part of a hack, see core.h
void Core : : saveCurrentInformation ( )
{
if ( pwsaltedkeys [ ptMain ] )
{
2015-04-20 05:12:44 +08:00
backupkeys [ ptMain ] = new TOX_PASS_KEY ;
2015-04-24 04:59:12 +08:00
std : : copy ( pwsaltedkeys [ ptMain ] , pwsaltedkeys [ ptMain ] + 1 , backupkeys [ ptMain ] ) ;
2015-01-28 04:16:28 +08:00
}
if ( pwsaltedkeys [ ptHistory ] )
{
2015-04-20 05:12:44 +08:00
backupkeys [ ptHistory ] = new TOX_PASS_KEY ;
2015-04-24 04:59:12 +08:00
std : : copy ( pwsaltedkeys [ ptHistory ] , pwsaltedkeys [ ptHistory ] + 1 , backupkeys [ ptHistory ] ) ;
2015-01-28 04:16:28 +08:00
}
backupProfile = new QString ( Settings : : getInstance ( ) . getCurrentProfile ( ) ) ;
}
QString Core : : loadOldInformation ( )
{
QString out ;
if ( backupProfile )
{
out = * backupProfile ;
delete backupProfile ;
backupProfile = nullptr ;
}
backupProfile = nullptr ;
clearPassword ( ptMain ) ;
clearPassword ( ptHistory ) ;
// we can just copy the pointer, as long as we null out backupkeys
// (if backupkeys was null anyways, then this is a null-op)
pwsaltedkeys [ ptMain ] = backupkeys [ ptMain ] ;
pwsaltedkeys [ ptHistory ] = backupkeys [ ptHistory ] ;
backupkeys [ ptMain ] = nullptr ;
backupkeys [ ptHistory ] = nullptr ;
return out ;
}
2014-11-18 12:18:24 +08:00
QByteArray Core : : encryptData ( const QByteArray & data , PasswordType passtype )
{
if ( ! pwsaltedkeys [ passtype ] )
return QByteArray ( ) ;
2015-04-20 15:58:06 +08:00
2015-04-20 05:12:44 +08:00
uint8_t encrypted [ data . size ( ) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH ] ;
if ( ! tox_pass_key_encrypt ( reinterpret_cast < const uint8_t * > ( data . data ( ) ) , data . size ( ) ,
pwsaltedkeys [ passtype ] , encrypted , nullptr ) )
2014-11-18 12:18:24 +08:00
{
qWarning ( ) < < " Core::encryptData: encryption failed " ;
return QByteArray ( ) ;
}
2015-04-20 05:12:44 +08:00
return QByteArray ( reinterpret_cast < char * > ( encrypted ) , data . size ( ) + TOX_PASS_ENCRYPTION_EXTRA_LENGTH ) ;
2014-11-18 12:18:24 +08:00
}
QByteArray Core : : decryptData ( const QByteArray & data , PasswordType passtype )
{
if ( ! pwsaltedkeys [ passtype ] )
return QByteArray ( ) ;
2015-04-20 15:58:06 +08:00
2015-04-20 05:12:44 +08:00
int sz = data . size ( ) - TOX_PASS_ENCRYPTION_EXTRA_LENGTH ;
2014-11-18 12:18:24 +08:00
uint8_t decrypted [ sz ] ;
2015-04-20 05:12:44 +08:00
if ( ! tox_pass_key_decrypt ( reinterpret_cast < const uint8_t * > ( data . data ( ) ) , data . size ( ) ,
pwsaltedkeys [ passtype ] , decrypted , nullptr ) )
2014-11-18 12:18:24 +08:00
{
qWarning ( ) < < " Core::decryptData: decryption failed " ;
return QByteArray ( ) ;
}
return QByteArray ( reinterpret_cast < char * > ( decrypted ) , sz ) ;
}
bool Core : : isPasswordSet ( PasswordType passtype )
{
if ( pwsaltedkeys [ passtype ] )
return true ;
return false ;
}
QByteArray Core : : getSaltFromFile ( QString filename )
{
QFile file ( filename ) ;
2014-12-10 08:08:38 +08:00
if ( ! file . open ( QIODevice : : ReadOnly ) )
{
2015-01-20 11:49:41 +08:00
qWarning ( ) < < " Core: file " < < filename < < " doesn't exist " ;
2014-12-10 08:08:38 +08:00
return QByteArray ( ) ;
}
2015-04-20 05:12:44 +08:00
QByteArray data = file . read ( TOX_PASS_ENCRYPTION_EXTRA_LENGTH ) ;
2014-11-18 12:18:24 +08:00
file . close ( ) ;
2015-04-20 05:12:44 +08:00
uint8_t * salt = new uint8_t [ TOX_PASS_SALT_LENGTH ] ;
2015-04-24 07:08:34 +08:00
if ( ! tox_get_salt ( reinterpret_cast < uint8_t * > ( data . data ( ) ) , salt ) )
2014-11-18 12:18:24 +08:00
{
qWarning ( ) < < " Core: can't get salt from " < < filename < < " header " ;
return QByteArray ( ) ;
}
2015-04-24 07:10:42 +08:00
QByteArray res ( reinterpret_cast < const char * > ( salt ) , TOX_PASS_SALT_LENGTH ) ;
2015-03-22 04:58:26 +08:00
delete [ ] salt ;
2014-11-18 12:18:24 +08:00
return res ;
}
2014-11-18 14:34:31 +08:00
bool Core : : loadEncryptedSave ( QByteArray & data )
{
if ( ! Settings : : getInstance ( ) . getEncryptTox ( ) )
2015-02-07 02:01:31 +08:00
GUI : : showWarning ( tr ( " Encryption error " ) , tr ( " The .tox file is encrypted, but encryption was not checked, continuing regardless. " ) ) ;
2014-11-18 14:34:31 +08:00
2015-04-24 04:59:12 +08:00
size_t fileSize = data . size ( ) ;
2014-11-18 14:34:31 +08:00
int error = - 1 ;
2015-01-20 11:59:27 +08:00
QString a ( tr ( " Please enter the password for the %1 profile. " , " used in load() when no pw is already set " ).arg(Settings::getInstance().getCurrentProfile())) ;
2014-11-18 14:34:31 +08:00
QString b ( tr ( " The previous password is incorrect; please try again: " , " used on retries in load() " ) ) ;
QString dialogtxt ;
if ( pwsaltedkeys [ ptMain ] ) // password set, try it
{
2015-04-24 04:59:12 +08:00
QByteArray newData ( fileSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH , 0 ) ;
if ( tox_pass_key_decrypt ( ( uint8_t * ) data . data ( ) , fileSize , pwsaltedkeys [ ptMain ] ,
( uint8_t * ) newData . data ( ) , nullptr ) )
2014-11-18 14:34:31 +08:00
{
2015-04-24 04:59:12 +08:00
data = newData ;
2014-11-18 14:34:31 +08:00
Settings : : getInstance ( ) . setEncryptTox ( true ) ;
return true ;
}
2015-04-24 04:59:12 +08:00
2015-02-04 01:58:37 +08:00
dialogtxt = tr ( " The profile password failed. Please try another? " , " used only when pw set before load() doesn't work " ) ;
2014-11-18 14:34:31 +08:00
}
else
dialogtxt = a ;
2015-04-20 05:12:44 +08:00
uint8_t salt [ TOX_PASS_SALT_LENGTH ] ;
2014-11-18 14:34:31 +08:00
tox_get_salt ( reinterpret_cast < uint8_t * > ( data . data ( ) ) , salt ) ;
2014-12-06 08:18:43 +08:00
2014-11-18 14:34:31 +08:00
do
{
2015-02-07 02:01:31 +08:00
QString pw = GUI : : passwordDialog ( tr ( " Change profile " ) , dialogtxt ) ;
2014-11-18 14:34:31 +08:00
2014-12-06 08:18:43 +08:00
if ( pw . isEmpty ( ) )
2014-11-18 14:34:31 +08:00
{
clearPassword ( ptMain ) ;
return false ;
}
2014-12-06 08:18:43 +08:00
else
setPassword ( pw , ptMain , salt ) ;
2014-11-18 14:34:31 +08:00
2015-04-24 04:59:12 +08:00
QByteArray newData ( fileSize - TOX_PASS_ENCRYPTION_EXTRA_LENGTH , 0 ) ;
error = ! tox_pass_key_decrypt ( ( uint8_t * ) data . data ( ) , data . size ( ) , pwsaltedkeys [ ptMain ] ,
( uint8_t * ) newData . data ( ) , nullptr ) ;
if ( ! error )
data = newData ;
2015-01-23 21:05:46 +08:00
dialogtxt = a + " \n " + b ;
2014-11-18 14:34:31 +08:00
} while ( error ! = 0 ) ;
Settings : : getInstance ( ) . setEncryptTox ( true ) ;
return true ;
}
void Core : : checkEncryptedHistory ( )
{
2015-01-20 11:49:41 +08:00
QString path = HistoryKeeper : : getHistoryPath ( ) ;
bool exists = QFile : : exists ( path ) ;
2014-11-18 14:34:31 +08:00
2015-01-20 11:49:41 +08:00
QByteArray salt = getSaltFromFile ( path ) ;
if ( exists & & salt . size ( ) = = 0 )
2014-11-18 14:34:31 +08:00
{ // maybe we should handle this better
2015-02-07 02:01:31 +08:00
GUI : : showWarning ( tr ( " Encrypted chat history " ) , tr ( " No encrypted chat history file found, or it was corrupted. \n History will be disabled! " ) ) ;
2014-11-18 14:34:31 +08:00
Settings : : getInstance ( ) . setEncryptLogs ( false ) ;
Settings : : getInstance ( ) . setEnableLogging ( false ) ;
2015-01-28 07:25:41 +08:00
HistoryKeeper : : resetInstance ( ) ;
2014-11-18 14:34:31 +08:00
return ;
}
2015-02-04 01:58:37 +08:00
QString a ( tr ( " Please enter the password for the chat history for the %1 profile. " , " used in load() when no hist pw set " ).arg(Settings::getInstance().getCurrentProfile())) ;
2014-11-18 14:34:31 +08:00
QString b ( tr ( " The previous password is incorrect; please try again: " , " used on retries in load() " ) ) ;
2015-02-05 06:11:21 +08:00
QString c ( tr ( " \n Disabling chat history now will leave the encrypted history intact (but not usable) ; if you later remember the password , you may re - enable encryption from the Privacy tab with the correct password to use the history . " , " part of history password dialog " ));
2014-11-18 14:34:31 +08:00
QString dialogtxt ;
if ( pwsaltedkeys [ ptHistory ] )
{
2015-01-20 11:49:41 +08:00
if ( ! exists | | HistoryKeeper : : checkPassword ( ) )
2014-11-18 14:34:31 +08:00
return ;
2015-04-20 15:58:06 +08:00
2015-02-04 01:58:37 +08:00
dialogtxt = tr ( " The chat history password failed. Please try another? " , " used only when pw set before load() doesn't work " ) ;
2014-11-18 14:34:31 +08:00
}
else
dialogtxt = a ;
2015-04-20 15:58:06 +08:00
2015-02-04 08:22:05 +08:00
dialogtxt + = " \n " + c ;
2014-11-18 14:34:31 +08:00
2014-12-10 08:40:05 +08:00
if ( pwsaltedkeys [ ptMain ] )
{
useOtherPassword ( ptHistory ) ;
2015-01-20 11:49:41 +08:00
if ( ! exists | | HistoryKeeper : : checkPassword ( ) )
{
2015-02-04 01:58:37 +08:00
qDebug ( ) < < " Core: using main password for chat history " ;
2014-12-10 08:40:05 +08:00
return ;
2015-01-20 11:49:41 +08:00
}
2014-12-10 08:40:05 +08:00
clearPassword ( ptHistory ) ;
}
2014-11-18 14:34:31 +08:00
bool error = true ;
do
{
2015-02-07 02:01:31 +08:00
QString pw = GUI : : passwordDialog ( tr ( " Disable chat history " ) , dialogtxt ) ;
2014-11-18 14:34:31 +08:00
2014-12-06 08:18:43 +08:00
if ( pw . isEmpty ( ) )
2014-11-18 14:34:31 +08:00
{
clearPassword ( ptHistory ) ;
Settings : : getInstance ( ) . setEncryptLogs ( false ) ;
Settings : : getInstance ( ) . setEnableLogging ( false ) ;
2015-01-28 07:25:41 +08:00
HistoryKeeper : : resetInstance ( ) ;
2014-11-18 14:34:31 +08:00
return ;
}
2014-12-06 08:18:43 +08:00
else
setPassword ( pw , ptHistory , reinterpret_cast < uint8_t * > ( salt . data ( ) ) ) ;
2014-11-18 14:34:31 +08:00
2015-01-20 11:49:41 +08:00
error = exists & & ! HistoryKeeper : : checkPassword ( ) ;
2015-02-04 08:22:05 +08:00
dialogtxt = a + " \n " + c + " \n " + b ;
2014-11-18 14:34:31 +08:00
} while ( error ) ;
}
void Core : : saveConfiguration ( const QString & path )
{
2015-01-28 08:28:44 +08:00
if ( QThread : : currentThread ( ) ! = coreThread )
return ( void ) QMetaObject : : invokeMethod ( this , " saveConfiguration " , Q_ARG ( const QString & , path ) ) ;
2015-01-23 22:18:46 +08:00
if ( ! isReady ( ) )
2014-11-18 14:34:31 +08:00
{
qWarning ( ) < < " Core::saveConfiguration: Tox not started, aborting! " ;
return ;
}
QSaveFile configurationFile ( path ) ;
2015-04-20 15:58:06 +08:00
if ( ! configurationFile . open ( QIODevice : : WriteOnly ) )
{
2014-11-18 14:34:31 +08:00
qCritical ( ) < < " File " < < path < < " cannot be opened " ;
return ;
}
qDebug ( ) < < " Core: writing tox_save to " < < path ;
2015-04-24 04:59:12 +08:00
uint32_t fileSize = tox_get_savedata_size ( tox ) ;
2015-04-20 05:12:44 +08:00
bool encrypt = Settings : : getInstance ( ) . getEncryptTox ( ) ;
2014-11-18 14:34:31 +08:00
2015-04-20 15:58:06 +08:00
if ( fileSize > 0 & & fileSize < = std : : numeric_limits < int32_t > : : max ( ) )
{
2014-11-18 14:34:31 +08:00
uint8_t * data = new uint8_t [ fileSize ] ;
if ( encrypt )
{
if ( ! pwsaltedkeys [ ptMain ] )
{
// probably zero chance event
2015-03-12 10:37:26 +08:00
GUI : : showWarning ( tr ( " NO Password " ) , tr ( " Local file encryption is enabled, but there is no password! It will be disabled. " ) ) ;
2014-12-05 04:21:48 +08:00
Settings : : getInstance ( ) . setEncryptTox ( false ) ;
2015-04-20 05:12:44 +08:00
tox_get_savedata ( tox , data ) ;
2014-11-18 14:34:31 +08:00
}
else
{
2015-04-24 04:59:12 +08:00
tox_get_savedata ( tox , data ) ;
uint8_t * newData = new uint8_t [ fileSize + TOX_PASS_ENCRYPTION_EXTRA_LENGTH ] ;
if ( tox_pass_key_encrypt ( data , fileSize , pwsaltedkeys [ ptMain ] , newData , nullptr ) )
{
delete [ ] data ;
data = newData ;
fileSize + = TOX_PASS_ENCRYPTION_EXTRA_LENGTH ;
}
else
2014-11-18 14:34:31 +08:00
{
2015-04-24 04:59:12 +08:00
delete [ ] newData ;
delete [ ] data ;
qCritical ( ) < < " Core::saveConfiguration(QString): Encryption failed, couldn't save " ;
configurationFile . cancelWriting ( ) ;
2014-11-18 14:34:31 +08:00
return ;
}
}
}
else
2015-04-20 05:12:44 +08:00
tox_get_savedata ( tox , data ) ;
2014-11-18 14:34:31 +08:00
configurationFile . write ( reinterpret_cast < char * > ( data ) , fileSize ) ;
configurationFile . commit ( ) ;
delete [ ] data ;
}
2015-01-27 09:22:38 +08:00
Settings : : getInstance ( ) . save ( ) ;
2014-11-18 14:34:31 +08:00
}