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;
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
  {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
 | 
			
		||||
      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;
 | 
			
		||||
 | 
			
		||||
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};
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -6,9 +6,7 @@ import org.whispersystems.libaxolotl.DuplicateMessageException;
 | 
			
		|||
import org.whispersystems.libaxolotl.InvalidMessageException;
 | 
			
		||||
import org.whispersystems.libaxolotl.LegacyMessageException;
 | 
			
		||||
import org.whispersystems.libaxolotl.NoSessionException;
 | 
			
		||||
import org.whispersystems.libaxolotl.ecc.ECKeyPair;
 | 
			
		||||
import org.whispersystems.libaxolotl.protocol.SenderKeyDistributionMessage;
 | 
			
		||||
import org.whispersystems.libaxolotl.util.KeyHelper;
 | 
			
		||||
 | 
			
		||||
import java.security.NoSuchAlgorithmException;
 | 
			
		||||
import java.security.SecureRandom;
 | 
			
		||||
| 
						 | 
				
			
			@ -25,18 +23,13 @@ public class GroupCipherTest extends TestCase {
 | 
			
		|||
    GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
 | 
			
		||||
    GroupSessionBuilder bobSessionBuilder   = new GroupSessionBuilder(bobStore);
 | 
			
		||||
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
 | 
			
		||||
    GroupCipher bobGroupCipher   = new GroupCipher(bobStore, "groupWithBobInIt::aliceUserName");
 | 
			
		||||
 | 
			
		||||
    byte[]    aliceSenderKey        = KeyHelper.generateSenderKey();
 | 
			
		||||
    ECKeyPair aliceSenderSigningKey = KeyHelper.generateSenderSigningKey();
 | 
			
		||||
    int       aliceSenderKeyId      = KeyHelper.generateSenderKeyId();
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, new SenderKeyName("cool group", 1111, 0));
 | 
			
		||||
    GroupCipher bobGroupCipher   = new GroupCipher(bobStore, new SenderKeyName("cool group", 1111, 0));
 | 
			
		||||
 | 
			
		||||
    SenderKeyDistributionMessage aliceDistributionMessage =
 | 
			
		||||
        aliceSessionBuilder.process("groupWithBobInIt", aliceSenderKeyId, 0,
 | 
			
		||||
                                    aliceSenderKey, aliceSenderSigningKey);
 | 
			
		||||
        aliceSessionBuilder.create(new SenderKeyName("cool group", 1111, 0));
 | 
			
		||||
 | 
			
		||||
    bobSessionBuilder.process("groupWithBobInIt::aliceUserName", aliceDistributionMessage);
 | 
			
		||||
    bobSessionBuilder.process(new SenderKeyName("cool group", 1111, 0), aliceDistributionMessage);
 | 
			
		||||
 | 
			
		||||
    byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
 | 
			
		||||
    byte[] plaintextFromAlice  = bobGroupCipher.decrypt(ciphertextFromAlice);
 | 
			
		||||
