@ -10,13 +10,16 @@ import android.text.TextUtils;
import android.util.Log ;
import android.util.Log ;
import android.util.Pair ;
import android.util.Pair ;
import com.annimon.stream.function. Function;
import com.annimon.stream.function. Bi Function;
import org.thoughtcrime.securesms.DatabaseUpgradeActivity ;
import org.thoughtcrime.securesms.R ;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher ;
import org.thoughtcrime.securesms.crypto.AsymmetricMasterCipher ;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider ;
import org.thoughtcrime.securesms.crypto.AttachmentSecretProvider ;
import org.thoughtcrime.securesms.crypto.MasterCipher ;
import org.thoughtcrime.securesms.crypto.MasterCipher ;
import org.thoughtcrime.securesms.crypto.MasterSecret ;
import org.thoughtcrime.securesms.crypto.MasterSecret ;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil ;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil ;
import org.thoughtcrime.securesms.service.GenericForegroundService ;
import org.thoughtcrime.securesms.util.Base64 ;
import org.thoughtcrime.securesms.util.Base64 ;
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
import org.thoughtcrime.securesms.util.TextSecurePreferences ;
import org.whispersystems.libsignal.InvalidMessageException ;
import org.whispersystems.libsignal.InvalidMessageException ;
@ -30,11 +33,13 @@ public class SQLCipherMigrationHelper {
private static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000 ;
private static final long ENCRYPTION_SYMMETRIC_BIT = 0x80000000 ;
private static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000 ;
private static final long ENCRYPTION_ASYMMETRIC_BIT = 0x40000000 ;
static void migratePlaintext ( @NonNull android . database . sqlite . SQLiteDatabase legacyDb ,
static void migratePlaintext ( @NonNull Context context ,
@NonNull android . database . sqlite . SQLiteDatabase legacyDb ,
@NonNull net . sqlcipher . database . SQLiteDatabase modernDb )
@NonNull net . sqlcipher . database . SQLiteDatabase modernDb )
{
{
modernDb . beginTransaction ( ) ;
modernDb . beginTransaction ( ) ;
try {
try {
GenericForegroundService . startForegroundTask ( context , context . getString ( R . string . SQLCipherMigrationHelper_migrating_signal_database ) ) ;
copyTable ( "identities" , legacyDb , modernDb , null ) ;
copyTable ( "identities" , legacyDb , modernDb , null ) ;
copyTable ( "push" , legacyDb , modernDb , null ) ;
copyTable ( "push" , legacyDb , modernDb , null ) ;
copyTable ( "groups" , legacyDb , modernDb , null ) ;
copyTable ( "groups" , legacyDb , modernDb , null ) ;
@ -43,13 +48,15 @@ public class SQLCipherMigrationHelper {
modernDb . setTransactionSuccessful ( ) ;
modernDb . setTransactionSuccessful ( ) ;
} finally {
} finally {
modernDb . endTransaction ( ) ;
modernDb . endTransaction ( ) ;
GenericForegroundService . stopForegroundTask ( context ) ;
}
}
}
}
public static void migrateCiphertext ( @NonNull Context context ,
public static void migrateCiphertext ( @NonNull Context context ,
@NonNull MasterSecret masterSecret ,
@NonNull MasterSecret masterSecret ,
@NonNull android . database . sqlite . SQLiteDatabase legacyDb ,
@NonNull android . database . sqlite . SQLiteDatabase legacyDb ,
@NonNull net . sqlcipher . database . SQLiteDatabase modernDb )
@NonNull net . sqlcipher . database . SQLiteDatabase modernDb ,
@Nullable DatabaseUpgradeActivity . DatabaseUpgradeListener listener )
{
{
MasterCipher legacyCipher = new MasterCipher ( masterSecret ) ;
MasterCipher legacyCipher = new MasterCipher ( masterSecret ) ;
AsymmetricMasterCipher legacyAsymmetricCipher = new AsymmetricMasterCipher ( MasterSecretUtil . getAsymmetricMasterSecret ( context , masterSecret ) ) ;
AsymmetricMasterCipher legacyAsymmetricCipher = new AsymmetricMasterCipher ( MasterSecretUtil . getAsymmetricMasterSecret ( context , masterSecret ) ) ;
@ -57,7 +64,10 @@ public class SQLCipherMigrationHelper {
modernDb . beginTransaction ( ) ;
modernDb . beginTransaction ( ) ;
try {
try {
copyTable ( "sms" , legacyDb , modernDb , ( row ) - > {
GenericForegroundService . startForegroundTask ( context , context . getString ( R . string . SQLCipherMigrationHelper_migrating_signal_database ) ) ;
int total = 5000 ;
copyTable ( "sms" , legacyDb , modernDb , ( row , progress ) - > {
Pair < Long , String > plaintext = getPlaintextBody ( legacyCipher , legacyAsymmetricCipher ,
Pair < Long , String > plaintext = getPlaintextBody ( legacyCipher , legacyAsymmetricCipher ,
row . getAsLong ( "type" ) ,
row . getAsLong ( "type" ) ,
row . getAsString ( "body" ) ) ;
row . getAsString ( "body" ) ) ;
@ -65,10 +75,14 @@ public class SQLCipherMigrationHelper {
row . put ( "body" , plaintext . second ) ;
row . put ( "body" , plaintext . second ) ;
row . put ( "type" , plaintext . first ) ;
row . put ( "type" , plaintext . first ) ;
if ( listener ! = null & & ( progress . first % 1000 = = 0 ) ) {
listener . setProgress ( getTotalProgress ( 0 , progress . first , progress . second ) , total ) ;
}
return row ;
return row ;
} ) ;
} ) ;
copyTable ( "mms" , legacyDb , modernDb , ( row ) - > {
copyTable ( "mms" , legacyDb , modernDb , ( row , progress ) - > {
Pair < Long , String > plaintext = getPlaintextBody ( legacyCipher , legacyAsymmetricCipher ,
Pair < Long , String > plaintext = getPlaintextBody ( legacyCipher , legacyAsymmetricCipher ,
row . getAsLong ( "msg_box" ) ,
row . getAsLong ( "msg_box" ) ,
row . getAsString ( "body" ) ) ;
row . getAsString ( "body" ) ) ;
@ -76,10 +90,14 @@ public class SQLCipherMigrationHelper {
row . put ( "body" , plaintext . second ) ;
row . put ( "body" , plaintext . second ) ;
row . put ( "msg_box" , plaintext . first ) ;
row . put ( "msg_box" , plaintext . first ) ;
if ( listener ! = null & & ( progress . first % 1000 = = 0 ) ) {
listener . setProgress ( getTotalProgress ( 1000 , progress . first , progress . second ) , total ) ;
}
return row ;
return row ;
} ) ;
} ) ;
copyTable ( "part" , legacyDb , modernDb , ( row ) - > {
copyTable ( "part" , legacyDb , modernDb , ( row , progress ) - > {
String fileName = row . getAsString ( "file_name" ) ;
String fileName = row . getAsString ( "file_name" ) ;
String mediaKey = row . getAsString ( "cd" ) ;
String mediaKey = row . getAsString ( "cd" ) ;
@ -107,11 +125,14 @@ public class SQLCipherMigrationHelper {
Log . w ( TAG , e ) ;
Log . w ( TAG , e ) ;
}
}
if ( listener ! = null & & ( progress . first % 1000 = = 0 ) ) {
listener . setProgress ( getTotalProgress ( 2000 , progress . first , progress . second ) , total ) ;
}
return row ;
return row ;
} ) ;
} ) ;
copyTable ( "thread" , legacyDb , modernDb , ( row ) - > {
copyTable ( "thread" , legacyDb , modernDb , ( row , progress ) - > {
Pair < Long , String > plaintext = getPlaintextBody ( legacyCipher , legacyAsymmetricCipher ,
Pair < Long , String > plaintext = getPlaintextBody ( legacyCipher , legacyAsymmetricCipher ,
row . getAsLong ( "snippet_type" ) ,
row . getAsLong ( "snippet_type" ) ,
row . getAsString ( "snippet" ) ) ;
row . getAsString ( "snippet" ) ) ;
@ -119,11 +140,15 @@ public class SQLCipherMigrationHelper {
row . put ( "snippet" , plaintext . second ) ;
row . put ( "snippet" , plaintext . second ) ;
row . put ( "snippet_type" , plaintext . first ) ;
row . put ( "snippet_type" , plaintext . first ) ;
if ( listener ! = null & & ( progress . first % 1000 = = 0 ) ) {
listener . setProgress ( getTotalProgress ( 3000 , progress . first , progress . second ) , total ) ;
}
return row ;
return row ;
} ) ;
} ) ;
copyTable ( "drafts" , legacyDb , modernDb , ( row ) - > {
copyTable ( "drafts" , legacyDb , modernDb , ( row , progress ) - > {
String draftType = row . getAsString ( "type" ) ;
String draftType = row . getAsString ( "type" ) ;
String draft = row . getAsString ( "value" ) ;
String draft = row . getAsString ( "value" ) ;
@ -134,24 +159,31 @@ public class SQLCipherMigrationHelper {
Log . w ( TAG , e ) ;
Log . w ( TAG , e ) ;
}
}
if ( listener ! = null & & ( progress . first % 1000 = = 0 ) ) {
listener . setProgress ( getTotalProgress ( 4000 , progress . first , progress . second ) , total ) ;
}
return row ;
return row ;
} ) ;
} ) ;
AttachmentSecretProvider . getInstance ( context ) . setClassicKey ( context , masterSecret . getEncryptionKey ( ) . getEncoded ( ) , masterSecret . getMacKey ( ) . getEncoded ( ) ) ;
TextSecurePreferences . setNeedsSqlCipherMigration ( context , false ) ;
TextSecurePreferences . setNeedsSqlCipherMigration ( context , false ) ;
modernDb . setTransactionSuccessful ( ) ;
modernDb . setTransactionSuccessful ( ) ;
} finally {
} finally {
modernDb . endTransaction ( ) ;
modernDb . endTransaction ( ) ;
GenericForegroundService . stopForegroundTask ( context ) ;
}
}
AttachmentSecretProvider . getInstance ( context ) . setClassicKey ( context , masterSecret . getEncryptionKey ( ) . getEncoded ( ) , masterSecret . getMacKey ( ) . getEncoded ( ) ) ;
}
}
private static void copyTable ( @NonNull String tableName ,
private static void copyTable ( @NonNull String tableName ,
@NonNull android . database . sqlite . SQLiteDatabase legacyDb ,
@NonNull android . database . sqlite . SQLiteDatabase legacyDb ,
@NonNull net . sqlcipher . database . SQLiteDatabase modernDb ,
@NonNull net . sqlcipher . database . SQLiteDatabase modernDb ,
@Nullable Function< ContentValues , ContentValues > transformer )
@Nullable Bi Function< ContentValues , Pair < Integer , Integer > , ContentValues > transformer )
{
{
try ( Cursor cursor = legacyDb . query ( tableName , null , null , null , null , null , null ) ) {
try ( Cursor cursor = legacyDb . query ( tableName , null , null , null , null , null , null ) ) {
int count = ( cursor ! = null ) ? cursor . getCount ( ) : 0 ;
int progress = 1 ;
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
while ( cursor ! = null & & cursor . moveToNext ( ) ) {
ContentValues row = new ContentValues ( ) ;
ContentValues row = new ContentValues ( ) ;
@ -167,7 +199,7 @@ public class SQLCipherMigrationHelper {
}
}
if ( transformer ! = null ) {
if ( transformer ! = null ) {
row = transformer . apply ( row );
row = transformer . apply ( row , new Pair < > ( progress + + , count ) );
}
}
modernDb . insert ( tableName , null , row ) ;
modernDb . insert ( tableName , null , row ) ;
@ -194,4 +226,9 @@ public class SQLCipherMigrationHelper {
return new Pair < > ( type , body ) ;
return new Pair < > ( type , body ) ;
}
}
private static int getTotalProgress ( int sectionOffset , int sectionProgress , int sectionTotal ) {
double percentOfSectionComplete = ( ( double ) sectionProgress ) / ( ( double ) sectionTotal ) ;
return sectionOffset + ( int ) ( ( ( double ) 1000 ) * percentOfSectionComplete ) ;
}
}
}