Search in sources :

Example 1 with IdentityKey

use of org.whispersystems.libsignal.IdentityKey in project Signal-Android by WhisperSystems.

the class IdentityKeyUtil method generateIdentityKeys.

public static void generateIdentityKeys(Context context) {
    ECKeyPair djbKeyPair = Curve.generateKeyPair();
    IdentityKey djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
    ECPrivateKey djbPrivateKey = djbKeyPair.getPrivateKey();
    save(context, IDENTITY_PUBLIC_KEY_PREF, Base64.encodeBytes(djbIdentityKey.serialize()));
    save(context, IDENTITY_PRIVATE_KEY_PREF, Base64.encodeBytes(djbPrivateKey.serialize()));
}
Also used : ECPrivateKey(org.whispersystems.libsignal.ecc.ECPrivateKey) IdentityKey(org.whispersystems.libsignal.IdentityKey) ECKeyPair(org.whispersystems.libsignal.ecc.ECKeyPair)

Example 2 with IdentityKey

use of org.whispersystems.libsignal.IdentityKey in project Signal-Android by WhisperSystems.

the class IdentityKeyUtil method getIdentityKeyPair.

@NonNull
public static IdentityKeyPair getIdentityKeyPair(@NonNull Context context) {
    if (!hasIdentityKey(context))
        throw new AssertionError("There isn't one!");
    try {
        IdentityKey publicKey = getIdentityKey(context);
        ECPrivateKey privateKey = Curve.decodePrivatePoint(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_PREF)));
        return new IdentityKeyPair(publicKey, privateKey);
    } catch (IOException e) {
        throw new AssertionError(e);
    }
}
Also used : ECPrivateKey(org.whispersystems.libsignal.ecc.ECPrivateKey) IdentityKey(org.whispersystems.libsignal.IdentityKey) IOException(java.io.IOException) IdentityKeyPair(org.whispersystems.libsignal.IdentityKeyPair) NonNull(android.support.annotation.NonNull)

Example 3 with IdentityKey

use of org.whispersystems.libsignal.IdentityKey in project Signal-Android by WhisperSystems.

the class IdentityKeyUtil method getLegacyIdentityKeyPair.

private static IdentityKeyPair getLegacyIdentityKeyPair(@NonNull Context context, @NonNull MasterSecret masterSecret) {
    try {
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        byte[] publicKeyBytes = Base64.decode(retrieve(context, IDENTITY_PUBLIC_KEY_CIPHERTEXT_LEGACY_PREF));
        IdentityKey identityKey = new IdentityKey(publicKeyBytes, 0);
        ECPrivateKey privateKey = masterCipher.decryptKey(Base64.decode(retrieve(context, IDENTITY_PRIVATE_KEY_CIPHERTEXT_LEGACY_PREF)));
        return new IdentityKeyPair(identityKey, privateKey);
    } catch (IOException | InvalidKeyException e) {
        throw new AssertionError(e);
    }
}
Also used : ECPrivateKey(org.whispersystems.libsignal.ecc.ECPrivateKey) IdentityKey(org.whispersystems.libsignal.IdentityKey) IOException(java.io.IOException) IdentityKeyPair(org.whispersystems.libsignal.IdentityKeyPair) InvalidKeyException(org.whispersystems.libsignal.InvalidKeyException)

Example 4 with IdentityKey

use of org.whispersystems.libsignal.IdentityKey in project Signal-Android by WhisperSystems.

the class DatabaseFactory method onApplicationLevelUpgrade.