| 
						 | 
				
			
			@ -53,18 +46,15 @@ public class GroupCipherTest extends TestCase {
 | 
			
		|||
    GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
 | 
			
		||||
    GroupSessionBuilder bobSessionBuilder   = new GroupSessionBuilder(bobStore);
 | 
			
		||||
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
 | 
			
		||||
    GroupCipher bobGroupCipher   = new GroupCipher(bobStore, "groupWithBobInIt::aliceUserName");
 | 
			
		||||
    SenderKeyName aliceName = new SenderKeyName("cool group", 1111, 0);
 | 
			
		||||
 | 
			
		||||
    byte[]    aliceSenderKey        = KeyHelper.generateSenderKey();
 | 
			
		||||
    ECKeyPair aliceSenderSigningKey = KeyHelper.generateSenderSigningKey();
 | 
			
		||||
    int       aliceSenderKeyId      = KeyHelper.generateSenderKeyId();
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
 | 
			
		||||
    GroupCipher bobGroupCipher   = new GroupCipher(bobStore, aliceName);
 | 
			
		||||
 | 
			
		||||
    SenderKeyDistributionMessage aliceDistributionMessage =
 | 
			
		||||
        aliceSessionBuilder.process("groupWithBobInIt", aliceSenderKeyId, 0,
 | 
			
		||||
                                    aliceSenderKey, aliceSenderSigningKey);
 | 
			
		||||
        aliceSessionBuilder.create(aliceName);
 | 
			
		||||
 | 
			
		||||
    bobSessionBuilder.process("groupWithBobInIt::aliceUserName", aliceDistributionMessage);
 | 
			
		||||
    bobSessionBuilder.process(aliceName, aliceDistributionMessage);
 | 
			
		||||
 | 
			
		||||
    byte[] ciphertextFromAlice  = aliceGroupCipher.encrypt("smert ze smert".getBytes());
 | 
			
		||||
    byte[] ciphertextFromAlice2 = aliceGroupCipher.encrypt("smert ze smert2".getBytes());
 | 
			
		||||
| 
						 | 
				
			
			@ -96,19 +86,15 @@ public class GroupCipherTest extends TestCase {
 | 
			
		|||
    GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
 | 
			
		||||
    GroupSessionBuilder bobSessionBuilder   = new GroupSessionBuilder(bobStore);
 | 
			
		||||
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
 | 
			
		||||
    GroupCipher bobGroupCipher   = new GroupCipher(bobStore, "groupWithBobInIt::aliceUserName");
 | 
			
		||||
    SenderKeyName aliceName = new SenderKeyName("cool group", 1111, 0);
 | 
			
		||||
 | 
			
		||||
    byte[]    aliceSenderKey        = KeyHelper.generateSenderKey();
 | 
			
		||||
    ECKeyPair aliceSenderSigningKey = KeyHelper.generateSenderSigningKey();
 | 
			
		||||
    int       aliceSenderKeyId      = KeyHelper.generateSenderKeyId();
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
 | 
			
		||||
    GroupCipher bobGroupCipher   = new GroupCipher(bobStore, aliceName);
 | 
			
		||||
 | 
			
		||||
    SenderKeyDistributionMessage aliceDistributionMessage =
 | 
			
		||||
        aliceSessionBuilder.process("groupWithBobInIt", aliceSenderKeyId, 0,
 | 
			
		||||
                                    aliceSenderKey, aliceSenderSigningKey);
 | 
			
		||||
 | 
			
		||||
    bobSessionBuilder.process("groupWithBobInIt::aliceUserName", aliceDistributionMessage);
 | 
			
		||||
        aliceSessionBuilder.create(aliceName);
 | 
			
		||||
 | 
			
		||||
    bobSessionBuilder.process(aliceName, aliceDistributionMessage);
 | 
			
		||||
 | 
			
		||||
    ArrayList<byte[]> ciphertexts = new ArrayList<>(100);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -127,7 +113,7 @@ public class GroupCipherTest extends TestCase {
 | 
			
		|||
 | 
			
		||||
  public void testEncryptNoSession() {
 | 
			
		||||
    InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore();
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, "groupWithBobInIt");
 | 
			
		||||
    GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, new SenderKeyName("coolio groupio", 1111, 0));
 | 
			
		||||
    try {
 | 
			
		||||
      aliceGroupCipher.encrypt("up the punks".getBytes());
 | 
			
		||||
      throw new AssertionError("Should have failed!");
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,17 +9,17 @@ import java.util.Map;
 | 
			
		|||
 | 
			
		||||
public class InMemorySenderKeyStore implements SenderKeyStore {
 | 
			
		||||
 | 
			
		||||
  private final Map<String, SenderKeyRecord> store = new HashMap<>();
 | 
			
		||||
  private final Map<SenderKeyName, SenderKeyRecord> store = new HashMap<>();
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public void storeSenderKey(String senderKeyId, SenderKeyRecord record) {
 | 
			
		||||
    store.put(senderKeyId, record);
 | 
			
		||||
  public void storeSenderKey(SenderKeyName senderKeyName, SenderKeyRecord record) {
 | 
			
		||||
    store.put(senderKeyName, record);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  @Override
 | 
			
		||||
  public SenderKeyRecord loadSenderKey(String senderKeyId) {
 | 
			
		||||
  public SenderKeyRecord loadSenderKey(SenderKeyName senderKeyName) {
 | 
			
		||||
    try {
 | 
			
		||||
      SenderKeyRecord record = store.get(senderKeyId);
 | 
			
		||||
      SenderKeyRecord record = store.get(senderKeyName);
 | 
			
		||||
 | 
			
		||||
      if (record == null) {
 | 
			
		||||
        return new SenderKeyRecord();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue