From 64b40df15bb545d3fd374108ff1880e39ffdbd1f Mon Sep 17 00:00:00 2001 From: Moxie Marlinspike Date: Tue, 8 Jul 2014 19:54:07 -0700 Subject: [PATCH] Add V3 support for KeyExchangeMessage case. 1) V3 KeyExchangeMessages can now contain signatures and verification tags. --- libaxolotl/protobuf/WhisperTextProtocol.proto | 10 +- .../test/SessionBuilderTest.java | 15 +- .../libaxolotl/SessionBuilder.java | 137 ++++++------- .../protocol/KeyExchangeMessage.java | 68 +++++-- .../libaxolotl/protocol/WhisperProtos.java | 181 +++++++++++++++++- 5 files changed, 310 insertions(+), 101 deletions(-) diff --git a/libaxolotl/protobuf/WhisperTextProtocol.proto b/libaxolotl/protobuf/WhisperTextProtocol.proto index cc278ea748..68435211a7 100644 --- a/libaxolotl/protobuf/WhisperTextProtocol.proto +++ b/libaxolotl/protobuf/WhisperTextProtocol.proto @@ -21,8 +21,10 @@ message PreKeyWhisperMessage { } message KeyExchangeMessage { - optional uint32 id = 1; - optional bytes baseKey = 2; - optional bytes ephemeralKey = 3; - optional bytes identityKey = 4; + optional uint32 id = 1; + optional bytes baseKey = 2; + optional bytes ephemeralKey = 3; + optional bytes identityKey = 4; + optional bytes baseKeySignature = 5; + optional bytes verification = 6; } \ No newline at end of file diff --git a/libaxolotl/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java b/libaxolotl/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java index cb00031667..ac5de16646 100644 --- a/libaxolotl/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java +++ b/libaxolotl/src/androidTest/java/org/whispersystems/test/SessionBuilderTest.java @@ -501,7 +501,7 @@ public class SessionBuilderTest extends AndroidTestCase { } - public void testBasicKeyExchange() throws InvalidKeyException, LegacyMessageException, InvalidMessageException, DuplicateMessageException, UntrustedIdentityException, StaleKeyExchangeException { + public void testBasicKeyExchange() throws InvalidKeyException, LegacyMessageException, InvalidMessageException, DuplicateMessageException, UntrustedIdentityException, StaleKeyExchangeException, InvalidVersionException { SessionStore aliceSessionStore = new InMemorySessionStore(); PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); DeviceKeyStore aliceDeviceKeyStore = new InMemoryDeviceKeyStore(); @@ -520,13 +520,16 @@ public class SessionBuilderTest extends AndroidTestCase { bobIdentityKeyStore, ALICE_RECIPIENT_ID, 1); - KeyExchangeMessage aliceKeyExchangeMessage = aliceSessionBuilder.process(); - KeyExchangeMessage bobKeyExchangeMessage = bobSessionBuilder.process(aliceKeyExchangeMessage); - - assertTrue(bobKeyExchangeMessage != null); + KeyExchangeMessage aliceKeyExchangeMessage = aliceSessionBuilder.process(); assertTrue(aliceKeyExchangeMessage != null); - KeyExchangeMessage response = aliceSessionBuilder.process(bobKeyExchangeMessage); + byte[] aliceKeyExchangeMessageBytes = aliceKeyExchangeMessage.serialize(); + KeyExchangeMessage bobKeyExchangeMessage = bobSessionBuilder.process(new KeyExchangeMessage(aliceKeyExchangeMessageBytes)); + + assertTrue(bobKeyExchangeMessage != null); + + byte[] bobKeyExchangeMessageBytes = bobKeyExchangeMessage.serialize(); + KeyExchangeMessage response = aliceSessionBuilder.process(new KeyExchangeMessage(bobKeyExchangeMessageBytes)); assertTrue(response == null); assertTrue(aliceSessionStore.containsSession(BOB_RECIPIENT_ID, 1)); diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java index e0de65519e..9241a42758 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/SessionBuilder.java @@ -5,10 +5,10 @@ import android.util.Log; import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.ECKeyPair; import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.protocol.CiphertextMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.ratchet.RatchetingSession; -import org.whispersystems.libaxolotl.state.DeviceKeyRecord; import org.whispersystems.libaxolotl.state.DeviceKeyStore; import org.whispersystems.libaxolotl.state.IdentityKeyStore; import org.whispersystems.libaxolotl.state.PreKeyBundle; @@ -149,7 +149,6 @@ public class SessionBuilder { sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); sessionRecord.getSessionState().setAliceBaseKey(theirBaseKey.serialize()); - sessionRecord.getSessionState().setSessionVersion(message.getMessageVersion()); if (simultaneousInitiate) sessionRecord.getSessionState().setNeedsRefresh(true); @@ -197,7 +196,6 @@ public class SessionBuilder { sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId()); sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId()); - sessionRecord.getSessionState().setSessionVersion(message.getMessageVersion()); if (simultaneousInitiate) sessionRecord.getSessionState().setNeedsRefresh(true); @@ -277,31 +275,71 @@ public class SessionBuilder { } KeyExchangeMessage responseMessage = null; - SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); - Log.w(TAG, "Received key exchange with sequence: " + message.getSequence()); + if (message.isInitiate()) responseMessage = processInitiate(message); + else processResponse(message); - if (message.isInitiate()) { - Log.w(TAG, "KeyExchange is an initiate."); - responseMessage = processInitiate(sessionRecord, message); + return responseMessage; + } + + private KeyExchangeMessage processInitiate(KeyExchangeMessage message) throws InvalidKeyException { + ECKeyPair ourBaseKey, ourEphemeralKey; + IdentityKeyPair ourIdentityKey; + + int flags = KeyExchangeMessage.RESPONSE_FLAG; + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + + if (message.getVersion() >= 3 && + !Curve.verifySignature(message.getIdentityKey().getPublicKey(), + message.getBaseKey().serialize(), + message.getBaseKeySignature())) + { + throw new InvalidKeyException("Bad signature!"); } - if (message.isResponse()) { - SessionState sessionState = sessionRecord.getSessionState(); - boolean hasPendingKeyExchange = sessionState.hasPendingKeyExchange(); - boolean isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate(); - - if ((!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence()) && - !isSimultaneousInitiateResponse) - { - throw new StaleKeyExchangeException(); - } + if (!sessionRecord.getSessionState().hasPendingKeyExchange()) { + Log.w(TAG, "We don't have a pending initiate..."); + ourBaseKey = Curve.generateKeyPair(true); + ourEphemeralKey = Curve.generateKeyPair(true); + ourIdentityKey = identityKeyStore.getIdentityKeyPair(); + } else { + Log.w(TAG, "We already have a pending initiate, responding as simultaneous initiate..."); + ourBaseKey = sessionRecord.getSessionState().getPendingKeyExchangeBaseKey(); + ourEphemeralKey = sessionRecord.getSessionState().getPendingKeyExchangeEphemeralKey(); + ourIdentityKey = sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey(); + flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG; } - if (message.getSequence() != sessionRecord.getSessionState().getPendingKeyExchangeSequence()) { - Log.w("KeyExchangeProcessor", "No matching sequence for response. " + - "Is simultaneous initiate response: " + message.isResponseForSimultaneousInitiate()); - return responseMessage; + sessionRecord.reset(); + + RatchetingSession.initializeSession(sessionRecord.getSessionState(), + Math.min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION), + ourBaseKey, message.getBaseKey(), + ourEphemeralKey, message.getEphemeralKey(), + ourBaseKey, message.getBaseKey(), + ourIdentityKey, message.getIdentityKey()); + + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + identityKeyStore.saveIdentity(recipientId, message.getIdentityKey()); + + return new KeyExchangeMessage(sessionRecord.getSessionState().getSessionVersion(), + message.getSequence(), flags, ourBaseKey.getPublicKey(), null, + ourEphemeralKey.getPublicKey(), ourIdentityKey.getPublicKey(), + sessionRecord.getSessionState().getVerification()); + } + + private void processResponse(KeyExchangeMessage message) + throws StaleKeyExchangeException, InvalidKeyException + { + SessionRecord sessionRecord = sessionStore.loadSession(recipientId, deviceId); + SessionState sessionState = sessionRecord.getSessionState(); + boolean hasPendingKeyExchange = sessionState.hasPendingKeyExchange(); + boolean isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate(); + + if (!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence()) { + Log.w(TAG, "No matching sequence for response. Is simultaneous initiate response: " + isSimultaneousInitiateResponse); + if (!isSimultaneousInitiateResponse) throw new StaleKeyExchangeException(); + else return; } ECKeyPair ourBaseKey = sessionRecord.getSessionState().getPendingKeyExchangeBaseKey(); @@ -311,51 +349,22 @@ public class SessionBuilder { sessionRecord.reset(); RatchetingSession.initializeSession(sessionRecord.getSessionState(), - 2, + Math.min(message.getMaxVersion(), CiphertextMessage.CURRENT_VERSION), ourBaseKey, message.getBaseKey(), ourEphemeralKey, message.getEphemeralKey(), - null, null, + ourBaseKey, message.getBaseKey(), ourIdentityKey, message.getIdentityKey()); - sessionRecord.getSessionState().setSessionVersion(message.getVersion()); - sessionStore.storeSession(recipientId, deviceId, sessionRecord); - - identityKeyStore.saveIdentity(recipientId, message.getIdentityKey()); - - return responseMessage; - } - - private KeyExchangeMessage processInitiate(SessionRecord sessionRecord, KeyExchangeMessage message) - throws InvalidKeyException - { - ECKeyPair ourBaseKey, ourEphemeralKey; - IdentityKeyPair ourIdentityKey; - - int flags = KeyExchangeMessage.RESPONSE_FLAG; - - if (!sessionRecord.getSessionState().hasPendingKeyExchange()) { - Log.w(TAG, "We don't have a pending initiate..."); - ourBaseKey = Curve.generateKeyPair(true); - ourEphemeralKey = Curve.generateKeyPair(true); - ourIdentityKey = identityKeyStore.getIdentityKeyPair(); - - sessionRecord.getSessionState().setPendingKeyExchange(message.getSequence(), ourBaseKey, - ourEphemeralKey, ourIdentityKey); - } else { - Log.w(TAG, "We already have a pending initiate, responding as simultaneous initiate..."); - ourBaseKey = sessionRecord.getSessionState().getPendingKeyExchangeBaseKey(); - ourEphemeralKey = sessionRecord.getSessionState().getPendingKeyExchangeEphemeralKey(); - ourIdentityKey = sessionRecord.getSessionState().getPendingKeyExchangeIdentityKey(); - flags |= KeyExchangeMessage.SIMULTAENOUS_INITIATE_FLAG; - - sessionRecord.getSessionState().setPendingKeyExchange(message.getSequence(), ourBaseKey, - ourEphemeralKey, ourIdentityKey); + if (sessionRecord.getSessionState().getSessionVersion() >= 3 && + !MessageDigest.isEqual(message.getVerificationTag(), + sessionRecord.getSessionState().getVerification())) + { + throw new InvalidKeyException("Verification tag doesn't match!"); } - return new KeyExchangeMessage(message.getSequence(), - flags, ourBaseKey.getPublicKey(), - ourEphemeralKey.getPublicKey(), - ourIdentityKey.getPublicKey()); + sessionStore.storeSession(recipientId, deviceId, sessionRecord); + identityKeyStore.saveIdentity(recipientId, message.getIdentityKey()); + } /** @@ -374,10 +383,10 @@ public class SessionBuilder { sessionRecord.getSessionState().setPendingKeyExchange(sequence, baseKey, ephemeralKey, identityKey); sessionStore.storeSession(recipientId, deviceId, sessionRecord); - return new KeyExchangeMessage(sequence, flags, - baseKey.getPublicKey(), + return new KeyExchangeMessage(2, sequence, flags, + baseKey.getPublicKey(), null, ephemeralKey.getPublicKey(), - identityKey.getPublicKey()); + identityKey.getPublicKey(), null); } diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java index 561664fccb..97babda5f3 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/KeyExchangeMessage.java @@ -14,6 +14,8 @@ import org.whispersystems.libaxolotl.util.ByteUtil; import java.io.IOException; +import static org.whispersystems.libaxolotl.protocol.WhisperProtos.KeyExchangeMessage.Builder; + public class KeyExchangeMessage { public static final int INITIATE_FLAG = 0x01; @@ -26,31 +28,43 @@ public class KeyExchangeMessage { private final int flags; private final ECPublicKey baseKey; + private final byte[] baseKeySignature; private final ECPublicKey ephemeralKey; private final IdentityKey identityKey; + private final byte[] verificationTag; private final byte[] serialized; - public KeyExchangeMessage(int sequence, int flags, - ECPublicKey baseKey, ECPublicKey ephemeralKey, - IdentityKey identityKey) + public KeyExchangeMessage(int messageVersion, int sequence, int flags, + ECPublicKey baseKey, byte[] baseKeySignature, + ECPublicKey ephemeralKey, + IdentityKey identityKey, byte[] verificationTag) { - this.supportedVersion = 2; - this.version = 2; + this.supportedVersion = CiphertextMessage.CURRENT_VERSION; + this.version = messageVersion; this.sequence = sequence; this.flags = flags; this.baseKey = baseKey; + this.baseKeySignature = baseKeySignature; this.ephemeralKey = ephemeralKey; this.identityKey = identityKey; + this.verificationTag = verificationTag; - byte[] version = {ByteUtil.intsToByteHighAndLow(this.version, this.supportedVersion)}; - byte[] message = WhisperProtos.KeyExchangeMessage.newBuilder() - .setId((sequence << 5) | flags) - .setBaseKey(ByteString.copyFrom(baseKey.serialize())) - .setEphemeralKey(ByteString.copyFrom(ephemeralKey.serialize())) - .setIdentityKey(ByteString.copyFrom(identityKey.serialize())) - .build().toByteArray(); + byte[] version = {ByteUtil.intsToByteHighAndLow(this.version, this.supportedVersion)}; + Builder builder = WhisperProtos.KeyExchangeMessage.newBuilder() + .setId((sequence << 5) | flags) + .setBaseKey(ByteString.copyFrom(baseKey.serialize())) + .setEphemeralKey(ByteString.copyFrom(ephemeralKey.serialize())) + .setIdentityKey(ByteString.copyFrom(identityKey.serialize())); - this.serialized = ByteUtil.combine(version, message); + if (messageVersion >= 3 && baseKeySignature != null) { + builder.setBaseKeySignature(ByteString.copyFrom(baseKeySignature)); + } + + if (messageVersion >=3 && verificationTag != null) { + builder.setVerification(ByteString.copyFrom(verificationTag)); + } + + this.serialized = ByteUtil.combine(version, builder.build().toByteArray()); } public KeyExchangeMessage(byte[] serialized) @@ -71,18 +85,22 @@ public class KeyExchangeMessage { WhisperProtos.KeyExchangeMessage message = WhisperProtos.KeyExchangeMessage.parseFrom(parts[1]); - if (!message.hasId() || !message.hasBaseKey() || - !message.hasEphemeralKey() || !message.hasIdentityKey()) + if (!message.hasId() || !message.hasBaseKey() || + !message.hasEphemeralKey() || !message.hasIdentityKey() || + (this.version >=3 && (((message.getId() & 0x1f) & INITIATE_FLAG) != 0) && !message.hasBaseKeySignature()) || + (this.version >=3 && (((message.getId() & 0x1f) & RESPONSE_FLAG) != 0) && !message.hasVerification())) { throw new InvalidMessageException("Some required fields missing!"); } - this.sequence = message.getId() >> 5; - this.flags = message.getId() & 0x1f; - this.serialized = serialized; - this.baseKey = Curve.decodePoint(message.getBaseKey().toByteArray(), 0); - this.ephemeralKey = Curve.decodePoint(message.getEphemeralKey().toByteArray(), 0); - this.identityKey = new IdentityKey(message.getIdentityKey().toByteArray(), 0); + this.sequence = message.getId() >> 5; + this.flags = message.getId() & 0x1f; + this.serialized = serialized; + this.baseKey = Curve.decodePoint(message.getBaseKey().toByteArray(), 0); + this.baseKeySignature = message.getBaseKeySignature().toByteArray(); + this.verificationTag = message.getVerification().toByteArray(); + this.ephemeralKey = Curve.decodePoint(message.getEphemeralKey().toByteArray(), 0); + this.identityKey = new IdentityKey(message.getIdentityKey().toByteArray(), 0); } catch (InvalidKeyException | IOException e) { throw new InvalidMessageException(e); } @@ -96,6 +114,14 @@ public class KeyExchangeMessage { return baseKey; } + public byte[] getBaseKeySignature() { + return baseKeySignature; + } + + public byte[] getVerificationTag() { + return verificationTag; + } + public ECPublicKey getEphemeralKey() { return ephemeralKey; } diff --git a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java index 6765827afc..355fc2b773 100644 --- a/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java +++ b/libaxolotl/src/main/java/org/whispersystems/libaxolotl/protocol/WhisperProtos.java @@ -1621,6 +1621,26 @@ public final class WhisperProtos { * optional bytes identityKey = 4; */ com.google.protobuf.ByteString getIdentityKey(); + + // optional bytes baseKeySignature = 5; + /** + * optional bytes baseKeySignature = 5; + */ + boolean hasBaseKeySignature(); + /** + * optional bytes baseKeySignature = 5; + */ + com.google.protobuf.ByteString getBaseKeySignature(); + + // optional bytes verification = 6; + /** + * optional bytes verification = 6; + */ + boolean hasVerification(); + /** + * optional bytes verification = 6; + */ + com.google.protobuf.ByteString getVerification(); } /** * Protobuf type {@code textsecure.KeyExchangeMessage} @@ -1693,6 +1713,16 @@ public final class WhisperProtos { identityKey_ = input.readBytes(); break; } + case 42: { + bitField0_ |= 0x00000010; + baseKeySignature_ = input.readBytes(); + break; + } + case 50: { + bitField0_ |= 0x00000020; + verification_ = input.readBytes(); + break; + } } } } catch (com.google.protobuf.InvalidProtocolBufferException e) { @@ -1797,11 +1827,45 @@ public final class WhisperProtos { return identityKey_; } + // optional bytes baseKeySignature = 5; + public static final int BASEKEYSIGNATURE_FIELD_NUMBER = 5; + private com.google.protobuf.ByteString baseKeySignature_; + /** + * optional bytes baseKeySignature = 5; + */ + public boolean hasBaseKeySignature() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes baseKeySignature = 5; + */ + public com.google.protobuf.ByteString getBaseKeySignature() { + return baseKeySignature_; + } + + // optional bytes verification = 6; + public static final int VERIFICATION_FIELD_NUMBER = 6; + private com.google.protobuf.ByteString verification_; + /** + * optional bytes verification = 6; + */ + public boolean hasVerification() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes verification = 6; + */ + public com.google.protobuf.ByteString getVerification() { + return verification_; + } + private void initFields() { id_ = 0; baseKey_ = com.google.protobuf.ByteString.EMPTY; ephemeralKey_ = com.google.protobuf.ByteString.EMPTY; identityKey_ = com.google.protobuf.ByteString.EMPTY; + baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + verification_ = com.google.protobuf.ByteString.EMPTY; } private byte memoizedIsInitialized = -1; public final boolean isInitialized() { @@ -1827,6 +1891,12 @@ public final class WhisperProtos { if (((bitField0_ & 0x00000008) == 0x00000008)) { output.writeBytes(4, identityKey_); } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + output.writeBytes(5, baseKeySignature_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + output.writeBytes(6, verification_); + } getUnknownFields().writeTo(output); } @@ -1852,6 +1922,14 @@ public final class WhisperProtos { size += com.google.protobuf.CodedOutputStream .computeBytesSize(4, identityKey_); } + if (((bitField0_ & 0x00000010) == 0x00000010)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(5, baseKeySignature_); + } + if (((bitField0_ & 0x00000020) == 0x00000020)) { + size += com.google.protobuf.CodedOutputStream + .computeBytesSize(6, verification_); + } size += getUnknownFields().getSerializedSize(); memoizedSerializedSize = size; return size; @@ -1976,6 +2054,10 @@ public final class WhisperProtos { bitField0_ = (bitField0_ & ~0x00000004); identityKey_ = com.google.protobuf.ByteString.EMPTY; bitField0_ = (bitField0_ & ~0x00000008); + baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000010); + verification_ = com.google.protobuf.ByteString.EMPTY; + bitField0_ = (bitField0_ & ~0x00000020); return this; } @@ -2020,6 +2102,14 @@ public final class WhisperProtos { to_bitField0_ |= 0x00000008; } result.identityKey_ = identityKey_; + if (((from_bitField0_ & 0x00000010) == 0x00000010)) { + to_bitField0_ |= 0x00000010; + } + result.baseKeySignature_ = baseKeySignature_; + if (((from_bitField0_ & 0x00000020) == 0x00000020)) { + to_bitField0_ |= 0x00000020; + } + result.verification_ = verification_; result.bitField0_ = to_bitField0_; onBuilt(); return result; @@ -2048,6 +2138,12 @@ public final class WhisperProtos { if (other.hasIdentityKey()) { setIdentityKey(other.getIdentityKey()); } + if (other.hasBaseKeySignature()) { + setBaseKeySignature(other.getBaseKeySignature()); + } + if (other.hasVerification()) { + setVerification(other.getVerification()); + } this.mergeUnknownFields(other.getUnknownFields()); return this; } @@ -2216,6 +2312,78 @@ public final class WhisperProtos { return this; } + // optional bytes baseKeySignature = 5; + private com.google.protobuf.ByteString baseKeySignature_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes baseKeySignature = 5; + */ + public boolean hasBaseKeySignature() { + return ((bitField0_ & 0x00000010) == 0x00000010); + } + /** + * optional bytes baseKeySignature = 5; + */ + public com.google.protobuf.ByteString getBaseKeySignature() { + return baseKeySignature_; + } + /** + * optional bytes baseKeySignature = 5; + */ + public Builder setBaseKeySignature(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000010; + baseKeySignature_ = value; + onChanged(); + return this; + } + /** + * optional bytes baseKeySignature = 5; + */ + public Builder clearBaseKeySignature() { + bitField0_ = (bitField0_ & ~0x00000010); + baseKeySignature_ = getDefaultInstance().getBaseKeySignature(); + onChanged(); + return this; + } + + // optional bytes verification = 6; + private com.google.protobuf.ByteString verification_ = com.google.protobuf.ByteString.EMPTY; + /** + * optional bytes verification = 6; + */ + public boolean hasVerification() { + return ((bitField0_ & 0x00000020) == 0x00000020); + } + /** + * optional bytes verification = 6; + */ + public com.google.protobuf.ByteString getVerification() { + return verification_; + } + /** + * optional bytes verification = 6; + */ + public Builder setVerification(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + bitField0_ |= 0x00000020; + verification_ = value; + onChanged(); + return this; + } + /** + * optional bytes verification = 6; + */ + public Builder clearVerification() { + bitField0_ = (bitField0_ & ~0x00000020); + verification_ = getDefaultInstance().getVerification(); + onChanged(); + return this; + } + // @@protoc_insertion_point(builder_scope:textsecure.KeyExchangeMessage) } @@ -2258,11 +2426,12 @@ public final class WhisperProtos { "essage\022\026\n\016registrationId\030\005 \001(\r\022\020\n\010preKey" + "Id\030\001 \001(\r\022\023\n\013deviceKeyId\030\006 \001(\r\022\017\n\007baseKey" + "\030\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014\022\024\n\014verificat" + - "ion\030\007 \001(\014\022\017\n\007message\030\004 \001(\014\"\\\n\022KeyExchang" + - "eMessage\022\n\n\002id\030\001 \001(\r\022\017\n\007baseKey\030\002 \001(\014\022\024\n" + - "\014ephemeralKey\030\003 \001(\014\022\023\n\013identityKey\030\004 \001(\014", - "B7\n&org.whispersystems.libaxolotl.protoc" + - "olB\rWhisperProtos" + "ion\030\007 \001(\014\022\017\n\007message\030\004 \001(\014\"\214\001\n\022KeyExchan" + + "geMessage\022\n\n\002id\030\001 \001(\r\022\017\n\007baseKey\030\002 \001(\014\022\024" + + "\n\014ephemeralKey\030\003 \001(\014\022\023\n\013identityKey\030\004 \001(", + "\014\022\030\n\020baseKeySignature\030\005 \001(\014\022\024\n\014verificat" + + "ion\030\006 \001(\014B7\n&org.whispersystems.libaxolo" + + "tl.protocolB\rWhisperProtos" }; com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { @@ -2286,7 +2455,7 @@ public final class WhisperProtos { internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable = new com.google.protobuf.GeneratedMessage.FieldAccessorTable( internal_static_textsecure_KeyExchangeMessage_descriptor, - new java.lang.String[] { "Id", "BaseKey", "EphemeralKey", "IdentityKey", }); + new java.lang.String[] { "Id", "BaseKey", "EphemeralKey", "IdentityKey", "BaseKeySignature", "Verification", }); return null; } };