public final class AuthModeUnavailableException extends CapillaryException {

  /**
   * Creates a new {@link AuthModeUnavailableException} instance.
   *
   * @param msg the exception message.
   */
  public AuthModeUnavailableException(String msg) {
    super(msg);
  }
} public final class Config {

  /**
   * Initializes the Capillary library.
   *
   * <p>This should be called before using any functionality provided by Capillary.
   *
   * @throws GeneralSecurityException if the initialization fails.
   */
  public static void initialize() throws GeneralSecurityException {
    com.google.crypto.tink.Config.register(SignatureConfig.TINK_1_1_0);
    com.google.crypto.tink.Config.register(AeadConfig.TINK_1_1_0);
  }
} public abstract class EncrypterManager {

  private boolean isLoaded;
  private CapillaryPublicKey capillaryPublicKey;
  private HybridEncrypt encrypter;

  /**
   * Loads a serialized Capillary public key.
   *
   * @param publicKey the serialized Capillary public key.
   * @throws GeneralSecurityException if the given Capillary public key cannot be loaded.
   */
  public synchronized void loadPublicKey(byte[] publicKey) throws GeneralSecurityException {
    try {
      capillaryPublicKey = CapillaryPublicKey.parseFrom(publicKey);
    } catch (InvalidProtocolBufferException e) {
      throw new GeneralSecurityException("unable to parse public key", e);
    }
    encrypter = rawLoadPublicKey(capillaryPublicKey.getKeyBytes().toByteArray());
    isLoaded = true;
  }

  /**
   * Creates a {@link HybridEncrypt} for a raw public key embedded in a Capillary public key.
   */
  abstract HybridEncrypt rawLoadPublicKey(byte[] rawPublicKey) throws GeneralSecurityException;

  /**
   * Encryptes the given plaintext into a Capillary ciphertext.
   *
   * @param data the plaintext.
   * @return the Capillary ciphertext.
   * @throws GeneralSecurityException if the encryption fails.
   */
  public synchronized byte[] encrypt(byte[] data) throws GeneralSecurityException {
    if (!isLoaded) {
      throw new GeneralSecurityException("public key is not loaded");
    }
    byte[] ciphertext = encrypter.encrypt(data, null);
    return CapillaryCiphertext.newBuilder()
        .setKeychainUniqueId(capillaryPublicKey.getKeychainUniqueId())
        .setKeySerialNumber((capillaryPublicKey.getSerialNumber()))
        .setIsAuthKey(capillaryPublicKey.getIsAuth())
        .setCiphertext(ByteString.copyFrom(ciphertext))
        .build().toByteArray();
  }

  /**
   * Clears the loaded Capillary public key.
   */
  public synchronized void clearPublicKey() {
    isLoaded = false;
    capillaryPublicKey = null;
    encrypter = null;
  }
} public final class NoSuchKeyException extends CapillaryException {

  /**
   * Creates a new {@link NoSuchKeyException} object.
   *
   * @param msg the exception message.
   */
  public NoSuchKeyException(String msg) {
    super(msg);
  }
} public final class RsaEcdsaEncrypterManager extends EncrypterManager {

  private final PublicKeySign senderSigner;

  /**
   * Constructs a new RSA-ECDSA EncrypterManager.
   *
   * <p>Please note that the {@link InputStream} {@code senderSigningKey} will not be closed.
   *
   * @param senderSigningKey the serialized Tink signing key.
   * @throws GeneralSecurityException if the initialization fails.
   * @throws IOException if the given sender signing key cannot be read.
   */
  public RsaEcdsaEncrypterManager(InputStream senderSigningKey)
      throws GeneralSecurityException, IOException {
    KeysetHandle signingKeyHandle = CleartextKeysetHandle
        .read(BinaryKeysetReader.withInputStream(senderSigningKey));
    senderSigner = PublicKeySignFactory.getPrimitive(signingKeyHandle);
  }

  @Override
  HybridEncrypt rawLoadPublicKey(byte[] publicKey) throws GeneralSecurityException {
    WrappedRsaEcdsaPublicKey wrappedRsaEcdsaPublicKey;
    try {
      wrappedRsaEcdsaPublicKey = WrappedRsaEcdsaPublicKey.parseFrom(publicKey);
    } catch (InvalidProtocolBufferException e) {
      throw new GeneralSecurityException("unable to parse public key", e);
    }
    PublicKey recipientPublicKey = KeyFactory.getInstance("RSA").generatePublic(
        new X509EncodedKeySpec(wrappedRsaEcdsaPublicKey.getKeyBytes().toByteArray()));
    return new RsaEcdsaHybridEncrypt.Builder()
        .withSenderSigner(senderSigner)
        .withRecipientPublicKey(recipientPublicKey)
        .withPadding(RsaEcdsaConstants.Padding.valueOf(wrappedRsaEcdsaPublicKey.getPadding()))
        .build();
  }
} public final class WebPushEncrypterManager extends EncrypterManager {

  @Override
  HybridEncrypt rawLoadPublicKey(byte[] publicKey) throws GeneralSecurityException {
    WrappedWebPushPublicKey wrappedWebPushPublicKey;
    try {
      wrappedWebPushPublicKey = WrappedWebPushPublicKey.parseFrom(publicKey);
    } catch (InvalidProtocolBufferException e) {
      throw new GeneralSecurityException("unable to parse public key", e);
    }
    return new WebPushHybridEncrypt.Builder()
        .withAuthSecret(wrappedWebPushPublicKey.getAuthSecret().toByteArray())
        .withRecipientPublicKey(wrappedWebPushPublicKey.getKeyBytes().toByteArray())
        .build();
  }
} static class AuthCiphertextSavedForLaterRequest {

  byte[] ciphertext;
  Object extra;

  AuthCiphertextSavedForLaterRequest(byte[] ciphertext, Object extra) {
    this.ciphertext = ciphertext;
    this.extra = extra;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    AuthCiphertextSavedForLaterRequest that = (AuthCiphertextSavedForLaterRequest) o;

    return Arrays.equals(ciphertext, that.ciphertext) && extra.equals(that.extra);
  }

  @Override
  public int hashCode() {
    int result = Arrays.hashCode(ciphertext);
    result = 31 * result + extra.hashCode();
    return result;
  }
} static class ErrorRequest {

  CapillaryHandlerErrorCode errorCode;
  byte[] ciphertext;
  Object extra;

  ErrorRequest(CapillaryHandlerErrorCode errorCode, byte[] ciphertext, Object extra) {
    this.errorCode = errorCode;
    this.ciphertext = ciphertext;
    this.extra = extra;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    ErrorRequest that = (ErrorRequest) o;

    return errorCode == that.errorCode
        && Arrays.equals(ciphertext, that.ciphertext)
        && extra.equals(that.extra);
  }

  @Override
  public int hashCode() {
    int result = errorCode.hashCode();
    result = 31 * result + Arrays.hashCode(ciphertext);
    result = 31 * result + extra.hashCode();
    return result;
  }
} static class HandleDataRequest {

  boolean isAuthKey;
  byte[] data;
  Object extra;

  HandleDataRequest(boolean isAuthKey, byte[] data, Object extra) {
    this.isAuthKey = isAuthKey;
    this.data = data;
    this.extra = extra;
  }

  @Override
  public int hashCode() {
    int result = (isAuthKey ? 1 : 0);
    result = 31 * result + Arrays.hashCode(data);
    result = 31 * result + extra.hashCode();
    return result;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    HandleDataRequest that = (HandleDataRequest) o;

    return isAuthKey == that.isAuthKey
        && Arrays.equals(data, that.data)
        && extra.equals(that.extra);
  }
} static class HandlePublicKeyRequest {

  boolean isAuthKey;
  byte[] publicKey;
  byte[] ciphertext;
  Object extra;

  HandlePublicKeyRequest(
      boolean isAuthKey, byte[] publicKey, byte[] ciphertext, Object extra) {
    this(isAuthKey, publicKey, extra);
    this.ciphertext = ciphertext;
  }

  HandlePublicKeyRequest(boolean isAuthKey, byte[] publicKey, Object extra) {
    this.isAuthKey = isAuthKey;
    this.publicKey = publicKey;
    this.extra = extra;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || getClass() != o.getClass()) {
      return false;
    }

    HandlePublicKeyRequest that = (HandlePublicKeyRequest) o;

    return isAuthKey == that.isAuthKey
        && Arrays.equals(publicKey, that.publicKey)
        && Arrays.equals(ciphertext, that.ciphertext)
        && extra.equals(that.extra);
  }

  @Override
  public int hashCode() {
    int result = (isAuthKey ? 1 : 0);
    result = 31 * result + Arrays.hashCode(publicKey);
    result = 31 * result + Arrays.hashCode(ciphertext);
    result = 31 * result + extra.hashCode();
    return result;
  }
}
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;
import com.google.capillary.AuthModeUnavailableException;
import com.google.capillary.Config;
import com.google.capillary.EncrypterManager;
import com.google.capillary.NoSuchKeyException;
import com.google.capillary.RsaEcdsaEncrypterManager;
import com.google.capillary.WebPushEncrypterManager;
import com.google.capillary.android.TestHandler.AuthCiphertextSavedForLaterRequest;
import com.google.capillary.android.TestHandler.ErrorRequest;
import com.google.capillary.android.TestHandler.HandleDataRequest;
import com.google.capillary.android.TestHandler.HandlePublicKeyRequest;
import com.google.capillary.internal.CapillaryCiphertext;
import com.google.protobuf.InvalidProtocolBufferException;
import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
/*
 * Copyright 2018 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.capillary.android;



@RunWith(AndroidJUnit4.class)
public final class IntegrationTest {

  private static final String TAG = IntegrationTest.class.getSimpleName();
  private static final int USER_REQUEST_DURATION_SECONDS = 10;
  private static final String KEYCHAIN_ID = "keychain 1";

  private EncrypterManager rsaEcdsaEncrypterManager;
  private WebPushEncrypterManager webPushEncrypterManager;
  private KeyManager rsaEcdsaKeyManager;
  private KeyManager webPushKeyManager;

  /**
   * Creates a new {@link IntegrationTest} instance.
   */
  public IntegrationTest() throws GeneralSecurityException {
    Config.initialize();
  }

  /**
   * Initializes test case-specific state.
   */
  @Before
  public void setUp() throws GeneralSecurityException, IOException {
    Context context = InstrumentationRegistry.getTargetContext();
    try (InputStream senderSigningKey =
        IntegrationTest.class.getResourceAsStream("signing_key.dat")) {
      rsaEcdsaEncrypterManager = new RsaEcdsaEncrypterManager(senderSigningKey);
    }
    try (InputStream senderVerificationKey =
        IntegrationTest.class.getResourceAsStream("verification_key.dat")) {
      rsaEcdsaKeyManager =
          RsaEcdsaKeyManager.getInstance(context, KEYCHAIN_ID, senderVerificationKey);
    }
    webPushEncrypterManager = new WebPushEncrypterManager();
    webPushKeyManager = WebPushKeyManager.getInstance(context, KEYCHAIN_ID);
    TestUtils.clearKeyStore();
  }

  @Test
  public void testRsaEcdsaKeyGenEncryptDecrypt()
      throws NoSuchKeyException,