diff options
Diffstat (limited to 'java')
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); } |