Switch payload encryption to AES-GCM

This also ensures that the IV is generated with proper randomness.
This commit is contained in:
Andreas Straub 2015-07-21 01:15:32 +02:00
parent 971aa3a11e
commit 122bc97ce2
3 changed files with 46 additions and 17 deletions

View file

@ -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) {

View file

@ -0,0 +1,7 @@
package eu.siacs.conversations.crypto.axolotl;
public class CryptoFailedException extends Exception {
public CryptoFailedException(Exception e){
super(e);
}
}

View file

@ -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;
}