From 60800e155612bea797eed93c67046a23d26054cc Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 24 Nov 2014 12:54:30 -0800 Subject: Break out into separate repo. --- .../libaxolotl/groups/state/SenderKeyRecord.java | 64 +++++++++ .../libaxolotl/groups/state/SenderKeyState.java | 144 +++++++++++++++++++++ .../libaxolotl/groups/state/SenderKeyStore.java | 6 + 3 files changed, 214 insertions(+) create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java create mode 100644 src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java (limited to 'src/main/java/org/whispersystems/libaxolotl/groups/state') diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java new file mode 100644 index 00000000..bb1ba952 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyRecord.java @@ -0,0 +1,64 @@ +package org.whispersystems.libaxolotl.groups.state; + +import org.whispersystems.libaxolotl.InvalidKeyIdException; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.state.StorageProtos; + +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyRecordStructure; + +public class SenderKeyRecord { + + private List senderKeyStates = new LinkedList<>(); + + public SenderKeyRecord() {} + + public SenderKeyRecord(byte[] serialized) throws IOException { + SenderKeyRecordStructure senderKeyRecordStructure = SenderKeyRecordStructure.parseFrom(serialized); + + for (StorageProtos.SenderKeyStateStructure structure : senderKeyRecordStructure.getSenderKeyStatesList()) { + this.senderKeyStates.add(new SenderKeyState(structure)); + } + } + + public SenderKeyState getSenderKeyState() throws InvalidKeyIdException { + if (!senderKeyStates.isEmpty()) { + return senderKeyStates.get(0); + } else { + throw new InvalidKeyIdException("No key state in record!"); + } + } + + public SenderKeyState getSenderKeyState(int keyId) throws InvalidKeyIdException { + for (SenderKeyState state : senderKeyStates) { + if (state.getKeyId() == keyId) { + return state; + } + } + + throw new InvalidKeyIdException("No keys for: " + keyId); + } + + public void addSenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + senderKeyStates.add(new SenderKeyState(id, iteration, chainKey, signatureKey)); + } + + public void setSenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { + senderKeyStates.clear(); + senderKeyStates.add(new SenderKeyState(id, iteration, chainKey, signatureKey)); + } + + public byte[] serialize() { + SenderKeyRecordStructure.Builder recordStructure = SenderKeyRecordStructure.newBuilder(); + + for (SenderKeyState senderKeyState : senderKeyStates) { + recordStructure.addSenderKeyStates(senderKeyState.getStructure()); + } + + return recordStructure.build().toByteArray(); + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java new file mode 100644 index 00000000..80498ce0 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyState.java @@ -0,0 +1,144 @@ +package org.whispersystems.libaxolotl.groups.state; + +import com.google.protobuf.ByteString; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECKeyPair; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.groups.ratchet.SenderChainKey; +import org.whispersystems.libaxolotl.groups.ratchet.SenderMessageKey; +import org.whispersystems.libaxolotl.util.guava.Optional; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static org.whispersystems.libaxolotl.state.StorageProtos.SenderKeyStateStructure; + +public class SenderKeyState { + + private SenderKeyStateStructure senderKeyStateStructure; + + public SenderKeyState(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) { + this(id, iteration, chainKey, signatureKey, Optional.absent()); + } + + public SenderKeyState(int id, int iteration, byte[] chainKey, ECKeyPair signatureKey) { + this(id, iteration, chainKey, signatureKey.getPublicKey(), Optional.of(signatureKey.getPrivateKey())); + } + + private SenderKeyState(int id, int iteration, byte[] chainKey, + ECPublicKey signatureKeyPublic, + Optional signatureKeyPrivate) + { + SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = + SenderKeyStateStructure.SenderChainKey.newBuilder() + .setIteration(iteration) + .setSeed(ByteString.copyFrom(chainKey)) + .build(); + + SenderKeyStateStructure.SenderSigningKey.Builder signingKeyStructure = + SenderKeyStateStructure.SenderSigningKey.newBuilder() + .setPublic(ByteString.copyFrom(signatureKeyPublic.serialize())); + + if (signatureKeyPrivate.isPresent()) { + signingKeyStructure.setPrivate(ByteString.copyFrom(signatureKeyPrivate.get().serialize())); + } + + this.senderKeyStateStructure = SenderKeyStateStructure.newBuilder() + .setSenderKeyId(id) + .setSenderChainKey(senderChainKeyStructure) + .setSenderSigningKey(signingKeyStructure) + .build(); + } + + public SenderKeyState(SenderKeyStateStructure senderKeyStateStructure) { + this.senderKeyStateStructure = senderKeyStateStructure; + } + + public int getKeyId() { + return senderKeyStateStructure.getSenderKeyId(); + } + + public SenderChainKey getSenderChainKey() { + return new SenderChainKey(senderKeyStateStructure.getSenderChainKey().getIteration(), + senderKeyStateStructure.getSenderChainKey().getSeed().toByteArray()); + } + + public void setSenderChainKey(SenderChainKey chainKey) { + SenderKeyStateStructure.SenderChainKey senderChainKeyStructure = + SenderKeyStateStructure.SenderChainKey.newBuilder() + .setIteration(chainKey.getIteration()) + .setSeed(ByteString.copyFrom(chainKey.getSeed())) + .build(); + + this.senderKeyStateStructure = senderKeyStateStructure.toBuilder() + .setSenderChainKey(senderChainKeyStructure) + .build(); + } + + public ECPublicKey getSigningKeyPublic() throws InvalidKeyException { + return Curve.decodePoint(senderKeyStateStructure.getSenderSigningKey() + .getPublic() + .toByteArray(), 0); + } + + public ECPrivateKey getSigningKeyPrivate() { + return Curve.decodePrivatePoint(senderKeyStateStructure.getSenderSigningKey() + .getPrivate().toByteArray()); + } + + public boolean hasSenderMessageKey(int iteration) { + for (SenderKeyStateStructure.SenderMessageKey senderMessageKey : senderKeyStateStructure.getSenderMessageKeysList()) { + if (senderMessageKey.getIteration() == iteration) return true; + } + + return false; + } + + public void addSenderMessageKey(SenderMessageKey senderMessageKey) { + SenderKeyStateStructure.SenderMessageKey senderMessageKeyStructure = + SenderKeyStateStructure.SenderMessageKey.newBuilder() + .setIteration(senderMessageKey.getIteration()) + .setSeed(ByteString.copyFrom(senderMessageKey.getSeed())) + .build(); + + this.senderKeyStateStructure = this.senderKeyStateStructure.toBuilder() + .addSenderMessageKeys(senderMessageKeyStructure) + .build(); + } + + public SenderMessageKey removeSenderMessageKey(int iteration) { + List keys = new LinkedList<>(senderKeyStateStructure.getSenderMessageKeysList()); + Iterator iterator = keys.iterator(); + + SenderKeyStateStructure.SenderMessageKey result = null; + + while (iterator.hasNext()) { + SenderKeyStateStructure.SenderMessageKey senderMessageKey = iterator.next(); + + if (senderMessageKey.getIteration() == iteration) { + result = senderMessageKey; + iterator.remove(); + break; + } + } + + this.senderKeyStateStructure = this.senderKeyStateStructure.toBuilder() + .clearSenderMessageKeys() + .addAllSenderMessageKeys(keys) + .build(); + + if (result != null) { + return new SenderMessageKey(result.getIteration(), result.getSeed().toByteArray()); + } else { + return null; + } + } + + public SenderKeyStateStructure getStructure() { + return senderKeyStateStructure; + } +} diff --git a/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java new file mode 100644 index 00000000..da01b1f3 --- /dev/null +++ b/src/main/java/org/whispersystems/libaxolotl/groups/state/SenderKeyStore.java @@ -0,0 +1,6 @@ +package org.whispersystems.libaxolotl.groups.state; + +public interface SenderKeyStore { + public void storeSenderKey(String senderKeyId, SenderKeyRecord record); + public SenderKeyRecord loadSenderKey(String senderKeyId); +} -- cgit v1.2.3