public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion, DatabaseUpgradeActivity.DatabaseUpgradeListener listener) {
    SQLiteDatabase db = databaseHelper.getWritableDatabase();
    db.beginTransaction();
    if (fromVersion < DatabaseUpgradeActivity.NO_MORE_KEY_EXCHANGE_PREFIX_VERSION) {
        String KEY_EXCHANGE = "?TextSecureKeyExchange";
        String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd";
        String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs";
        int ROW_LIMIT = 500;
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        int smsCount = 0;
        int threadCount = 0;
        int skip = 0;
        Cursor cursor = db.query("sms", new String[] { "COUNT(*)" }, "type & " + 0x80000000 + " != 0", null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            smsCount = cursor.getInt(0);
            cursor.close();
        }
        cursor = db.query("thread", new String[] { "COUNT(*)" }, "snippet_type & " + 0x80000000 + " != 0", null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            threadCount = cursor.getInt(0);
            cursor.close();
        }
        Cursor smsCursor = null;
        Log.w("DatabaseFactory", "Upgrade count: " + (smsCount + threadCount));
        do {
            Log.w("DatabaseFactory", "Looping SMS cursor...");
            if (smsCursor != null)
                smsCursor.close();
            smsCursor = db.query("sms", new String[] { "_id", "type", "body" }, "type & " + 0x80000000 + " != 0", null, null, null, "_id", skip + "," + ROW_LIMIT);
            while (smsCursor != null && smsCursor.moveToNext()) {
                listener.setProgress(smsCursor.getPosition() + skip, smsCount + threadCount);
                try {
                    String body = masterCipher.decryptBody(smsCursor.getString(smsCursor.getColumnIndexOrThrow("body")));
                    long type = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("type"));
                    long id = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("_id"));
                    if (body.startsWith(KEY_EXCHANGE)) {
                        body = body.substring(KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= 0x8000;
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    } else if (body.startsWith(PROCESSED_KEY_EXCHANGE)) {
                        body = body.substring(PROCESSED_KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= (0x8000 | 0x2000);
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    } else if (body.startsWith(STALE_KEY_EXCHANGE)) {
                        body = body.substring(STALE_KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= (0x8000 | 0x4000);
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    }
                } catch (InvalidMessageException e) {
                    Log.w("DatabaseFactory", e);
                }
            }
            skip += ROW_LIMIT;
        } while (smsCursor != null && smsCursor.getCount() > 0);
        Cursor threadCursor = null;
        skip = 0;
        do {
            Log.w("DatabaseFactory", "Looping thread cursor...");
            if (threadCursor != null)
                threadCursor.close();
            threadCursor = db.query("thread", new String[] { "_id", "snippet_type", "snippet" }, "snippet_type & " + 0x80000000 + " != 0", null, null, null, "_id", skip + "," + ROW_LIMIT);
            while (threadCursor != null && threadCursor.moveToNext()) {
                listener.setProgress(smsCount + threadCursor.getPosition(), smsCount + threadCount);
                try {
                    String snippet = threadCursor.getString(threadCursor.getColumnIndexOrThrow("snippet"));
                    long snippetType = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("snippet_type"));
                    long id = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id"));
                    if (!TextUtils.isEmpty(snippet)) {
                        snippet = masterCipher.decryptBody(snippet);
                    }
                    if (snippet.startsWith(KEY_EXCHANGE)) {
                        snippet = snippet.substring(KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= 0x8000;
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    } else if (snippet.startsWith(PROCESSED_KEY_EXCHANGE)) {
                        snippet = snippet.substring(PROCESSED_KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= (0x8000 | 0x2000);
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    } else if (snippet.startsWith(STALE_KEY_EXCHANGE)) {
                        snippet = snippet.substring(STALE_KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= (0x8000 | 0x4000);
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    }
                } catch (InvalidMessageException e) {
                    Log.w("DatabaseFactory", e);
                }
            }
            skip += ROW_LIMIT;
        } while (threadCursor != null && threadCursor.getCount() > 0);
        if (smsCursor != null)
            smsCursor.close();
        if (threadCursor != null)
            threadCursor.close();
    }
    if (fromVersion < DatabaseUpgradeActivity.MMS_BODY_VERSION) {
        Log.w("DatabaseFactory", "Update MMS bodies...");
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        Cursor mmsCursor = db.query("mms", new String[] { "_id" }, "msg_box & " + 0x80000000L + " != 0", null, null, null, null);
        Log.w("DatabaseFactory", "Got MMS rows: " + (mmsCursor == null ? "null" : mmsCursor.getCount()));
        while (mmsCursor != null && mmsCursor.moveToNext()) {
            listener.setProgress(mmsCursor.getPosition(), mmsCursor.getCount());
            long mmsId = mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow("_id"));
            String body = null;
            int partCount = 0;
            Cursor partCursor = db.query("part", new String[] { "_id", "ct", "_data", "encrypted" }, "mid = ?", new String[] { mmsId + "" }, null, null, null);
            while (partCursor != null && partCursor.moveToNext()) {
                String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct"));
                if (ContentType.isTextType(contentType)) {
                    try {
                        long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id"));
                        String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data"));
                        boolean encrypted = partCursor.getInt(partCursor.getColumnIndexOrThrow("encrypted")) == 1;
                        File dataFile = new File(dataLocation);
                        InputStream is;
                        if (encrypted)
                            is = new DecryptingPartInputStream(dataFile, masterSecret);
                        else
                            is = new FileInputStream(dataFile);
                        body = (body == null) ? Util.readFullyAsString(is) : body + " " + Util.readFullyAsString(is);
                        //noinspection ResultOfMethodCallIgnored
                        dataFile.delete();
                        db.delete("part", "_id = ?", new String[] { partId + "" });
                    } catch (IOException e) {
                        Log.w("DatabaseFactory", e);
                    }
                } else if (ContentType.isAudioType(contentType) || ContentType.isImageType(contentType) || ContentType.isVideoType(contentType)) {
                    partCount++;
                }
            }
            if (!TextUtils.isEmpty(body)) {
                body = masterCipher.encryptBody(body);
                db.execSQL("UPDATE mms SET body = ?, part_count = ? WHERE _id = ?", new String[] { body, partCount + "", mmsId + "" });
            } else {
                db.execSQL("UPDATE mms SET part_count = ? WHERE _id = ?", new String[] { partCount + "", mmsId + "" });
            }
            Log.w("DatabaseFactory", "Updated body: " + body + " and part_count: " + partCount);
        }
    }
    if (fromVersion < DatabaseUpgradeActivity.TOFU_IDENTITIES_VERSION) {
        File sessionDirectory = new File(context.getFilesDir() + File.separator + "sessions");
        if (sessionDirectory.exists() && sessionDirectory.isDirectory()) {
            File[] sessions = sessionDirectory.listFiles();
            if (sessions != null) {
                for (File session : sessions) {
                    String name = session.getName();
                    if (name.matches("[0-9]+")) {
                        long recipientId = Long.parseLong(name);
                        IdentityKey identityKey = null;
                        if (identityKey != null) {
                            MasterCipher masterCipher = new MasterCipher(masterSecret);
                            String identityKeyString = Base64.encodeBytes(identityKey.serialize());
                            String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId + identityKeyString));
                            db.execSQL("REPLACE INTO identities (recipient, key, mac) VALUES (?, ?, ?)", new String[] { recipientId + "", identityKeyString, macString });
                        }
                    }
                }
            }
        }
    }
    if (fromVersion < DatabaseUpgradeActivity.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) {
        if (!MasterSecretUtil.hasAsymmericMasterSecret(context)) {
            MasterSecretUtil.generateAsymmetricMasterSecret(context, masterSecret);
            MasterCipher masterCipher = new MasterCipher(masterSecret);
            Cursor cursor = null;
            try {
                cursor = db.query(SmsDatabase.TABLE_NAME, new String[] { SmsDatabase.ID, SmsDatabase.BODY, SmsDatabase.TYPE }, SmsDatabase.TYPE + " & ? == 0", new String[] { String.valueOf(SmsDatabase.Types.ENCRYPTION_MASK) }, null, null, null);
                while (cursor.moveToNext()) {
                    long id = cursor.getLong(0);
                    String body = cursor.getString(1);
                    long type = cursor.getLong(2);
                    String encryptedBody = masterCipher.encryptBody(body);
                    ContentValues update = new ContentValues();
                    update.put(SmsDatabase.BODY, encryptedBody);
                    update.put(SmsDatabase.TYPE, type | SmsDatabase.Types.ENCRYPTION_SYMMETRIC_BIT);
                    db.update(SmsDatabase.TABLE_NAME, update, SmsDatabase.ID + " = ?", new String[] { String.valueOf(id) });
                }
            } finally {
                if (cursor != null)
                    cursor.close();
            }
        }
    }
    db.setTransactionSuccessful();
    db.endTransaction();
    //    DecryptingQueue.schedulePendingDecrypts(context, masterSecret);
    MessageNotifier.updateNotification(context, masterSecret);
}
Also used : ContentValues(android.content.ContentValues) InvalidMessageException(org.whispersystems.libsignal.InvalidMessageException) IdentityKey(org.whispersystems.libsignal.IdentityKey) FileInputStream(java.io.FileInputStream) DecryptingPartInputStream(org.thoughtcrime.securesms.crypto.DecryptingPartInputStream) InputStream(java.io.InputStream) MasterCipher(org.thoughtcrime.securesms.crypto.MasterCipher) IOException(java.io.IOException) Cursor(android.database.Cursor) FileInputStream(java.io.FileInputStream) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) File(java.io.File) DecryptingPartInputStream(org.thoughtcrime.securesms.crypto.DecryptingPartInputStream)

