Switch payload encryption to AES-GCM
This also ensures that the IV is generated with proper randomness.
This commit is contained in:
parent
971aa3a11e
commit
122bc97ce2
|
@ -4,6 +4,7 @@ import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.whispersystems.libaxolotl.AxolotlAddress;
|
import org.whispersystems.libaxolotl.AxolotlAddress;
|
||||||
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
||||||
import org.whispersystems.libaxolotl.IdentityKey;
|
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.state.SignedPreKeyRecord;
|
||||||
import org.whispersystems.libaxolotl.util.KeyHelper;
|
import org.whispersystems.libaxolotl.util.KeyHelper;
|
||||||
|
|
||||||
|
import java.security.Security;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
@ -678,6 +680,9 @@ public class AxolotlService {
|
||||||
}
|
}
|
||||||
|
|
||||||
public AxolotlService(Account account, XmppConnectionService connectionService) {
|
public AxolotlService(Account account, XmppConnectionService connectionService) {
|
||||||
|
if (Security.getProvider("BC") == null) {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
this.mXmppConnectionService = connectionService;
|
this.mXmppConnectionService = connectionService;
|
||||||
this.account = account;
|
this.account = account;
|
||||||
this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
|
this.axolotlStore = new SQLiteAxolotlStore(this.account, this.mXmppConnectionService);
|
||||||
|
@ -1053,8 +1058,14 @@ public class AxolotlService {
|
||||||
} else {
|
} else {
|
||||||
content = message.getBody();
|
content = message.getBody();
|
||||||
}
|
}
|
||||||
final XmppAxolotlMessage axolotlMessage = new XmppAxolotlMessage(message.getContact().getJid().toBareJid(),
|
final XmppAxolotlMessage axolotlMessage;
|
||||||
|
try {
|
||||||
|
axolotlMessage = new XmppAxolotlMessage(message.getContact().getJid().toBareJid(),
|
||||||
getOwnDeviceId(), content);
|
getOwnDeviceId(), content);
|
||||||
|
} catch (CryptoFailedException e) {
|
||||||
|
Log.w(Config.LOGTAG, getLogprefix(account) + "Failed to encrypt message: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if(findSessionsforContact(message.getContact()).isEmpty()) {
|
if(findSessionsforContact(message.getContact()).isEmpty()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -1143,7 +1154,12 @@ public class AxolotlService {
|
||||||
byte[] payloadKey = session.processReceiving(header);
|
byte[] payloadKey = session.processReceiving(header);
|
||||||
if (payloadKey != null) {
|
if (payloadKey != null) {
|
||||||
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Got payload key from axolotl header. Decrypting message...");
|
Log.d(Config.LOGTAG, AxolotlService.getLogprefix(account)+"Got payload key from axolotl header. Decrypting message...");
|
||||||
|
try{
|
||||||
plaintextMessage = message.decrypt(session, payloadKey, session.getFingerprint());
|
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();
|
Integer preKeyId = session.getPreKeyId();
|
||||||
if (preKeyId != null) {
|
if (preKeyId != null) {
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
package eu.siacs.conversations.crypto.axolotl;
|
||||||
|
|
||||||
|
public class CryptoFailedException extends Exception {
|
||||||
|
public CryptoFailedException(Exception e){
|
||||||
|
super(e);
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,8 @@ import android.util.Base64;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.NoSuchProviderException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
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.from = from;
|
||||||
this.sourceDeviceId = sourceDeviceId;
|
this.sourceDeviceId = sourceDeviceId;
|
||||||
this.headers = new HashSet<>();
|
this.headers = new HashSet<>();
|
||||||
this.encrypt(plaintext);
|
this.encrypt(plaintext);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void encrypt(String plaintext) {
|
private void encrypt(String plaintext) throws CryptoFailedException {
|
||||||
try {
|
try {
|
||||||
KeyGenerator generator = KeyGenerator.getInstance("AES");
|
KeyGenerator generator = KeyGenerator.getInstance("AES");
|
||||||
generator.init(128);
|
generator.init(128);
|
||||||
SecretKey secretKey = generator.generateKey();
|
SecretKey secretKey = generator.generateKey();
|
||||||
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
|
SecureRandom random = new SecureRandom();
|
||||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
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.innerKey = secretKey.getEncoded();
|
||||||
this.iv = cipher.getIV();
|
|
||||||
this.ciphertext = cipher.doFinal(plaintext.getBytes());
|
this.ciphertext = cipher.doFinal(plaintext.getBytes());
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
} 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;
|
XmppAxolotlPlaintextMessage plaintextMessage = null;
|
||||||
try {
|
try {
|
||||||
|
|
||||||
Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding");
|
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
|
||||||
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
|
||||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||||
|
|
||||||
|
@ -189,8 +195,8 @@ public class XmppAxolotlMessage {
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException
|
||||||
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
| InvalidAlgorithmParameterException | IllegalBlockSizeException
|
||||||
| BadPaddingException e) {
|
| BadPaddingException | NoSuchProviderException e) {
|
||||||
throw new AssertionError(e);
|
throw new CryptoFailedException(e);
|
||||||
}
|
}
|
||||||
return plaintextMessage;
|
return plaintextMessage;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue