From 2a652571827066a46b7dd2d3cbbb365bb6e76523 Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Mon, 28 Apr 2014 12:10:24 -0700 Subject: [PATCH] Add serialization helpers for IdentityKeyPair. --- .../protobuf/LocalStorageProtocol.proto | 5 + .../libaxolotl/IdentityKeyPair.java | 25 +- .../libaxolotl/state/StorageProtos.java | 420 +++++++++++++++++- .../storage/TextSecureSessionStore.java | 6 +- 4 files changed, 450 insertions(+), 6 deletions(-) diff --git a/libaxolotl/protobuf/LocalStorageProtocol.proto b/libaxolotl/protobuf/LocalStorageProtocol.proto index f1a72a4125..9339bb706c 100644 --- a/libaxolotl/protobuf/LocalStorageProtocol.proto +++ b/libaxolotl/protobuf/LocalStorageProtocol.proto @@ -67,4 +67,9 @@ message PreKeyRecordStructure { optional uint32 id = 1; optional bytes publicKey = 2; optional bytes privateKey = 3; +} + +message IdentityKeyPairStructure { + optional bytes publicKey = 1; + optional bytes privateKey = 2; } \ No newline at end of file diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java index 220be57a64..c47df4bcd5 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/IdentityKeyPair.java @@ -16,8 +16,14 @@ */ package org.whispersystems.libaxolotl; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure; + /** * Holder for public and private identity key pair. * @@ -25,7 +31,7 @@ import org.whispersystems.libaxolotl.ecc.ECPrivateKey; */ public class IdentityKeyPair { - private final IdentityKey publicKey; + private final IdentityKey publicKey; private final ECPrivateKey privateKey; public IdentityKeyPair(IdentityKey publicKey, ECPrivateKey privateKey) { @@ -33,6 +39,16 @@ public class IdentityKeyPair { this.privateKey = privateKey; } + public IdentityKeyPair(byte[] serialized) throws InvalidKeyException { + try { + IdentityKeyPairStructure structure = IdentityKeyPairStructure.parseFrom(serialized); + this.publicKey = new IdentityKey(structure.getPublicKey().toByteArray(), 0); + this.privateKey = Curve.decodePrivatePoint(structure.getPrivateKey().toByteArray()); + } catch (InvalidProtocolBufferException e) { + throw new InvalidKeyException(e); + } + } + public IdentityKey getPublicKey() { return publicKey; } @@ -40,4 +56,11 @@ public class IdentityKeyPair { public ECPrivateKey getPrivateKey() { return privateKey; } + + public byte[] serialize() { + return IdentityKeyPairStructure.newBuilder() + .setPublicKey(ByteString.copyFrom(publicKey.serialize())) + .setPrivateKey(ByteString.copyFrom(privateKey.serialize())) + .build().toByteArray(); + } } diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java index d09c3c9021..4db4a72c02 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/state/StorageProtos.java @@ -5380,6 +5380,407 @@ public final class StorageProtos { // @@protoc_insertion_point(class_scope:textsecure.PreKeyRecordStructure) } + public interface IdentityKeyPairStructureOrBuilder + extends com.google.protobuf.MessageOrBuilder { + + // optional bytes publicKey = 1; + boolean hasPublicKey(); + com.google.protobuf.ByteString getPublicKey(); + + // optional bytes privateKey = 2; + boolean hasPrivateKey(); + com.google.protobuf.ByteString getPrivateKey(); + } + public static final class IdentityKeyPairStructure extends + com.google.protobuf.GeneratedMessage + implements IdentityKeyPairStructureOrBuilder { + // Use IdentityKeyPairStructure.newBuilder() to construct. + private IdentityKeyPairStructure(Builder builder) { + super(builder); + } + private IdentityKeyPairStructure(boolean noInit) {} + + private static final IdentityKeyPairStructure defaultInstance; + public static IdentityKeyPairStructure getDefaultInstance() { + return defaultInstance; + } + + public IdentityKeyPairStructure getDefaultInstanceForType() { + return defaultInstance; + } + + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable; + } + + private int bitField0_; + // optional bytes publicKey = 1; + public static final int PUBLICKEY_FIELD_NUMBER = 1; + private com.google.protobuf.ByteString publicKey_; + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + + // optional bytes privateKey = 2; + public static final int PRIVATEKEY_FIELD_NUMBER = 2; + private com.google.protobuf.ByteString privateKey_; + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + + private void initFields() { + publicKey_ = com.google.protobuf.ByteString.EMPTY; + privateKey_ = com.google.protobuf.ByteString.EMPTY; + } + private byte memoizedIsInitialized = -1; + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized != -1) return isInitialized == 1; + + memoizedIsInitialized = 1; + return true; + } + + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + getSerializedSize(); + if (((bitField0_ & 0x00000001) == 0x00000001)) { + output.writeBytes(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + output.writeBytes(2, privateKey_); + } + getUnknownFields().writeTo(output); + } + + private int memoizedSerializedSize = -1; + public int getSerializedSize() { + int size = memoizedSerializedSize; + if (size != -1) return size; + + size = 0; + if (((bitField0_ & 0x00000001) == 0x00000001)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(1, publicKey_); + } + if (((bitField0_ & 0x00000002) == 0x00000002)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(2, privateKey_); + } + size += getUnknownFields().getSerializedSize(); + memoizedSerializedSize = size; + return size; + } + + private static final long serialVersionUID = 0L; + @java.lang.Override + protected java.lang.Object writeReplace() + throws java.io.ObjectStreamException { + return super.writeReplace(); + } + + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data).buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return newBuilder().mergeFrom(data, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom(java.io.InputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Builder builder = newBuilder(); + if (builder.mergeDelimitedFrom(input, extensionRegistry)) { + return builder.buildParsed(); + } else { + return null; + } + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return newBuilder().mergeFrom(input).buildParsed(); + } + public static org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return newBuilder().mergeFrom(input, extensionRegistry) + .buildParsed(); + } + + public static Builder newBuilder() { return Builder.create(); } + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder(org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure prototype) { + return newBuilder().mergeFrom(prototype); + } + public Builder toBuilder() { return newBuilder(this); } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessage.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + public static final class Builder extends + com.google.protobuf.GeneratedMessage.Builder + implements org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructureOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_descriptor; + } + + protected com.google.protobuf.GeneratedMessage.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.whispersystems.libaxolotl.state.StorageProtos.internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable; + } + + // Construct using org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) { + } + } + private static Builder create() { + return new Builder(); + } + + public Builder clear() { + super.clear(); + publicKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000001); + privateKey_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000002); + return this; + } + + public Builder clone() { + return create().mergeFrom(buildPartial()); + } + + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.getDescriptor(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure getDefaultInstanceForType() { + return org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance(); + } + + public org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure build() { + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + private org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure buildParsed() + throws com.google.protobuf.InvalidProtocolBufferException { + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException( + result).asInvalidProtocolBufferException(); + } + return result; + } + + public org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure buildPartial() { + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure result = new org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure(this); + int from_bitField0_ = bitField0_; + int to_bitField0_ = 0; + if (((from_bitField0_ & 0x00000001) == 0x00000001)) { + to_bitField0_ |= 0x00000001; + } + result.publicKey_ = publicKey_; + if (((from_bitField0_ & 0x00000002) == 0x00000002)) { + to_bitField0_ |= 0x00000002; + } + result.privateKey_ = privateKey_; + result.bitField0_ = to_bitField0_; + onBuilt(); + return result; + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure) { + return mergeFrom((org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure other) { + if (other == org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.getDefaultInstance()) return this; + if (other.hasPublicKey()) { + setPublicKey(other.getPublicKey()); + } + if (other.hasPrivateKey()) { + setPrivateKey(other.getPrivateKey()); + } + this.mergeUnknownFields(other.getUnknownFields()); + return this; + } + + public final boolean isInitialized() { + return true; + } + + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder( + this.getUnknownFields()); + while (true) { + int tag = input.readTag(); + switch (tag) { + case 0: + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + default: { + if (!parseUnknownField(input, unknownFields, + extensionRegistry, tag)) { + this.setUnknownFields(unknownFields.build()); + onChanged(); + return this; + } + break; + } + case 10: { + bitField0_ |= 0x00000001; + publicKey_ = input.readBytes(); + break; + } + case 18: { + bitField0_ |= 0x00000002; + privateKey_ = input.readBytes(); + break; + } + } + } + } + + private int bitField0_; + + // optional bytes publicKey = 1; + private com.google.protobuf.ByteString publicKey_ = com.google.protobuf.ByteString.EMPTY; + public boolean hasPublicKey() { + return ((bitField0_ & 0x00000001) == 0x00000001); + } + public com.google.protobuf.ByteString getPublicKey() { + return publicKey_; + } + public Builder setPublicKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000001; + publicKey_ = value; + onChanged(); + return this; + } + public Builder clearPublicKey() { + bitField0_ = (bitField0_ & ~0x00000001); + publicKey_ = getDefaultInstance().getPublicKey(); + onChanged(); + return this; + } + + // optional bytes privateKey = 2; + private com.google.protobuf.ByteString privateKey_ = com.google.protobuf.ByteString.EMPTY; + public boolean hasPrivateKey() { + return ((bitField0_ & 0x00000002) == 0x00000002); + } + public com.google.protobuf.ByteString getPrivateKey() { + return privateKey_; + } + public Builder setPrivateKey(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000002; + privateKey_ = value; + onChanged(); + return this; + } + public Builder clearPrivateKey() { + bitField0_ = (bitField0_ & ~0x00000002); + privateKey_ = getDefaultInstance().getPrivateKey(); + onChanged(); + return this; + } + + // @@protoc_insertion_point(builder_scope:textsecure.IdentityKeyPairStructure) + } + + static { + defaultInstance = new IdentityKeyPairStructure(true); + defaultInstance.initFields(); + } + + // @@protoc_insertion_point(class_scope:textsecure.IdentityKeyPairStructure) + } + private static com.google.protobuf.Descriptors.Descriptor internal_static_textsecure_SessionStructure_descriptor; private static @@ -5420,6 +5821,11 @@ public final class StorageProtos { private static com.google.protobuf.GeneratedMessage.FieldAccessorTable internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable; + private static com.google.protobuf.Descriptors.Descriptor + internal_static_textsecure_IdentityKeyPairStructure_descriptor; + private static + com.google.protobuf.GeneratedMessage.FieldAccessorTable + internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable; public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { @@ -5461,8 +5867,10 @@ public final class StorageProtos { "ructure\0226\n\020previousSessions\030\002 \003(\0132\034.text", "secure.SessionStructure\"J\n\025PreKeyRecordS" + "tructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicKey\030\002 \001(\014\022" + - "\022\n\nprivateKey\030\003 \001(\014B4\n#org.whispersystem" + - "s.libaxolotl.stateB\rStorageProtos" + "\022\n\nprivateKey\030\003 \001(\014\"A\n\030IdentityKeyPairSt" + + "ructure\022\021\n\tpublicKey\030\001 \001(\014\022\022\n\nprivateKey" + + "\030\002 \001(\014B4\n#org.whispersystems.libaxolotl." + + "stateB\rStorageProtos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -5533,6 +5941,14 @@ public final class StorageProtos { new java.lang.String[] { "Id", "PublicKey", "PrivateKey", }, org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.class, org.whispersystems.libaxolotl.state.StorageProtos.PreKeyRecordStructure.Builder.class); + internal_static_textsecure_IdentityKeyPairStructure_descriptor = + getDescriptor().getMessageTypes().get(3); + internal_static_textsecure_IdentityKeyPairStructure_fieldAccessorTable = new + com.google.protobuf.GeneratedMessage.FieldAccessorTable( + internal_static_textsecure_IdentityKeyPairStructure_descriptor, + new java.lang.String[] { "PublicKey", "PrivateKey", }, + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.class, + org.whispersystems.libaxolotl.state.StorageProtos.IdentityKeyPairStructure.Builder.class); return null; } }; diff --git a/library/src/org/whispersystems/textsecure/storage/TextSecureSessionStore.java b/library/src/org/whispersystems/textsecure/storage/TextSecureSessionStore.java index c9ad6f7b0f..ac170312a3 100644 --- a/library/src/org/whispersystems/textsecure/storage/TextSecureSessionStore.java +++ b/library/src/org/whispersystems/textsecure/storage/TextSecureSessionStore.java @@ -75,9 +75,9 @@ public class TextSecureSessionStore implements SessionStore { @Override public void store(long recipientId, int deviceId, SessionRecord record) { try { - MasterCipher masterCipher = new MasterCipher(masterSecret); - RandomAccessFile sessionFile = new RandomAccessFile(getSessionFile(recipientId, deviceId), "rw"); - FileChannel out = sessionFile.getChannel(); + MasterCipher masterCipher = new MasterCipher(masterSecret); + RandomAccessFile sessionFile = new RandomAccessFile(getSessionFile(recipientId, deviceId), "rw"); + FileChannel out = sessionFile.getChannel(); out.position(0); writeInteger(CURRENT_VERSION, out);