Remove verification tag.

1) Remove verification tag from PreKeyWhisperMessage.

2) Include sender and recipient identity keys in the MAC of
   each WhisperMessage.
This commit is contained in:
Moxie Marlinspike 2014-07-23 21:46:19 -07:00
parent 641ac9aed9
commit 5ea3b3038e
13 changed files with 79 additions and 555 deletions

View File

@ -58,7 +58,6 @@ message SessionStructure {
optional bool needsRefresh = 12;
optional bytes aliceBaseKey = 13;
optional bytes verification = 14;
}
message RecordStructure {

View File

@ -16,7 +16,6 @@ message PreKeyWhisperMessage {
optional uint32 signedPreKeyId = 6;
optional bytes baseKey = 2;
optional bytes identityKey = 3;
optional bytes verification = 7;
optional bytes message = 4; // WhisperMessage
}

View File

@ -406,82 +406,6 @@ public class SessionBuilderTest extends AndroidTestCase {
}
public void testBadVerificationTagV3() throws InvalidKeyException, UntrustedIdentityException, InvalidVersionException, InvalidMessageException, InvalidKeyIdException, DuplicateMessageException, LegacyMessageException, NoSessionException {
SessionStore aliceSessionStore = new InMemorySessionStore();
SignedPreKeyStore aliceSignedPreKeyStore = new InMemorySignedPreKeyStore();
PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore();
IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore();
SessionBuilder aliceSessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore,
aliceSignedPreKeyStore,
aliceIdentityKeyStore,
BOB_RECIPIENT_ID, 1);
SessionStore bobSessionStore = new InMemorySessionStore();
PreKeyStore bobPreKeyStore = new InMemoryPreKeyStore();
SignedPreKeyStore bobSignedPreKeyStore = new InMemorySignedPreKeyStore();
IdentityKeyStore bobIdentityKeyStore = new InMemoryIdentityKeyStore();
ECKeyPair bobPreKeyPair = Curve.generateKeyPair(true);
ECKeyPair bobSignedPreKeyPair = Curve.generateKeyPair(true);
byte[] bobSignedPreKeySignature = Curve.calculateSignature(bobIdentityKeyStore.getIdentityKeyPair().getPrivateKey(),
bobSignedPreKeyPair.getPublicKey().serialize());
PreKeyBundle bobPreKey = new PreKeyBundle(bobIdentityKeyStore.getLocalRegistrationId(), 1,
31337, bobPreKeyPair.getPublicKey(),
22, bobSignedPreKeyPair.getPublicKey(), bobSignedPreKeySignature,
bobIdentityKeyStore.getIdentityKeyPair().getPublicKey());
bobPreKeyStore.storePreKey(31337, new PreKeyRecord(bobPreKey.getPreKeyId(), bobPreKeyPair));
bobSignedPreKeyStore.storeSignedPreKey(22, new SignedPreKeyRecord(22, System.currentTimeMillis(), bobSignedPreKeyPair, bobSignedPreKeySignature));
aliceSessionBuilder.process(bobPreKey);
String originalMessage = "L'homme est condamné à être libre";
SessionCipher aliceSessionCipher = new SessionCipher(aliceSessionStore, alicePreKeyStore, aliceSignedPreKeyStore, aliceIdentityKeyStore, BOB_RECIPIENT_ID, 1);
CiphertextMessage outgoingMessageOne = aliceSessionCipher.encrypt(originalMessage.getBytes());
assertTrue(outgoingMessageOne.getType() == CiphertextMessage.PREKEY_TYPE);
PreKeyWhisperMessage incomingMessage = new PreKeyWhisperMessage(outgoingMessageOne.serialize());
SessionCipher bobSessionCipher = new SessionCipher(bobSessionStore, bobPreKeyStore, bobSignedPreKeyStore, bobIdentityKeyStore, ALICE_RECIPIENT_ID, 1);
for (int i=0;i<incomingMessage.getVerification().length * 8;i++) {
byte[] modifiedVerification = new byte[incomingMessage.getVerification().length];
System.arraycopy(incomingMessage.getVerification(), 0, modifiedVerification, 0, modifiedVerification.length);
modifiedVerification[i / 8] ^= (0x01 << i % 8);
PreKeyWhisperMessage modifiedMessage = new PreKeyWhisperMessage(incomingMessage.getMessageVersion(),
incomingMessage.getRegistrationId(),
incomingMessage.getPreKeyId(),
incomingMessage.getSignedPreKeyId(),
incomingMessage.getBaseKey(),
incomingMessage.getIdentityKey(),
modifiedVerification,
incomingMessage.getWhisperMessage());
try {
bobSessionCipher.decrypt(modifiedMessage);
throw new AssertionError("Modified verification tag passed!");
} catch (InvalidKeyException e) {
// good
}
}
PreKeyWhisperMessage unmodifiedMessage = new PreKeyWhisperMessage(incomingMessage.getMessageVersion(),
incomingMessage.getRegistrationId(),
incomingMessage.getPreKeyId(),
incomingMessage.getSignedPreKeyId(),
incomingMessage.getBaseKey(),
incomingMessage.getIdentityKey(),
incomingMessage.getVerification(),
incomingMessage.getWhisperMessage());
bobSessionCipher.decrypt(unmodifiedMessage);
}
public void testBasicKeyExchange() throws InvalidKeyException, LegacyMessageException, InvalidMessageException, DuplicateMessageException, UntrustedIdentityException, StaleKeyExchangeException, InvalidVersionException, NoSessionException {
SessionStore aliceSessionStore = new InMemorySessionStore();
PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore();

View File

@ -1,90 +0,0 @@
package org.whispersystems.test.ratchet;
import android.test.AndroidTestCase;
import android.util.Log;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.ratchet.VerifyKey;
import org.whispersystems.libaxolotl.util.Hex;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class VerifyKeyTest extends AndroidTestCase {
public void testVerify() throws NoSuchAlgorithmException, InvalidKeyException {
byte[] aliceBaseKeyBytes = {(byte) 0x05, (byte) 0x2d, (byte) 0x0c, (byte) 0xdd, (byte) 0xde,
(byte) 0xa8, (byte) 0x9f, (byte) 0x6a, (byte) 0x2c, (byte) 0xe0,
(byte) 0x21, (byte) 0xfa, (byte) 0x69, (byte) 0x39, (byte) 0x30,
(byte) 0x43, (byte) 0x28, (byte) 0xd0, (byte) 0xa3, (byte) 0x53,
(byte) 0xe0, (byte) 0x67, (byte) 0xb9, (byte) 0x11, (byte) 0xf5,
(byte) 0xa9, (byte) 0xbd, (byte) 0xa4, (byte) 0x7b, (byte) 0x29,
(byte) 0x41, (byte) 0x6e, (byte) 0x2b};
byte[] aliceIdentityKeyBytes = {(byte) 0x05, (byte) 0x9d, (byte) 0x86, (byte) 0xef, (byte) 0x77,
(byte) 0x7d, (byte) 0x71, (byte) 0x0c, (byte) 0xc2, (byte) 0xb1,
(byte) 0x4e, (byte) 0xd6, (byte) 0x15, (byte) 0x2e, (byte) 0x91,
(byte) 0xfb, (byte) 0x7f, (byte) 0xa2, (byte) 0x34, (byte) 0xe5,
(byte) 0x5b, (byte) 0x57, (byte) 0x2e, (byte) 0x52, (byte) 0xb8,
(byte) 0x5f, (byte) 0x84, (byte) 0xdb, (byte) 0x34, (byte) 0x16,
(byte) 0x69, (byte) 0xfd, (byte) 0x45};
byte[] bobBaseKeyBytes = {(byte) 0x05, (byte) 0xc0, (byte) 0xbd, (byte) 0x26, (byte) 0x62,
(byte) 0xf7, (byte) 0xea, (byte) 0xa8, (byte) 0x5a, (byte) 0x5e,
(byte) 0x43, (byte) 0x95, (byte) 0x34, (byte) 0x3a, (byte) 0xcf,
(byte) 0x66, (byte) 0x36, (byte) 0xec, (byte) 0x75, (byte) 0x54,
(byte) 0x7b, (byte) 0x96, (byte) 0x02, (byte) 0x6d, (byte) 0x8a,
(byte) 0x16, (byte) 0xb6, (byte) 0x39, (byte) 0x10, (byte) 0x36,
(byte) 0xf6, (byte) 0x9f, (byte) 0x39};
byte[] bobPreKeyBytes = {(byte) 0x05, (byte) 0xb8, (byte) 0x28, (byte) 0x04, (byte) 0xe6,
(byte) 0x46, (byte) 0xeb, (byte) 0x04, (byte) 0xaf, (byte) 0x54,
(byte) 0xeb, (byte) 0xea, (byte) 0xfa, (byte) 0x8e, (byte) 0x27,
(byte) 0xb1, (byte) 0xa7, (byte) 0xa8, (byte) 0x00, (byte) 0xef,
(byte) 0xcf, (byte) 0xd7, (byte) 0xe8, (byte) 0x9c, (byte) 0x92,
(byte) 0xfc, (byte) 0x51, (byte) 0x66, (byte) 0xb8, (byte) 0x70,
(byte) 0xee, (byte) 0x63, (byte) 0x74};
byte[] bobIdentityKeyBytes = {(byte) 0x05, (byte) 0x3a, (byte) 0x32, (byte) 0x3a, (byte) 0xda,
(byte) 0xe8, (byte) 0x46, (byte) 0x1b, (byte) 0x57, (byte) 0x8d,
(byte) 0x46, (byte) 0x70, (byte) 0x80, (byte) 0x0e, (byte) 0x06,
(byte) 0x76, (byte) 0x5a, (byte) 0xf1, (byte) 0x50, (byte) 0x51,
(byte) 0xd3, (byte) 0x74, (byte) 0xa0, (byte) 0x65, (byte) 0x85,
(byte) 0xea, (byte) 0x03, (byte) 0xff, (byte) 0x58, (byte) 0x7c,
(byte) 0x81, (byte) 0xa8, (byte) 0x04};
byte[] key = {(byte)0xfc, (byte)0x57, (byte)0x05, (byte)0xdc, (byte)0xe0,
(byte)0x34, (byte)0x4c, (byte)0x8f, (byte)0x1c, (byte)0xeb,
(byte)0x9b, (byte)0x05, (byte)0x7c, (byte)0xaa, (byte)0xb0,
(byte)0x08, (byte)0xf0, (byte)0xb7, (byte)0x26, (byte)0x73,
(byte)0x46, (byte)0xa4, (byte)0x00, (byte)0xa3, (byte)0x66,
(byte)0x79, (byte)0x00, (byte)0xef, (byte)0x1b, (byte)0x40,
(byte)0x0f, (byte)0xdc};
byte[] expectedTag = {(byte)0xd3, (byte)0x62, (byte)0x84, (byte)0x3c,
(byte)0x9d, (byte)0x59, (byte)0x8c, (byte)0x6f};
ECPublicKey aliceBaseKey = Curve.decodePoint(aliceBaseKeyBytes, 0);
ECPublicKey alicePreKey = aliceBaseKey;
ECPublicKey aliceIdentityKey = Curve.decodePoint(aliceIdentityKeyBytes, 0);
ECPublicKey bobBaseKey = Curve.decodePoint(bobBaseKeyBytes, 0);
ECPublicKey bobPreKey = Curve.decodePoint(bobPreKeyBytes, 0);
ECPublicKey bobIdentityKey = Curve.decodePoint(bobIdentityKeyBytes, 0);
VerifyKey verifyKey = new VerifyKey(key);
byte[] verification = verifyKey.generateVerification(new IdentityKey(aliceIdentityKey),
new IdentityKey(bobIdentityKey),
aliceBaseKey, bobBaseKey,
Optional.of(bobPreKey));
assertTrue(MessageDigest.isEqual(verification, expectedTag));
}
}

View File

@ -23,8 +23,6 @@ import org.whispersystems.libaxolotl.util.KeyHelper;
import org.whispersystems.libaxolotl.util.Medium;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.security.MessageDigest;
/**
* SessionBuilder is responsible for setting up encrypted sessions.
* Once a session has been established, {@link org.whispersystems.libaxolotl.SessionCipher}
@ -143,10 +141,6 @@ public class SessionBuilder {
RatchetingSession.initializeSession(sessionRecord.getSessionState(), message.getMessageVersion(), parameters.create());
if (!MessageDigest.isEqual(sessionRecord.getSessionState().getVerification(), message.getVerification())) {
throw new InvalidKeyException("Verification secret mismatch!");
}
sessionRecord.getSessionState().setLocalRegistrationId(identityKeyStore.getLocalRegistrationId());
sessionRecord.getSessionState().setRemoteRegistrationId(message.getRegistrationId());
sessionRecord.getSessionState().setAliceBaseKey(message.getBaseKey().serialize());

View File

@ -105,7 +105,9 @@ public class SessionCipher {
byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage);
CiphertextMessage ciphertextMessage = new WhisperMessage(sessionVersion, messageKeys.getMacKey(),
senderEphemeral, chainKey.getIndex(),
previousCounter, ciphertextBody);
previousCounter, ciphertextBody,
sessionState.getLocalIdentityKey(),
sessionState.getRemoteIdentityKey());
if (sessionState.hasUnacknowledgedPreKeyMessage()) {
UnacknowledgedPreKeyMessageItems items = sessionState.getUnacknowledgedPreKeyMessageItems();
@ -114,7 +116,6 @@ public class SessionCipher {
ciphertextMessage = new PreKeyWhisperMessage(sessionVersion, localRegistrationId, items.getPreKeyId(),
items.getSignedPreKeyId(), items.getBaseKey(),
sessionState.getLocalIdentityKey(),
sessionState.getVerification(),
(WhisperMessage) ciphertextMessage);
}
@ -229,7 +230,10 @@ public class SessionCipher {
MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral,
chainKey, counter);
ciphertextMessage.verifyMac(messageKeys.getMacKey());
ciphertextMessage.verifyMac(ciphertextMessage.getMessageVersion(),
sessionState.getRemoteIdentityKey(),
sessionState.getLocalIdentityKey(),
messageKeys.getMacKey());
byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody());

View File

@ -37,7 +37,6 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
private final int signedPreKeyId;
private final ECPublicKey baseKey;
private final IdentityKey identityKey;
private final byte[] verification;
private final WhisperMessage message;
private final byte[] serialized;
@ -57,7 +56,6 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
if ((version == 2 && !preKeyWhisperMessage.hasPreKeyId()) ||
(version == 3 && !preKeyWhisperMessage.hasSignedPreKeyId()) ||
(version == 3 && !preKeyWhisperMessage.hasVerification()) ||
!preKeyWhisperMessage.hasBaseKey() ||
!preKeyWhisperMessage.hasIdentityKey() ||
!preKeyWhisperMessage.hasMessage())
@ -71,7 +69,6 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
this.signedPreKeyId = preKeyWhisperMessage.hasSignedPreKeyId() ? preKeyWhisperMessage.getSignedPreKeyId() : -1;
this.baseKey = Curve.decodePoint(preKeyWhisperMessage.getBaseKey().toByteArray(), 0);
this.identityKey = new IdentityKey(Curve.decodePoint(preKeyWhisperMessage.getIdentityKey().toByteArray(), 0));
this.verification = preKeyWhisperMessage.getVerification().toByteArray();
this.message = new WhisperMessage(preKeyWhisperMessage.getMessage().toByteArray());
} catch (InvalidProtocolBufferException | InvalidKeyException | LegacyMessageException e) {
throw new InvalidMessageException(e);
@ -79,8 +76,7 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
}
public PreKeyWhisperMessage(int messageVersion, int registrationId, int preKeyId, int signedPreKeyId,
ECPublicKey baseKey, IdentityKey identityKey, byte[] verification,
WhisperMessage message)
ECPublicKey baseKey, IdentityKey identityKey, WhisperMessage message)
{
this.version = messageVersion;
this.registrationId = registrationId;
@ -88,7 +84,6 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
this.signedPreKeyId = signedPreKeyId;
this.baseKey = baseKey;
this.identityKey = identityKey;
this.verification = verification;
this.message = message;
byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(this.version, CURRENT_VERSION)};
@ -97,7 +92,6 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
.setSignedPreKeyId(signedPreKeyId)
.setBaseKey(ByteString.copyFrom(baseKey.serialize()))
.setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
.setVerification(ByteString.copyFrom(verification))
.setMessage(ByteString.copyFrom(message.serialize()))
.setRegistrationId(registrationId)
.build().toByteArray();
@ -129,10 +123,6 @@ public class PreKeyWhisperMessage implements CiphertextMessage {
return baseKey;
}
public byte[] getVerification() {
return verification;
}
public WhisperMessage getWhisperMessage() {
return message;
}

View File

@ -19,6 +19,7 @@ package org.whispersystems.libaxolotl.protocol;
import com.google.protobuf.ByteString;
import com.google.protobuf.InvalidProtocolBufferException;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.LegacyMessageException;
@ -80,7 +81,9 @@ public class WhisperMessage implements CiphertextMessage {
}
public WhisperMessage(int messageVersion, SecretKeySpec macKey, ECPublicKey senderRatchetKey,
int counter, int previousCounter, byte[] ciphertext)
int counter, int previousCounter, byte[] ciphertext,
IdentityKey senderIdentityKey,
IdentityKey receiverIdentityKey)
{
byte[] version = {ByteUtil.intsToByteHighAndLow(messageVersion, CURRENT_VERSION)};
byte[] message = WhisperProtos.WhisperMessage.newBuilder()
@ -89,7 +92,9 @@ public class WhisperMessage implements CiphertextMessage {
.setPreviousCounter(previousCounter)
.setCiphertext(ByteString.copyFrom(ciphertext))
.build().toByteArray();
byte[] mac = getMac(macKey, ByteUtil.combine(version, message));
byte[] mac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey,
ByteUtil.combine(version, message));
this.serialized = ByteUtil.combine(version, message, mac);
this.senderRatchetKey = senderRatchetKey;
@ -115,11 +120,12 @@ public class WhisperMessage implements CiphertextMessage {
return ciphertext;
}
public void verifyMac(SecretKeySpec macKey)
public void verifyMac(int messageVersion, IdentityKey senderIdentityKey,
IdentityKey receiverIdentityKey, SecretKeySpec macKey)
throws InvalidMessageException
{
byte[][] parts = ByteUtil.split(serialized, serialized.length - MAC_LENGTH, MAC_LENGTH);
byte[] ourMac = getMac(macKey, parts[0]);
byte[] ourMac = getMac(messageVersion, senderIdentityKey, receiverIdentityKey, macKey, parts[0]);
byte[] theirMac = parts[1];
if (!MessageDigest.isEqual(ourMac, theirMac)) {
@ -127,11 +133,20 @@ public class WhisperMessage implements CiphertextMessage {
}
}
private byte[] getMac(SecretKeySpec macKey, byte[] serialized) {
private byte[] getMac(int messageVersion,
IdentityKey senderIdentityKey,
IdentityKey receiverIdentityKey,
SecretKeySpec macKey, byte[] serialized)
{
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(macKey);
if (messageVersion >= 3) {
mac.update(senderIdentityKey.getPublicKey().serialize());
mac.update(receiverIdentityKey.getPublicKey().serialize());
}
byte[] fullMac = mac.doFinal(serialized);
return ByteUtil.trim(fullMac, MAC_LENGTH);
} catch (NoSuchAlgorithmException | java.security.InvalidKeyException e) {

View File

@ -706,16 +706,6 @@ public final class WhisperProtos {
*/
com.google.protobuf.ByteString getIdentityKey();
// optional bytes verification = 7;
/**
* <code>optional bytes verification = 7;</code>
*/
boolean hasVerification();
/**
* <code>optional bytes verification = 7;</code>
*/
com.google.protobuf.ByteString getVerification();
// optional bytes message = 4;
/**
* <code>optional bytes message = 4;</code>
@ -801,7 +791,7 @@ public final class WhisperProtos {
break;
}
case 34: {
bitField0_ |= 0x00000040;
bitField0_ |= 0x00000020;
message_ = input.readBytes();
break;
}
@ -815,11 +805,6 @@ public final class WhisperProtos {
signedPreKeyId_ = input.readUInt32();
break;
}
case 58: {
bitField0_ |= 0x00000020;
verification_ = input.readBytes();
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@ -940,22 +925,6 @@ public final class WhisperProtos {
return identityKey_;
}
// optional bytes verification = 7;
public static final int VERIFICATION_FIELD_NUMBER = 7;
private com.google.protobuf.ByteString verification_;
/**
* <code>optional bytes verification = 7;</code>
*/
public boolean hasVerification() {
return ((bitField0_ & 0x00000020) == 0x00000020);
}
/**
* <code>optional bytes verification = 7;</code>
*/
public com.google.protobuf.ByteString getVerification() {
return verification_;
}
// optional bytes message = 4;
public static final int MESSAGE_FIELD_NUMBER = 4;
private com.google.protobuf.ByteString message_;
@ -967,7 +936,7 @@ public final class WhisperProtos {
* </pre>
*/
public boolean hasMessage() {
return ((bitField0_ & 0x00000040) == 0x00000040);
return ((bitField0_ & 0x00000020) == 0x00000020);
}
/**
* <code>optional bytes message = 4;</code>
@ -986,7 +955,6 @@ public final class WhisperProtos {
signedPreKeyId_ = 0;
baseKey_ = com.google.protobuf.ByteString.EMPTY;
identityKey_ = com.google.protobuf.ByteString.EMPTY;
verification_ = com.google.protobuf.ByteString.EMPTY;
message_ = com.google.protobuf.ByteString.EMPTY;
}
private byte memoizedIsInitialized = -1;
@ -1010,7 +978,7 @@ public final class WhisperProtos {
if (((bitField0_ & 0x00000010) == 0x00000010)) {
output.writeBytes(3, identityKey_);
}
if (((bitField0_ & 0x00000040) == 0x00000040)) {
if (((bitField0_ & 0x00000020) == 0x00000020)) {
output.writeBytes(4, message_);
}
if (((bitField0_ & 0x00000001) == 0x00000001)) {
@ -1019,9 +987,6 @@ public final class WhisperProtos {
if (((bitField0_ & 0x00000004) == 0x00000004)) {
output.writeUInt32(6, signedPreKeyId_);
}
if (((bitField0_ & 0x00000020) == 0x00000020)) {
output.writeBytes(7, verification_);
}
getUnknownFields().writeTo(output);
}
@ -1043,7 +1008,7 @@ public final class WhisperProtos {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(3, identityKey_);
}
if (((bitField0_ & 0x00000040) == 0x00000040)) {
if (((bitField0_ & 0x00000020) == 0x00000020)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(4, message_);
}
@ -1055,10 +1020,6 @@ public final class WhisperProtos {
size += com.google.protobuf.CodedOutputStream
.computeUInt32Size(6, signedPreKeyId_);
}
if (((bitField0_ & 0x00000020) == 0x00000020)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(7, verification_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@ -1185,10 +1146,8 @@ public final class WhisperProtos {
bitField0_ = (bitField0_ & ~0x00000008);
identityKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000010);
verification_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000020);
message_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00000040);
bitField0_ = (bitField0_ & ~0x00000020);
return this;
}
@ -1240,10 +1199,6 @@ public final class WhisperProtos {
if (((from_bitField0_ & 0x00000020) == 0x00000020)) {
to_bitField0_ |= 0x00000020;
}
result.verification_ = verification_;
if (((from_bitField0_ & 0x00000040) == 0x00000040)) {
to_bitField0_ |= 0x00000040;
}
result.message_ = message_;
result.bitField0_ = to_bitField0_;
onBuilt();
@ -1276,9 +1231,6 @@ public final class WhisperProtos {
if (other.hasIdentityKey()) {
setIdentityKey(other.getIdentityKey());
}
if (other.hasVerification()) {
setVerification(other.getVerification());
}
if (other.hasMessage()) {
setMessage(other.getMessage());
}
@ -1480,42 +1432,6 @@ public final class WhisperProtos {
return this;
}
// optional bytes verification = 7;
private com.google.protobuf.ByteString verification_ = com.google.protobuf.ByteString.EMPTY;
/**
* <code>optional bytes verification = 7;</code>
*/
public boolean hasVerification() {
return ((bitField0_ & 0x00000020) == 0x00000020);
}
/**
* <code>optional bytes verification = 7;</code>
*/
public com.google.protobuf.ByteString getVerification() {
return verification_;
}
/**
* <code>optional bytes verification = 7;</code>
*/
public Builder setVerification(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000020;
verification_ = value;
onChanged();
return this;
}
/**
* <code>optional bytes verification = 7;</code>
*/
public Builder clearVerification() {
bitField0_ = (bitField0_ & ~0x00000020);
verification_ = getDefaultInstance().getVerification();
onChanged();
return this;
}
// optional bytes message = 4;
private com.google.protobuf.ByteString message_ = com.google.protobuf.ByteString.EMPTY;
/**
@ -1526,7 +1442,7 @@ public final class WhisperProtos {
* </pre>
*/
public boolean hasMessage() {
return ((bitField0_ & 0x00000040) == 0x00000040);
return ((bitField0_ & 0x00000020) == 0x00000020);
}
/**
* <code>optional bytes message = 4;</code>
@ -1549,7 +1465,7 @@ public final class WhisperProtos {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00000040;
bitField0_ |= 0x00000020;
message_ = value;
onChanged();
return this;
@ -1562,7 +1478,7 @@ public final class WhisperProtos {
* </pre>
*/
public Builder clearMessage() {
bitField0_ = (bitField0_ & ~0x00000040);
bitField0_ = (bitField0_ & ~0x00000020);
message_ = getDefaultInstance().getMessage();
onChanged();
return this;
@ -1625,18 +1541,10 @@ public final class WhisperProtos {
// optional bytes baseKeySignature = 5;
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
boolean hasBaseKeySignature();
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
com.google.protobuf.ByteString getBaseKeySignature();
}
@ -1825,20 +1733,12 @@ public final class WhisperProtos {
private com.google.protobuf.ByteString baseKeySignature_;
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
public boolean hasBaseKeySignature() {
return ((bitField0_ & 0x00000010) == 0x00000010);
}
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
public com.google.protobuf.ByteString getBaseKeySignature() {
return baseKeySignature_;
@ -2284,30 +2184,18 @@ public final class WhisperProtos {
private com.google.protobuf.ByteString baseKeySignature_ = com.google.protobuf.ByteString.EMPTY;
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
public boolean hasBaseKeySignature() {
return ((bitField0_ & 0x00000010) == 0x00000010);
}
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
public com.google.protobuf.ByteString getBaseKeySignature() {
return baseKeySignature_;
}
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
public Builder setBaseKeySignature(com.google.protobuf.ByteString value) {
if (value == null) {
@ -2320,10 +2208,6 @@ public final class WhisperProtos {
}
/**
* <code>optional bytes baseKeySignature = 5;</code>
*
* <pre>
* optional bytes verification = 6;
* </pre>
*/
public Builder clearBaseKeySignature() {
bitField0_ = (bitField0_ & ~0x00000010);
@ -2370,16 +2254,15 @@ public final class WhisperProtos {
"\n\031WhisperTextProtocol.proto\022\ntextsecure\"" +
"b\n\016WhisperMessage\022\022\n\nratchetKey\030\001 \001(\014\022\017\n" +
"\007counter\030\002 \001(\r\022\027\n\017previousCounter\030\003 \001(\r\022" +
"\022\n\nciphertext\030\004 \001(\014\"\245\001\n\024PreKeyWhisperMes" +
"\022\n\nciphertext\030\004 \001(\014\"\217\001\n\024PreKeyWhisperMes" +
"sage\022\026\n\016registrationId\030\005 \001(\r\022\020\n\010preKeyId" +
"\030\001 \001(\r\022\026\n\016signedPreKeyId\030\006 \001(\r\022\017\n\007baseKe" +
"y\030\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014\022\024\n\014verifica" +
"tion\030\007 \001(\014\022\017\n\007message\030\004 \001(\014\"t\n\022KeyExchan" +
"geMessage\022\n\n\002id\030\001 \001(\r\022\017\n\007baseKey\030\002 \001(\014\022\022" +
"\n\nratchetKey\030\003 \001(\014\022\023\n\013identityKey\030\004 \001(\014\022",
"\030\n\020baseKeySignature\030\005 \001(\014B7\n&org.whisper" +
"systems.libaxolotl.protocolB\rWhisperProt" +
"os"
"y\030\002 \001(\014\022\023\n\013identityKey\030\003 \001(\014\022\017\n\007message\030" +
"\004 \001(\014\"t\n\022KeyExchangeMessage\022\n\n\002id\030\001 \001(\r\022" +
"\017\n\007baseKey\030\002 \001(\014\022\022\n\nratchetKey\030\003 \001(\014\022\023\n\013" +
"identityKey\030\004 \001(\014\022\030\n\020baseKeySignature\030\005 ",
"\001(\014B7\n&org.whispersystems.libaxolotl.pro" +
"tocolB\rWhisperProtos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -2397,7 +2280,7 @@ public final class WhisperProtos {
internal_static_textsecure_PreKeyWhisperMessage_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_PreKeyWhisperMessage_descriptor,
new java.lang.String[] { "RegistrationId", "PreKeyId", "SignedPreKeyId", "BaseKey", "IdentityKey", "Verification", "Message", });
new java.lang.String[] { "RegistrationId", "PreKeyId", "SignedPreKeyId", "BaseKey", "IdentityKey", "Message", });
internal_static_textsecure_KeyExchangeMessage_descriptor =
getDescriptor().getMessageTypes().get(2);
internal_static_textsecure_KeyExchangeMessage_fieldAccessorTable = new

View File

@ -28,7 +28,6 @@ import org.whispersystems.libaxolotl.util.guava.Optional;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.text.ParseException;
import java.util.Arrays;
public class RatchetingSession {
@ -98,11 +97,6 @@ public class RatchetingSession {
sessionState.addReceiverChain(parameters.getTheirRatchetKey(), derivedKeys.getChainKey());
sessionState.setSenderChain(sendingRatchetKey, sendingChain.second());
sessionState.setRootKey(sendingChain.first());
if (sessionVersion >= 3) {
sessionState.setVerification(calculateVerificationTag(derivedKeys.getVerifyKey(), parameters));
}
} catch (IOException e) {
throw new AssertionError(e);
}
@ -141,39 +135,11 @@ public class RatchetingSession {
sessionState.setSenderChain(parameters.getOurRatchetKey(), derivedKeys.getChainKey());
sessionState.setRootKey(derivedKeys.getRootKey());
if (sessionVersion >= 3) {
sessionState.setVerification(calculateVerificationTag(derivedKeys.getVerifyKey(), parameters));
}
} catch (IOException e) {
throw new AssertionError(e);
}
}
private static byte[] calculateVerificationTag(VerifyKey verifyKey, AliceAxolotlParameters parameters) {
return verifyKey.generateVerification(parameters.getOurIdentityKey().getPublicKey(),
parameters.getTheirIdentityKey(),
parameters.getOurBaseKey().getPublicKey(),
parameters.getTheirSignedPreKey(),
parameters.getTheirOneTimePreKey());
}
private static byte[] calculateVerificationTag(VerifyKey verifyKey, BobAxolotlParameters parameters) {
Optional<ECPublicKey> ourOneTimePreKey;
if (parameters.getOurOneTimePreKey().isPresent()) {
ourOneTimePreKey = Optional.of(parameters.getOurOneTimePreKey().get().getPublicKey());
} else {
ourOneTimePreKey = Optional.absent();
}
return verifyKey.generateVerification(parameters.getTheirIdentityKey(),
parameters.getOurIdentityKey().getPublicKey(),
parameters.getTheirBaseKey(),
parameters.getOurSignedPreKey().getPublicKey(),
ourOneTimePreKey);
}
private static byte[] getDiscontinuityBytes() {
byte[] discontinuity = new byte[32];
Arrays.fill(discontinuity, (byte) 0xFF);
@ -181,33 +147,25 @@ public class RatchetingSession {
}
private static DerivedKeys calculateDerivedKeys(int sessionVersion, byte[] masterSecret) {
try {
HKDF kdf = HKDF.createFor(sessionVersion);
byte[] derivedSecretBytes = kdf.deriveSecrets(masterSecret, "WhisperText".getBytes(), 96);
byte[][] derivedSecrets = ByteUtil.split(derivedSecretBytes, 32, 32, 32);
HKDF kdf = HKDF.createFor(sessionVersion);
byte[] derivedSecretBytes = kdf.deriveSecrets(masterSecret, "WhisperText".getBytes(), 64);
byte[][] derivedSecrets = ByteUtil.split(derivedSecretBytes, 32, 32);
return new DerivedKeys(new RootKey(kdf, derivedSecrets[0]),
new ChainKey(kdf, derivedSecrets[1], 0),
new VerifyKey(derivedSecrets[2]));
} catch (ParseException e) {
throw new AssertionError(e);
}
return new DerivedKeys(new RootKey(kdf, derivedSecrets[0]),
new ChainKey(kdf, derivedSecrets[1], 0));
}
private static boolean isAlice(ECPublicKey ourKey, ECPublicKey theirKey) {
return ourKey.compareTo(theirKey) < 0;
}
private static class DerivedKeys {
private final RootKey rootKey;
private final ChainKey chainKey;
private final VerifyKey verifyKey;
private DerivedKeys(RootKey rootKey, ChainKey chainKey, VerifyKey verifyKey) {
private DerivedKeys(RootKey rootKey, ChainKey chainKey) {
this.rootKey = rootKey;
this.chainKey = chainKey;
this.verifyKey = verifyKey;
}
public RootKey getRootKey() {
@ -217,9 +175,5 @@ public class RatchetingSession {
public ChainKey getChainKey() {
return chainKey;
}
public VerifyKey getVerifyKey() {
return verifyKey;
}
}
}

View File

@ -1,53 +0,0 @@
package org.whispersystems.libaxolotl.ratchet;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.util.ByteUtil;
import org.whispersystems.libaxolotl.util.guava.Optional;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public class VerifyKey {
private static final byte[] VERIFICATION_INFO = "TextSecure Verification Tag".getBytes();
private final byte[] key;
public VerifyKey(byte[] key) {
this.key = key;
}
public byte[] getKey() {
return key;
}
public byte[] generateVerification(IdentityKey aliceIdentity,
IdentityKey bobIdentity,
ECPublicKey aliceBaseKey,
ECPublicKey bobSignedPreKey,
Optional<ECPublicKey> bobOneTimePreKey)
{
try {
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(key, "HmacSHA256"));
mac.update(VERIFICATION_INFO);
mac.update(aliceIdentity.getPublicKey().serialize());
mac.update(bobIdentity.getPublicKey().serialize());
mac.update(aliceBaseKey.serialize());
mac.update(bobSignedPreKey.serialize());
if (bobOneTimePreKey.isPresent()) {
mac.update(bobOneTimePreKey.get().serialize());
}
return ByteUtil.trim(mac.doFinal(), 8);
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
throw new AssertionError(e);
}
}
}

View File

@ -61,16 +61,6 @@ public class SessionState {
return sessionStructure;
}
public byte[] getVerification() {
return this.sessionStructure.getVerification().toByteArray();
}
public void setVerification(byte[] verification) {
this.sessionStructure = this.sessionStructure.toBuilder()
.setVerification(ByteString.copyFrom(verification))
.build();
}
public byte[] getAliceBaseKey() {
return this.sessionStructure.getAliceBaseKey().toByteArray();
}

View File

@ -167,16 +167,6 @@ public final class StorageProtos {
* <code>optional bytes aliceBaseKey = 13;</code>
*/
com.google.protobuf.ByteString getAliceBaseKey();
// optional bytes verification = 14;
/**
* <code>optional bytes verification = 14;</code>
*/
boolean hasVerification();
/**
* <code>optional bytes verification = 14;</code>
*/
com.google.protobuf.ByteString getVerification();
}
/**
* Protobuf type {@code textsecure.SessionStructure}
@ -321,11 +311,6 @@ public final class StorageProtos {
aliceBaseKey_ = input.readBytes();
break;
}
case 114: {
bitField0_ |= 0x00001000;
verification_ = input.readBytes();
break;
}
}
}
} catch (com.google.protobuf.InvalidProtocolBufferException e) {
@ -4157,22 +4142,6 @@ public final class StorageProtos {
return aliceBaseKey_;
}
// optional bytes verification = 14;
public static final int VERIFICATION_FIELD_NUMBER = 14;
private com.google.protobuf.ByteString verification_;
/**
* <code>optional bytes verification = 14;</code>
*/
public boolean hasVerification() {
return ((bitField0_ & 0x00001000) == 0x00001000);
}
/**
* <code>optional bytes verification = 14;</code>
*/
public com.google.protobuf.ByteString getVerification() {
return verification_;
}
private void initFields() {
sessionVersion_ = 0;
localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY;
@ -4187,7 +4156,6 @@ public final class StorageProtos {
localRegistrationId_ = 0;
needsRefresh_ = false;
aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY;
verification_ = com.google.protobuf.ByteString.EMPTY;
}
private byte memoizedIsInitialized = -1;
public final boolean isInitialized() {
@ -4240,9 +4208,6 @@ public final class StorageProtos {
if (((bitField0_ & 0x00000800) == 0x00000800)) {
output.writeBytes(13, aliceBaseKey_);
}
if (((bitField0_ & 0x00001000) == 0x00001000)) {
output.writeBytes(14, verification_);
}
getUnknownFields().writeTo(output);
}
@ -4304,10 +4269,6 @@ public final class StorageProtos {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(13, aliceBaseKey_);
}
if (((bitField0_ & 0x00001000) == 0x00001000)) {
size += com.google.protobuf.CodedOutputStream
.computeBytesSize(14, verification_);
}
size += getUnknownFields().getSerializedSize();
memoizedSerializedSize = size;
return size;
@ -4470,8 +4431,6 @@ public final class StorageProtos {
bitField0_ = (bitField0_ & ~0x00000800);
aliceBaseKey_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00001000);
verification_ = com.google.protobuf.ByteString.EMPTY;
bitField0_ = (bitField0_ & ~0x00002000);
return this;
}
@ -4569,10 +4528,6 @@ public final class StorageProtos {
to_bitField0_ |= 0x00000800;
}
result.aliceBaseKey_ = aliceBaseKey_;
if (((from_bitField0_ & 0x00002000) == 0x00002000)) {
to_bitField0_ |= 0x00001000;
}
result.verification_ = verification_;
result.bitField0_ = to_bitField0_;
onBuilt();
return result;
@ -4651,9 +4606,6 @@ public final class StorageProtos {
if (other.hasAliceBaseKey()) {
setAliceBaseKey(other.getAliceBaseKey());
}
if (other.hasVerification()) {
setVerification(other.getVerification());
}
this.mergeUnknownFields(other.getUnknownFields());
return this;
}
@ -5581,42 +5533,6 @@ public final class StorageProtos {
return this;
}
// optional bytes verification = 14;
private com.google.protobuf.ByteString verification_ = com.google.protobuf.ByteString.EMPTY;
/**
* <code>optional bytes verification = 14;</code>
*/
public boolean hasVerification() {
return ((bitField0_ & 0x00002000) == 0x00002000);
}
/**
* <code>optional bytes verification = 14;</code>
*/
public com.google.protobuf.ByteString getVerification() {
return verification_;
}
/**
* <code>optional bytes verification = 14;</code>
*/
public Builder setVerification(com.google.protobuf.ByteString value) {
if (value == null) {
throw new NullPointerException();
}
bitField0_ |= 0x00002000;
verification_ = value;
onChanged();
return this;
}
/**
* <code>optional bytes verification = 14;</code>
*/
public Builder clearVerification() {
bitField0_ = (bitField0_ & ~0x00002000);
verification_ = getDefaultInstance().getVerification();
onChanged();
return this;
}
// @@protoc_insertion_point(builder_scope:textsecure.SessionStructure)
}
@ -8333,7 +8249,7 @@ public final class StorageProtos {
static {
java.lang.String[] descriptorData = {
"\n\032LocalStorageProtocol.proto\022\ntextsecure" +
"\"\335\010\n\020SessionStructure\022\026\n\016sessionVersion\030" +
"\"\307\010\n\020SessionStructure\022\026\n\016sessionVersion\030" +
"\001 \001(\r\022\033\n\023localIdentityPublic\030\002 \001(\014\022\034\n\024re" +
"moteIdentityPublic\030\003 \001(\014\022\017\n\007rootKey\030\004 \001(" +
"\014\022\027\n\017previousCounter\030\005 \001(\r\0227\n\013senderChai" +
@ -8345,34 +8261,33 @@ public final class StorageProtos {
"\001(\0132*.textsecure.SessionStructure.Pendin" +
"gPreKey\022\034\n\024remoteRegistrationId\030\n \001(\r\022\033\n" +
"\023localRegistrationId\030\013 \001(\r\022\024\n\014needsRefre" +
"sh\030\014 \001(\010\022\024\n\014aliceBaseKey\030\r \001(\014\022\024\n\014verifi" +
"cation\030\016 \001(\014\032\255\002\n\005Chain\022\030\n\020senderRatchetK" +
"ey\030\001 \001(\014\022\037\n\027senderRatchetKeyPrivate\030\002 \001(" +
"\014\022=\n\010chainKey\030\003 \001(\0132+.textsecure.Session" +
"Structure.Chain.ChainKey\022B\n\013messageKeys\030" +
"\004 \003(\0132-.textsecure.SessionStructure.Chai" +
"n.MessageKey\032&\n\010ChainKey\022\r\n\005index\030\001 \001(\r\022",
"\013\n\003key\030\002 \001(\014\032>\n\nMessageKey\022\r\n\005index\030\001 \001(" +
"\r\022\021\n\tcipherKey\030\002 \001(\014\022\016\n\006macKey\030\003 \001(\014\032\315\001\n" +
"\022PendingKeyExchange\022\020\n\010sequence\030\001 \001(\r\022\024\n" +
"\014localBaseKey\030\002 \001(\014\022\033\n\023localBaseKeyPriva" +
"te\030\003 \001(\014\022\027\n\017localRatchetKey\030\004 \001(\014\022\036\n\026loc" +
"alRatchetKeyPrivate\030\005 \001(\014\022\030\n\020localIdenti" +
"tyKey\030\007 \001(\014\022\037\n\027localIdentityKeyPrivate\030\010" +
" \001(\014\032J\n\rPendingPreKey\022\020\n\010preKeyId\030\001 \001(\r\022" +
"\026\n\016signedPreKeyId\030\003 \001(\005\022\017\n\007baseKey\030\002 \001(\014" +
"\"\177\n\017RecordStructure\0224\n\016currentSession\030\001 ",
"\001(\0132\034.textsecure.SessionStructure\0226\n\020pre" +
"viousSessions\030\002 \003(\0132\034.textsecure.Session" +
"Structure\"J\n\025PreKeyRecordStructure\022\n\n\002id" +
"\030\001 \001(\r\022\021\n\tpublicKey\030\002 \001(\014\022\022\n\nprivateKey\030" +
"\003 \001(\014\"v\n\033SignedPreKeyRecordStructure\022\n\n\002" +
"id\030\001 \001(\r\022\021\n\tpublicKey\030\002 \001(\014\022\022\n\nprivateKe" +
"y\030\003 \001(\014\022\021\n\tsignature\030\004 \001(\014\022\021\n\ttimestamp\030" +
"\005 \001(\006\"A\n\030IdentityKeyPairStructure\022\021\n\tpub" +
"licKey\030\001 \001(\014\022\022\n\nprivateKey\030\002 \001(\014B4\n#org." +
"whispersystems.libaxolotl.stateB\rStorage",
"Protos"
"sh\030\014 \001(\010\022\024\n\014aliceBaseKey\030\r \001(\014\032\255\002\n\005Chain" +
"\022\030\n\020senderRatchetKey\030\001 \001(\014\022\037\n\027senderRatc" +
"hetKeyPrivate\030\002 \001(\014\022=\n\010chainKey\030\003 \001(\0132+." +
"textsecure.SessionStructure.Chain.ChainK" +
"ey\022B\n\013messageKeys\030\004 \003(\0132-.textsecure.Ses" +
"sionStructure.Chain.MessageKey\032&\n\010ChainK" +
"ey\022\r\n\005index\030\001 \001(\r\022\013\n\003key\030\002 \001(\014\032>\n\nMessag",
"eKey\022\r\n\005index\030\001 \001(\r\022\021\n\tcipherKey\030\002 \001(\014\022\016" +
"\n\006macKey\030\003 \001(\014\032\315\001\n\022PendingKeyExchange\022\020\n" +
"\010sequence\030\001 \001(\r\022\024\n\014localBaseKey\030\002 \001(\014\022\033\n" +
"\023localBaseKeyPrivate\030\003 \001(\014\022\027\n\017localRatch" +
"etKey\030\004 \001(\014\022\036\n\026localRatchetKeyPrivate\030\005 " +
"\001(\014\022\030\n\020localIdentityKey\030\007 \001(\014\022\037\n\027localId" +
"entityKeyPrivate\030\010 \001(\014\032J\n\rPendingPreKey\022" +
"\020\n\010preKeyId\030\001 \001(\r\022\026\n\016signedPreKeyId\030\003 \001(" +
"\005\022\017\n\007baseKey\030\002 \001(\014\"\177\n\017RecordStructure\0224\n" +
"\016currentSession\030\001 \001(\0132\034.textsecure.Sessi",
"onStructure\0226\n\020previousSessions\030\002 \003(\0132\034." +
"textsecure.SessionStructure\"J\n\025PreKeyRec" +
"ordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicKey\030\002 " +
"\001(\014\022\022\n\nprivateKey\030\003 \001(\014\"v\n\033SignedPreKeyR" +
"ecordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicKey\030" +
"\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014\022\021\n\tsignature\030\004" +
" \001(\014\022\021\n\ttimestamp\030\005 \001(\006\"A\n\030IdentityKeyPa" +
"irStructure\022\021\n\tpublicKey\030\001 \001(\014\022\022\n\nprivat" +
"eKey\030\002 \001(\014B4\n#org.whispersystems.libaxol" +
"otl.stateB\rStorageProtos"
};
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
@ -8384,7 +8299,7 @@ public final class StorageProtos {
internal_static_textsecure_SessionStructure_fieldAccessorTable = new
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
internal_static_textsecure_SessionStructure_descriptor,
new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", "AliceBaseKey", "Verification", });
new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", "AliceBaseKey", });
internal_static_textsecure_SessionStructure_Chain_descriptor =
internal_static_textsecure_SessionStructure_descriptor.getNestedTypes().get(0);
internal_static_textsecure_SessionStructure_Chain_fieldAccessorTable = new