aboutsummaryrefslogtreecommitdiffstats
path: root/java
diff options
context:
space:
mode:
Diffstat (limited to 'java')
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java46
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java83
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/SenderKeyName.java68
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java26
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java23
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java26
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java21
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java46
8 files changed, 324 insertions, 15 deletions
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java
index 43dac752..778ca916 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupCipher.java
@@ -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;
import org.whispersystems.libaxolotl.DuplicateMessageException;
@@ -22,18 +38,35 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
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 {
static final Object LOCK = new Object();
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.senderKeyId = senderKeyId;
}
+ /**
+ * Encrypt a message.
+ *
+ * @param paddedPlaintext The plaintext message bytes, optionally padded.
+ * @return Ciphertext.
+ * @throws NoSessionException
+ */
public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException {
synchronized (LOCK) {
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)
throws LegacyMessageException, InvalidMessageException, DuplicateMessageException
{
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java
index 8b73484b..ee02fffd 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/GroupSessionBuilder.java
@@ -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;
-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.SenderKeyState;
import org.whispersystems.libaxolotl.groups.state.SenderKeyStore;
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 {
@@ -13,26 +48,52 @@ public class GroupSessionBuilder {
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) {
- SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(sender);
+ SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);
senderKeyRecord.addSenderKeyState(senderKeyDistributionMessage.getId(),
senderKeyDistributionMessage.getIteration(),
senderKeyDistributionMessage.getChainKey(),
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) {
- SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(groupId);
- senderKeyRecord.setSenderKeyState(keyId, iteration, chainKey, signatureKey);
- senderKeyStore.storeSenderKey(groupId, senderKeyRecord);
+ try {
+ SenderKeyRecord senderKeyRecord = senderKeyStore.loadSenderKey(senderKeyName);
+
+ 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());
- return new SenderKeyDistributionMessage(keyId, iteration, chainKey, signatureKey.getPublicKey());
+ } catch (InvalidKeyIdException | InvalidKeyException e) {
+ throw new AssertionError(e);
+ }
}
}
}
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/SenderKeyName.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/SenderKeyName.java
new file mode 100644
index 00000000..ce4325ff
--- /dev/null
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/SenderKeyName.java
@@ -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;
+ }
+
+}
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java
index 71375923..304c4667 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderChainKey.java
@@ -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;
import java.security.InvalidKeyException;
@@ -6,6 +22,16 @@ import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
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 {
private static final byte[] MESSAGE_KEY_SEED = {0x01};
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java
index 8808a8e8..cdee444d 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/ratchet/SenderMessageKey.java
@@ -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;
import org.whispersystems.libaxolotl.kdf.HKDFv3;
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 {
private final int iteration;
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java
index bb1ba952..f023b10f 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java
@@ -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;
import org.whispersystems.libaxolotl.InvalidKeyIdException;
@@ -11,6 +27,12 @@ import java.util.List;
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 {
private List<SenderKeyState> senderKeyStates = new LinkedList<>();
@@ -25,6 +47,10 @@ public class SenderKeyRecord {
}
}
+ public boolean isEmpty() {
+ return senderKeyStates.isEmpty();
+ }
+
public SenderKeyState getSenderKeyState() throws InvalidKeyIdException {
if (!senderKeyStates.isEmpty()) {
return senderKeyStates.get(0);
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java
index 80498ce0..0b0f9a63 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java
@@ -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;
import com.google.protobuf.ByteString;
@@ -17,6 +33,11 @@ import java.util.List;
import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure;
+/**
+ * Represents the state of an individual SenderKey ratchet.
+ *
+ * @author Moxie Marlinspike
+ */
public class SenderKeyState {
private SenderKeyStateStructure senderKeyStateStructure;
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java b/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java
index da01b1f3..42afb628 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java
@@ -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;
+import org.whispersystems.libaxolotl.groups.SenderKeyName;
+
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);
}