Update GroupSessionBuilder and GroupSessionCipher interfaces.
This commit is contained in:
parent
81e91efb3a
commit
752f2dcf69
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.whispersystems.libaxolotl.groups;
|
package org.whispersystems.libaxolotl.groups;
|
||||||
|
|
||||||
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
import org.whispersystems.libaxolotl.DuplicateMessageException;
|
||||||
|
@ -22,18 +38,35 @@ import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main entry point for axolotl group encrypt/decrypt operations.
|
||||||
|
*
|
||||||
|
* Once a session has been established with {@link org.whispersystems.libaxolotl.groups.GroupSessionBuilder}
|
||||||
|
* and a {@link org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage} has been
|
||||||
|
* distributed to each member of the group, this class can be used for all subsequent encrypt/decrypt
|
||||||
|
* operations within that session (ie: until group membership changes).
|
||||||
|
*
|
||||||
|
* @author Moxie Marlinspike
|
||||||
|
*/
|
||||||
public class GroupCipher {
|
public class GroupCipher {
|
||||||
|
|
||||||
static final Object LOCK = new Object();
|
static final Object LOCK = new Object();
|
||||||
|
|
||||||
private final SenderKeyStore senderKeyStore;
|
private final SenderKeyStore senderKeyStore;
|
||||||
private final String senderKeyId;
|
private final SenderKeyName senderKeyId;
|
||||||
|
|
||||||
public GroupCipher(SenderKeyStore senderKeyStore, String senderKeyId) {
|
public GroupCipher(SenderKeyStore senderKeyStore, SenderKeyName senderKeyId) {
|
||||||
this.senderKeyStore = senderKeyStore;
|
this.senderKeyStore = senderKeyStore;
|
||||||
this.senderKeyId = senderKeyId;
|
this.senderKeyId = senderKeyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt a message.
|
||||||
|
*
|
||||||
|
* @param paddedPlaintext The plaintext message bytes, optionally padded.
|
||||||
|
* @return Ciphertext.
|
||||||
|
* @throws NoSessionException
|
||||||
|
*/
|
||||||
public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException {
|
public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException {
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
try {
|
try {
|
||||||
|
@ -58,6 +91,15 @@ public class GroupCipher {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a SenderKey group message.
|
||||||
|
*
|
||||||
|
* @param senderKeyMessageBytes The received ciphertext.
|
||||||
|
* @return Plaintext
|
||||||
|
* @throws LegacyMessageException
|
||||||
|
* @throws InvalidMessageException
|
||||||
|
* @throws DuplicateMessageException
|
||||||
|
*/
|
||||||
public byte[] decrypt(byte[] senderKeyMessageBytes)
|
public byte[] decrypt(byte[] senderKeyMessageBytes)
|
||||||
throws LegacyMessageException, InvalidMessageException, DuplicateMessageException
|
throws LegacyMessageException, InvalidMessageException, DuplicateMessageException
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,9 +1,44 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.whispersystems.libaxolotl.groups;
|
package org.whispersystems.libaxolotl.groups;
|
||||||
|
|
||||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
import org.whispersystems.libaxolotl.InvalidKeyException;
|
||||||
|
import org.whispersystems.libaxolotl.InvalidKeyIdException;
|
||||||
import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord;
|
import org.whispersystems.libaxolotl.groups.state.SenderKeyRecord;
|
||||||
|
import org.whispersystems.libaxolotl.groups.state.SenderKeyState;
|
||||||
import org.whispersystems.libaxolotl.groups.state.SenderKeyStore;
|
import org.whispersystems.libaxolotl.groups.state.SenderKeyStore;
|
||||||
import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage;
|
import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage;
|
||||||
|
import org.whispersystems.libaxolotl.util.KeyHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GroupSessionBuilder is responsible for setting up group SenderKey encrypted sessions.
|
||||||
|
*
|
||||||
|
* Once a session has been established, {@link org.whispersystems.libaxolotl.groups.GroupCipher}
|
||||||
|
* can be used to encrypt/decrypt messages in that session.
|
||||||
|
* <p>
|
||||||
|
* The built sessions are unidirectional: they can be used either for sending or for receiving,
|
||||||
|
* but not both.
|
||||||
|
*
|
||||||
|
* Sessions are constructed per (groupId + senderId + deviceId) tuple. Remote logical users
|
||||||
|
* are identified by their senderId, and each logical recipientId can have multiple physical
|
||||||
|
* devices.
|
||||||
|
*
|
||||||
|
* @author Moxie Marlinspike
|
||||||
|
*/
|
||||||
|
|
||||||
public class GroupSessionBuilder {
|
public class GroupSessionBuilder {
|
||||||
|
|
||||||
|
@ -13,26 +48,52 @@ public class GroupSessionBuilder {
|
||||||
this.senderKeyStore = senderKeyStore;
|
this.senderKeyStore = senderKeyStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void process(String sender, SenderKeyDistributionMessage senderKeyDistributionMessage) {
|
/**
|
||||||
|
* Construct a group session for receiving messages from senderKeyName.
|
||||||
|
*
|
||||||
|
* @param senderKeyName The (groupId, senderId, deviceId) tuple associated with the SenderKeyDistributionMessage.
|
||||||
|
* @param senderKeyDistributionMessage A received SenderKeyDistributionMessage.
|
||||||
|
*/
|
||||||
|
public void process(SenderKeyName senderKeyName, SenderKeyDistributionMessage senderKeyDistributionMessage) {
|
||||||
synchronized (GroupCipher.LOCK) {
|
synchronized (GroupCipher.LOCK) {
|
||||||
SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(sender);
|
SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);
|
||||||
senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(),
|
senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(),
|
||||||
senderKeyDistributionMessage.getIteration(),
|
senderKeyDistributionMessage.getIteration(),
|
||||||
senderKeyDistributionMessage.getChainKey(),
|
senderKeyDistributionMessage.getChainKey(),
|
||||||
senderKeyDistributionMessage.getSignatureKey());
|
senderKeyDistributionMessage.getSignatureKey());
|
||||||
senderKeyStore.storeSenderKey(sender, senderKeyRecord);
|
senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SenderKeyDistributionMessage process(String groupId, int keyId, int iteration,
|
/**
|
||||||
byte[] chainKey, ECKeyPair signatureKey)
|
* Construct a group session for sending messages.
|
||||||
{
|
*
|
||||||
|
* @param senderKeyName The (groupId, senderId, deviceId) tuple. In this case, 'senderId' should be the caller.
|
||||||
|
* @return A SenderKeyDistributionMessage that is individually distributed to each member of the group.
|
||||||
|
*/
|
||||||
|
public SenderKeyDistributionMessage create(SenderKeyName senderKeyName) {
|
||||||
synchronized (GroupCipher.LOCK) {
|
synchronized (GroupCipher.LOCK) {
|
||||||
SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(groupId);
|
try {
|
||||||
senderKeyRecord.setSenderKeyState(keyId, iteration, chainKey, signatureKey);
|
SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);
|
||||||
senderKeyStore.storeSenderKey(groupId, senderKeyRecord);
|
|
||||||
|
|
||||||
return new SenderKeyDistributionMessage(keyId, iteration, chainKey, signatureKey.getPublicKey());
|
if (senderKeyRecord.isEmpty()) {
|
||||||
|
senderKeyRecord.setSenderKeyState(KeyHelper.generateSenderKeyId(),
|
||||||
|
0,
|
||||||
|
KeyHelper.generateSenderKey(),
|
||||||
|
KeyHelper.generateSenderSigningKey());
|
||||||
|
senderKeyStore.storeSenderKey(senderKeyName, senderKeyRecord);
|
||||||
|
}
|
||||||
|
|
||||||
|
SenderKeyState state = senderKeyRecord.getSenderKeyState();
|
||||||
|
|
||||||
|
return new SenderKeyDistributionMessage(state.getKeyId(),
|
||||||
|
state.getSenderChainKey().getIteration(),
|
||||||
|
state.getSenderChainKey().getSeed(),
|
||||||
|
state.getSigningKeyPublic());
|
||||||
|
|
||||||
|
} catch (InvalidKeyIdException | InvalidKeyException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.whispersystems.libaxolotl.groups;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a (groupId + senderId + deviceId) tuple.
|
||||||
|
*/
|
||||||
|
public class SenderKeyName {
|
||||||
|
|
||||||
|
private final String groupId;
|
||||||
|
private final long senderId;
|
||||||
|
private final int deviceId;
|
||||||
|
|
||||||
|
public SenderKeyName(String groupId, long senderId, int deviceId) {
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.senderId = senderId;
|
||||||
|
this.deviceId = deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSenderId() {
|
||||||
|
return senderId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getDeviceId() {
|
||||||
|
return deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String serialize() {
|
||||||
|
return groupId + "::" + String.valueOf(senderId) + "::" + String.valueOf(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (other == null) return false;
|
||||||
|
if (!(other instanceof SenderKeyName)) return false;
|
||||||
|
|
||||||
|
SenderKeyName that = (SenderKeyName)other;
|
||||||
|
|
||||||
|
return
|
||||||
|
this.groupId.equals(that.groupId) &&
|
||||||
|
this.senderId == that.senderId &&
|
||||||
|
this.deviceId == that.deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.groupId.hashCode() ^ (int)this.senderId ^ this.deviceId;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.whispersystems.libaxolotl.groups.ratchet;
|
package org.whispersystems.libaxolotl.groups.ratchet;
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
|
@ -6,6 +22,16 @@ import java.security.NoSuchAlgorithmException;
|
||||||
import javax.crypto.Mac;
|
import javax.crypto.Mac;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Each SenderKey is a "chain" of keys, each derived from the previous.
|
||||||
|
*
|
||||||
|
* At any given point in time, the state of a SenderKey can be represented
|
||||||
|
* as the current chain key value, along with its iteration count. From there,
|
||||||
|
* subsequent iterations can be derived, as well as individual message keys from
|
||||||
|
* each chain key.
|
||||||
|
*
|
||||||
|
* @author Moxie Marlinspike
|
||||||
|
*/
|
||||||
public class SenderChainKey {
|
public class SenderChainKey {
|
||||||
|
|
||||||
private static final byte[] MESSAGE_KEY_SEED = {0x01};
|
private static final byte[] MESSAGE_KEY_SEED = {0x01};
|
||||||
|
|
|
@ -1,8 +1,31 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
package org.whispersystems.libaxolotl.groups.ratchet;
|
package org.whispersystems.libaxolotl.groups.ratchet;
|
||||||
|
|
||||||
import org.whispersystems.libaxolotl.kdf.HKDFv3;
|
import org.whispersystems.libaxolotl.kdf.HKDFv3;
|
||||||
import org.whispersystems.libaxolotl.util.ByteUtil;
|
import org.whispersystems.libaxolotl.util.ByteUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The final symmetric material (IV and Cipher Key) used for encrypting
|
||||||
|
* individual SenderKey messages.
|
||||||
|
*
|
||||||
|
* @author Moxie Marlinspike
|
||||||
|
*/
|
||||||
public class SenderMessageKey {
|
public class SenderMessageKey {
|
||||||
|
|
||||||
private final int iteration;
|
private final int iteration;
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.whispersystems.libaxolotl.groups.state;
|
package org.whispersystems.libaxolotl.groups.state;
|
||||||
|
|
||||||
import org.whispersystems.libaxolotl.InvalidKeyIdException;
|
import org.whispersystems.libaxolotl.InvalidKeyIdException;
|
||||||
|
@ -11,6 +27,12 @@ import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure;
|
import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A durable representation of a set of SenderKeyStates for a specific
|
||||||
|
* SenderKeyName.
|
||||||
|
*
|
||||||
|
* @author Moxie Marlisnpike
|
||||||
|
*/
|
||||||
public class SenderKeyRecord {
|
public class SenderKeyRecord {
|
||||||
|
|
||||||
private List<SenderKeyState> senderKeyStates = new LinkedList<>();
|
private List<SenderKeyState> senderKeyStates = new LinkedList<>();
|
||||||
|
@ -25,6 +47,10 @@ public class SenderKeyRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return senderKeyStates.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
public SenderKeyState getSenderKeyState() throws InvalidKeyIdException {
|
public SenderKeyState getSenderKeyState() throws InvalidKeyIdException {
|
||||||
if (!senderKeyStates.isEmpty()) {
|
if (!senderKeyStates.isEmpty()) {
|
||||||
return senderKeyStates.get(0);
|
return senderKeyStates.get(0);
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.whispersystems.libaxolotl.groups.state;
|
package org.whispersystems.libaxolotl.groups.state;
|
||||||
|
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
@ -17,6 +33,11 @@ import java.util.List;
|
||||||
|
|
||||||
import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure;
|
import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the state of an individual SenderKey ratchet.
|
||||||
|
*
|
||||||
|
* @author Moxie Marlinspike
|
||||||
|
*/
|
||||||
public class SenderKeyState {
|
public class SenderKeyState {
|
||||||
|
|
||||||
private SenderKeyStateStructure senderKeyStateStructure;
|
private SenderKeyStateStructure senderKeyStateStructure;
|
||||||
|
|
|
@ -1,6 +1,48 @@
|
||||||
|
/**
|
||||||
|
* Copyright (C) 2014-2015 Open Whisper Systems
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
package org.whispersystems.libaxolotl.groups.state;
|
package org.whispersystems.libaxolotl.groups.state;
|
||||||
|
|
||||||
|
import org.whispersystems.libaxolotl.groups.SenderKeyName;
|
||||||
|
|
||||||
public interface SenderKeyStore {
|
public interface SenderKeyStore {
|
||||||
public void storeSenderKey(String senderKeyId, SenderKeyRecord record);
|
|
||||||
public SenderKeyRecord loadSenderKey(String senderKeyId);
|
/**
|
||||||
|
* Commit to storage the {@link org.whispersystems.libaxolotl.groups.state.SenderKeyRecord} for a
|
||||||
|
* given (groupId + senderId + deviceId) tuple.
|
||||||
|
*
|
||||||
|
* @param senderKeyName the (groupId + senderId + deviceId) tuple.
|
||||||
|
* @param record the current SenderKeyRecord for the specified senderKeyName.
|
||||||
|
*/
|
||||||
|
public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a copy of the {@link org.whispersystems.libaxolotl.groups.state.SenderKeyRecord}
|
||||||
|
* corresponding to the (groupId + senderId + deviceId) tuple, or a new SenderKeyRecord if
|
||||||
|
* one does not currently exist.
|
||||||
|
* <p>
|
||||||
|
* It is important that implementations return a copy of the current durable information. The
|
||||||
|
* returned SenderKeyRecord may be modified, but those changes should not have an effect on the
|
||||||
|
* durable session state (what is returned by subsequent calls to this method) without the
|
||||||
|
* store method being called here first.
|
||||||
|
*
|
||||||
|
* @param senderKeyName The (groupId + senderId + deviceId) tuple.
|
||||||
|
* @return a copy of the SenderKeyRecord corresponding to the (groupId + senderId + deviceId tuple, or
|
||||||
|
* a new SenderKeyRecord if one does not currently exist.
|
||||||
|
*/
|
||||||
|
|
||||||
|
public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,7 @@ import org.whispersystems.libaxolotl.DuplicateMessageException;
|
||||||
import org.whispersystems.libaxolotl.InvalidMessageException;
|
import org.whispersystems.libaxolotl.InvalidMessageException;
|
||||||
import org.whispersystems.libaxolotl.LegacyMessageException;
|
import org.whispersystems.libaxolotl.LegacyMessageException;
|
||||||
import org.whispersystems.libaxolotl.NoSessionException;
|
import org.whispersystems.libaxolotl.NoSessionException;
|
||||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
|
|
||||||
import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage;
|
import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage;
|
||||||
import org.whispersystems.libaxolotl.util.KeyHelper;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
@ -25,18 +23,13 @@ public class GroupCipherTest extends TestCase {
|
||||||
GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
|
GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
|
||||||
GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
|
GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
|
||||||
|
|
||||||
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
|
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, new SenderKeyName("cool group", 1111, 0));
|
||||||
GroupCipher bobGroupCipher = new GroupCipher(bobStore, "groupWithBobInIt::aliceUserName");
|
GroupCipher bobGroupCipher = new GroupCipher(bobStore, new SenderKeyName("cool group", 1111, 0));
|
||||||
|
|
||||||
byte[] aliceSenderKey = KeyHelper.generateSenderKey();
|
|
||||||
ECKeyPair aliceSenderSigningKey = KeyHelper.generateSenderSigningKey();
|
|
||||||
int aliceSenderKeyId = KeyHelper.generateSenderKeyId();
|
|
||||||
|
|
||||||
SenderKeyDistributionMessage aliceDistributionMessage =
|
SenderKeyDistributionMessage aliceDistributionMessage =
|
||||||
aliceSessionBuilder.process("groupWithBobInIt", aliceSenderKeyId, 0,
|
aliceSessionBuilder.create(new SenderKeyName("cool group", 1111, 0));
|
||||||
aliceSenderKey, aliceSenderSigningKey);
|
|
||||||
|
|
||||||
bobSessionBuilder.process("groupWithBobInIt::aliceUserName", aliceDistributionMessage);
|
bobSessionBuilder.process(new SenderKeyName("cool group", 1111, 0), aliceDistributionMessage);
|
||||||
|
|
||||||
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
|
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
|
||||||
byte[] plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice);
|
byte[] plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice);
|
||||||
|
@ -53,18 +46,15 @@ public class GroupCipherTest extends TestCase {
|
||||||
GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
|
GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
|
||||||
GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
|
GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
|
||||||
|
|
||||||
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
|
SenderKeyName aliceName = new SenderKeyName("cool group", 1111, 0);
|
||||||
GroupCipher bobGroupCipher = new GroupCipher(bobStore, "groupWithBobInIt::aliceUserName");
|
|
||||||
|
|
||||||
byte[] aliceSenderKey = KeyHelper.generateSenderKey();
|
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
|
||||||
ECKeyPair aliceSenderSigningKey = KeyHelper.generateSenderSigningKey();
|
GroupCipher bobGroupCipher = new GroupCipher(bobStore, aliceName);
|
||||||
int aliceSenderKeyId = KeyHelper.generateSenderKeyId();
|
|
||||||
|
|
||||||
SenderKeyDistributionMessage aliceDistributionMessage =
|
SenderKeyDistributionMessage aliceDistributionMessage =
|
||||||
aliceSessionBuilder.process("groupWithBobInIt", aliceSenderKeyId, 0,
|
aliceSessionBuilder.create(aliceName);
|
||||||
aliceSenderKey, aliceSenderSigningKey);
|
|
||||||
|
|
||||||
bobSessionBuilder.process("groupWithBobInIt::aliceUserName", aliceDistributionMessage);
|
bobSessionBuilder.process(aliceName, aliceDistributionMessage);
|
||||||
|
|
||||||
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
|
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
|
||||||
byte[] ciphertextFromAlice2 = aliceGroupCipher.encrypt("smert ze smert2".getBytes());
|
byte[] ciphertextFromAlice2 = aliceGroupCipher.encrypt("smert ze smert2".getBytes());
|
||||||
|
@ -96,19 +86,15 @@ public class GroupCipherTest extends TestCase {
|
||||||
GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
|
GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
|
||||||
GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
|
GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
|
||||||
|
|
||||||
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
|
SenderKeyName aliceName = new SenderKeyName("cool group", 1111, 0);
|
||||||
GroupCipher bobGroupCipher = new GroupCipher(bobStore, "groupWithBobInIt::aliceUserName");
|
|
||||||
|
|
||||||
byte[] aliceSenderKey = KeyHelper.generateSenderKey();
|
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
|
||||||
ECKeyPair aliceSenderSigningKey = KeyHelper.generateSenderSigningKey();
|
GroupCipher bobGroupCipher = new GroupCipher(bobStore, aliceName);
|
||||||
int aliceSenderKeyId = KeyHelper.generateSenderKeyId();
|
|
||||||
|
|
||||||
SenderKeyDistributionMessage aliceDistributionMessage =
|
SenderKeyDistributionMessage aliceDistributionMessage =
|
||||||
aliceSessionBuilder.process("groupWithBobInIt", aliceSenderKeyId, 0,
|
aliceSessionBuilder.create(aliceName);
|
||||||
aliceSenderKey, aliceSenderSigningKey);
|
|
||||||
|
|
||||||
bobSessionBuilder.process("groupWithBobInIt::aliceUserName", aliceDistributionMessage);
|
|
||||||
|
|
||||||
|
bobSessionBuilder.process(aliceName, aliceDistributionMessage);
|
||||||
|
|
||||||
ArrayList<byte[]> ciphertexts = new ArrayList<>(100);
|
ArrayList<byte[]> ciphertexts = new ArrayList<>(100);
|
||||||
|
|
||||||
|
@ -127,7 +113,7 @@ public class GroupCipherTest extends TestCase {
|
||||||
|
|
||||||
public void testEncryptNoSession() {
|
public void testEncryptNoSession() {
|
||||||
InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore();
|
InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore();
|
||||||
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
|
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, new SenderKeyName("coolio groupio", 1111, 0));
|
||||||
try {
|
try {
|
||||||
aliceGroupCipher.encrypt("up the punks".getBytes());
|
aliceGroupCipher.encrypt("up the punks".getBytes());
|
||||||
throw new AssertionError("Should have failed!");
|
throw new AssertionError("Should have failed!");
|
||||||
|
|
|
@ -9,17 +9,17 @@ import java.util.Map;
|
||||||
|
|
||||||
public class InMemorySenderKeyStore implements SenderKeyStore {
|
public class InMemorySenderKeyStore implements SenderKeyStore {
|
||||||
|
|
||||||
private final Map<String, SenderKeyRecord> store = new HashMap<>();
|
private final Map<SenderKeyName, SenderKeyRecord> store = new HashMap<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeSenderKey(String senderKeyId, SenderKeyRecord record) {
|
public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record) {
|
||||||
store.put(senderKeyId, record);
|
store.put(senderKeyName, record);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SenderKeyRecord loadSenderKey(String senderKeyId) {
|
public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName) {
|
||||||
try {
|
try {
|
||||||
SenderKeyRecord record = store.get(senderKeyId);
|
SenderKeyRecord record = store.get(senderKeyName);
|
||||||
|
|
||||||
if (record == null) {
|
if (record == null) {
|
||||||
return new SenderKeyRecord();
|
return new SenderKeyRecord();
|
||||||
|
|
Loading…
Reference in New Issue