From 122bc97ce24181ccd07cf9badf8d4c3b81d80c3f Mon Sep 17 00:00:00 2001 From: Andreas Straub Date: Tue, 21 Jul 2015 01:15:32 +0200 Subject: Switch payload encryption to AES-GCM This also ensures that the IV is generated with proper randomness. --- .../crypto/axolotl/AxolotlService.java | 28 +++++++++++++++++----- .../crypto/axolotl/CryptoFailedException.java | 7 ++++++ .../crypto/axolotl/XmppAxolotlMessage.java | 28 +++++++++++++--------- 3 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 src/main/java/eu/siacs/conversations/crypto/axolotl/CryptoFailedException.java (limited to 'src') diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java index b0724593..fbea3b0f 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/AxolotlService.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.util.Log; +import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.whispersystems.libaxolotl.AxolotlAddress; import org.whispersystems.libaxolotl.DuplicateMessageException; import org.whispersystems.libaxolotl.IdentityKey; @@ -30,6 +31,7 @@ import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SignedPreKeyRecord; import org.whispersystems.libaxolotl.util.KeyHelper; +import java.security.Security; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; @@ -678,6 +680,9 @@ public class AxolotlService { } public AxolotlService(Account account, XmppConnectionService connectionService) { + if (Security.getProvider("BC") == null) { + Security.addProvider(new BouncyCastleProvider()); + } this.mXmppConnectionService = connectionService; this.account = account; this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService); @@ -1050,11 +1055,17 @@ public class AxolotlService { final String content; if (message.hasFileOnRemoteHost()) { content = message.getFileParams().url.toString(); - } else { - content = message.getBody(); - } - final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(message.getContact().getJid().toBareJid(), - getOwnDeviceId(), content); + } else { + content = message.getBody(); + } + final XmppAxolotlMessage axolotlMessage; + try { + axolotlMessage = new XmppAxolotlMessage(message.getContact().getJid().toBareJid(), + getOwnDeviceId(), content); + } catch (CryptoFailedException e) { + Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage()); + return null; + } if(findSessionsforContact(message.getContact()).isEmpty()) { return null; @@ -1143,7 +1154,12 @@ public class AxolotlService { byte[] payloadKey = session.processReceiving(header); if (payloadKey != null) { Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Got payload key from axolotl header. Decrypting message..."); - plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint()); + try{ + plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint()); + } catch (CryptoFailedException e) { + Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to decrypt message: " + e.getMessage()); + break; + } } Integer preKeyId = session.getPreKeyId(); if (preKeyId != null) { diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/CryptoFailedException.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/CryptoFailedException.java new file mode 100644 index 00000000..5796ef30 --- /dev/null +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/CryptoFailedException.java @@ -0,0 +1,7 @@ +package eu.siacs.conversations.crypto.axolotl; + +public class CryptoFailedException extends Exception { + public CryptoFailedException(Exception e){ + super(e); + } +} diff --git a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java index ec068ec7..24afeaea 100644 --- a/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java +++ b/src/main/java/eu/siacs/conversations/crypto/axolotl/XmppAxolotlMessage.java @@ -6,6 +6,8 @@ import android.util.Base64; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.SecureRandom; import java.util.HashSet; import java.util.Set; @@ -107,26 +109,30 @@ public class XmppAxolotlMessage { } } - public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) { + public XmppAxolotlMessage(Jid from, int sourceDeviceId, String plaintext) throws CryptoFailedException{ this.from = from; this.sourceDeviceId = sourceDeviceId; this.headers = new HashSet<>(); this.encrypt(plaintext); } - private void encrypt(String plaintext) { + private void encrypt(String plaintext) throws CryptoFailedException { try { KeyGenerator generator = KeyGenerator.getInstance("AES"); generator.init(128); SecretKey secretKey = generator.generateKey(); - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - cipher.init(Cipher.ENCRYPT_MODE, secretKey); + SecureRandom random = new SecureRandom(); + this.iv = new byte[16]; + random.nextBytes(iv); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); + cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec); this.innerKey = secretKey.getEncoded(); - this.iv = cipher.getIV(); this.ciphertext = cipher.doFinal(plaintext.getBytes()); } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException - | IllegalBlockSizeException | BadPaddingException e) { - + | IllegalBlockSizeException | BadPaddingException | NoSuchProviderException + | InvalidAlgorithmParameterException e) { + throw new CryptoFailedException(e); } } @@ -174,11 +180,11 @@ public class XmppAxolotlMessage { } - public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key, String fingerprint) { + public XmppAxolotlPlaintextMessage decrypt(AxolotlService.XmppAxolotlSession session, byte[] key, String fingerprint) throws CryptoFailedException { XmppAxolotlPlaintextMessage plaintextMessage = null; try { - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC"); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); IvParameterSpec ivSpec = new IvParameterSpec(iv); @@ -189,8 +195,8 @@ public class XmppAxolotlMessage { } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException - | BadPaddingException e) { - throw new AssertionError(e); + | BadPaddingException | NoSuchProviderException e) { + throw new CryptoFailedException(e); } return plaintextMessage; } -- cgit v1.2.3