content: /**
   * Copyright (C) 2014-2016 Open Whisper Systems
   *
   * Licensed according to the LICENSE file in this repository.
   */
  package org.whispersystems.libsignal.protocol;
  
  import com.google.protobuf.ByteString;
  import com.google.protobuf.InvalidProtocolBufferException;
  
  import org.whispersystems.libsignal.IdentityKey;
  import org.whispersystems.libsignal.InvalidKeyException;
  import org.whispersystems.libsignal.InvalidMessageException;
  import org.whispersystems.libsignal.InvalidVersionException;
  import org.whispersystems.libsignal.LegacyMessageException;
  import org.whispersystems.libsignal.ecc.Curve;
  import org.whispersystems.libsignal.ecc.ECPublicKey;
  import org.whispersystems.libsignal.util.ByteUtil;
  import org.whispersystems.libsignal.util.guava.Optional;
  
  
  public class PreKeySignalMessage implements CiphertextMessage {
  
    private final int               version;
    private final int               registrationId;
    private final Optional<Integer> preKeyId;
    private final int               signedPreKeyId;
    private final ECPublicKey       baseKey;
    private final IdentityKey       identityKey;
    private final SignalMessage     message;
    private final byte[]            serialized;
  
    public PreKeySignalMessage(byte[] serialized)
        throws InvalidMessageException, InvalidVersionException
    {
      try {
        this.version = ByteUtil.highBitsToInt(serialized[0]);
  
        if (this.version > CiphertextMessage.CURRENT_VERSION) {
          throw new InvalidVersionException("Unknown version: " + this.version);
        }
  
        if (this.version < CiphertextMessage.CURRENT_VERSION) {
          throw new LegacyMessageException("Legacy version: " + this.version);
        }
  
        SignalProtos.PreKeySignalMessage preKeyWhisperMessage
            = SignalProtos.PreKeySignalMessage.parseFrom(ByteString.copyFrom(serialized, 1,
                                                                             serialized.length-1));
  
        if (!preKeyWhisperMessage.hasSignedPreKeyId()  ||
            !preKeyWhisperMessage.hasBaseKey()         ||
            !preKeyWhisperMessage.hasIdentityKey()     ||
            !preKeyWhisperMessage.hasMessage())
        {
          throw new InvalidMessageException("Incomplete message.");
        }
  
        this.serialized     = serialized;
        this.registrationId = preKeyWhisperMessage.getRegistrationId();
        this.preKeyId       = preKeyWhisperMessage.hasPreKeyId() ? Optional.of(preKeyWhisperMessage.getPreKeyId()) : Optional.<Integer>absent();
        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.message        = new SignalMessage(preKeyWhisperMessage.getMessage().toByteArray());
      } catch (InvalidProtocolBufferException | InvalidKeyException | LegacyMessageException e) {
        throw new InvalidMessageException(e);
      }
    }
  
    public PreKeySignalMessage(int messageVersion, int registrationId, Optional<Integer> preKeyId,
                               int signedPreKeyId, ECPublicKey baseKey, IdentityKey identityKey,
                               SignalMessage message)
    {
      this.version        = messageVersion;
      this.registrationId = registrationId;
      this.preKeyId       = preKeyId;
      this.signedPreKeyId = signedPreKeyId;
      this.baseKey        = baseKey;
      this.identityKey    = identityKey;
      this.message        = message;
  
      SignalProtos.PreKeySignalMessage.Builder builder =
          SignalProtos.PreKeySignalMessage.newBuilder()
                                          .setSignedPreKeyId(signedPreKeyId)
                                          .setBaseKey(ByteString.copyFrom(baseKey.serialize()))
                                          .setIdentityKey(ByteString.copyFrom(identityKey.serialize()))
                                          .setMessage(ByteString.copyFrom(message.serialize()))
                                          .setRegistrationId(registrationId);
  
      if (preKeyId.isPresent()) {
        builder.setPreKeyId(preKeyId.get());
      }
  
      byte[] versionBytes = {ByteUtil.intsToByteHighAndLow(this.version, CURRENT_VERSION)};
      byte[] messageBytes = builder.build().toByteArray();
  
      this.serialized = ByteUtil.combine(versionBytes, messageBytes);
    }
  
    public int getMessageVersion() {
      return version;
    }
  
    public IdentityKey getIdentityKey() {
      return identityKey;
    }
  
    public int getRegistrationId() {
      return registrationId;
    }
  
    public Optional<Integer> getPreKeyId() {
      return preKeyId;
    }
  
    public int getSignedPreKeyId() {
      return signedPreKeyId;
    }
  
    public ECPublicKey getBaseKey() {
      return baseKey;
    }
  
    public SignalMessage getWhisperMessage() {
      return message;
    }
  
    @Override
    public byte[] serialize() {
      return serialized;
    }
  
    @Override
    public int getType() {
      return CiphertextMessage.PREKEY_TYPE;
    }
  
  }
  