diff options
Diffstat (limited to 'java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java')
-rw-r--r-- | java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java b/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java new file mode 100644 index 00000000..b3a17456 --- /dev/null +++ b/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java @@ -0,0 +1,121 @@ +package org.whispersystems.libaxolotl.protocol; + +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.whispersystems.libaxolotl.InvalidKeyException; +import org.whispersystems.libaxolotl.InvalidMessageException; +import org.whispersystems.libaxolotl.LegacyMessageException; +import org.whispersystems.libaxolotl.ecc.Curve; +import org.whispersystems.libaxolotl.ecc.ECPrivateKey; +import org.whispersystems.libaxolotl.ecc.ECPublicKey; +import org.whispersystems.libaxolotl.util.ByteUtil; + +import java.text.ParseException; + +public class SenderKeyMessage implements CiphertextMessage { + + private static final int SIGNATURE_LENGTH = 64; + + private final int messageVersion; + private final int keyId; + private final int iteration; + private final byte[] ciphertext; + private final byte[] serialized; + + public SenderKeyMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { + try { + byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH); + byte version = messageParts[0][0]; + byte[] message = messageParts[1]; + byte[] signature = messageParts[2]; + + if (ByteUtil.highBitsToInt(version) < 3) { + throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version)); + } + + if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) { + throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version)); + } + + WhisperProtos.SenderKeyMessage senderKeyMessage = WhisperProtos.SenderKeyMessage.parseFrom(message); + + if (!senderKeyMessage.hasId() || + !senderKeyMessage.hasIteration() || + !senderKeyMessage.hasCiphertext()) + { + throw new InvalidMessageException("Incomplete message."); + } + + this.serialized = serialized; + this.messageVersion = ByteUtil.highBitsToInt(version); + this.keyId = senderKeyMessage.getId(); + this.iteration = senderKeyMessage.getIteration(); + this.ciphertext = senderKeyMessage.getCiphertext().toByteArray(); + } catch (InvalidProtocolBufferException | ParseException e) { + throw new InvalidMessageException(e); + } + } + + public SenderKeyMessage(int keyId, int iteration, byte[] ciphertext, ECPrivateKey signatureKey) { + byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; + byte[] message = WhisperProtos.SenderKeyMessage.newBuilder() + .setId(keyId) + .setIteration(iteration) + .setCiphertext(ByteString.copyFrom(ciphertext)) + .build().toByteArray(); + + byte[] signature = getSignature(signatureKey, ByteUtil.combine(version, message)); + + this.serialized = ByteUtil.combine(version, message, signature); + this.messageVersion = CURRENT_VERSION; + this.keyId = keyId; + this.iteration = iteration; + this.ciphertext = ciphertext; + } + + public int getKeyId() { + return keyId; + } + + public int getIteration() { + return iteration; + } + + public byte[] getCipherText() { + return ciphertext; + } + + public void verifySignature(ECPublicKey signatureKey) + throws InvalidMessageException + { + try { + byte[][] parts = ByteUtil.split(serialized, serialized.length - SIGNATURE_LENGTH, SIGNATURE_LENGTH); + + if (!Curve.verifySignature(signatureKey, parts[0], parts[1])) { + throw new InvalidMessageException("Invalid signature!"); + } + + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + private byte[] getSignature(ECPrivateKey signatureKey, byte[] serialized) { + try { + return Curve.calculateSignature(signatureKey, serialized); + } catch (InvalidKeyException e) { + throw new AssertionError(e); + } + } + + @Override + public byte[] serialize() { + return serialized; + } + + @Override + public int getType() { + return CiphertextMessage.SENDERKEY_TYPE; + } +} |