Example 5 with IdentityKey

use of org.whispersystems.libsignal.IdentityKey in project Signal-Android by WhisperSystems.

the class PushDecryptJob method handleUntrustedIdentityMessage.

private void handleUntrustedIdentityMessage(@NonNull MasterSecretUnion masterSecret, @NonNull SignalServiceEnvelope envelope, @NonNull Optional<Long> smsMessageId) {
    try {
        EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
        Recipients recipients = RecipientFactory.getRecipientsFromString(context, envelope.getSource(), false);
        long recipientId = recipients.getPrimaryRecipient().getRecipientId();
        byte[] serialized = envelope.hasLegacyMessage() ? envelope.getLegacyMessage() : envelope.getContent();
        PreKeySignalMessage whisperMessage = new PreKeySignalMessage(serialized);
        IdentityKey identityKey = whisperMessage.getIdentityKey();
        String encoded = Base64.encodeBytes(serialized);
        IncomingTextMessage textMessage = new IncomingTextMessage(envelope.getSource(), envelope.getSourceDevice(), envelope.getTimestamp(), encoded, Optional.<SignalServiceGroup>absent(), 0);
        if (!smsMessageId.isPresent()) {
            IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded, envelope.hasLegacyMessage());
            Optional<InsertResult> insertResult = database.insertMessageInbox(masterSecret, bundleMessage);
            if (insertResult.isPresent()) {
                database.setMismatchedIdentity(insertResult.get().getMessageId(), recipientId, identityKey);
                MessageNotifier.updateNotification(context, masterSecret.getMasterSecret().orNull(), insertResult.get().getThreadId());
            }
        } else {
            database.updateMessageBody(masterSecret, smsMessageId.get(), encoded);
            database.markAsPreKeyBundle(smsMessageId.get());
            database.setMismatchedIdentity(smsMessageId.get(), recipientId, identityKey);
        }
    } catch (InvalidMessageException | InvalidVersionException e) {
        throw new AssertionError(e);
    }
}
Also used : InsertResult(org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult) InvalidMessageException(org.whispersystems.libsignal.InvalidMessageException) IdentityKey(org.whispersystems.libsignal.IdentityKey) Recipients(org.thoughtcrime.securesms.recipients.Recipients) InvalidVersionException(org.whispersystems.libsignal.InvalidVersionException) EncryptingSmsDatabase(org.thoughtcrime.securesms.database.EncryptingSmsDatabase) IncomingPreKeyBundleMessage(org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage) PreKeySignalMessage(org.whispersystems.libsignal.protocol.PreKeySignalMessage) IncomingTextMessage(org.thoughtcrime.securesms.sms.IncomingTextMessage)

Aggregations

IdentityKey (org.whispersystems.libsignal.IdentityKey)8 IOException (java.io.IOException)4 ECPrivateKey (org.whispersystems.libsignal.ecc.ECPrivateKey)3 Cursor (android.database.Cursor)2 SQLiteDatabase (android.database.sqlite.SQLiteDatabase)2 Recipient (org.thoughtcrime.securesms.recipients.Recipient)2 IdentityKeyPair (org.whispersystems.libsignal.IdentityKeyPair)2 InvalidKeyException (org.whispersystems.libsignal.InvalidKeyException)2 InvalidMessageException (org.whispersystems.libsignal.InvalidMessageException)2 ContentValues (android.content.ContentValues)1 Intent (android.content.Intent)1 NonNull (android.support.annotation.NonNull)1 UiThread (android.support.annotation.UiThread)1 View (android.view.View)1 File (java.io.File)1 FileInputStream (java.io.FileInputStream)1 InputStream (java.io.InputStream)1 DecryptingPartInputStream (org.thoughtcrime.securesms.crypto.DecryptingPartInputStream)1 MasterCipher (org.thoughtcrime.securesms.crypto.MasterCipher)1 TextSecureSessionStore (org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore)1