class WebPushHybridEncrypt implements HybridEncrypt {

  @Override
  public byte[] encrypt(final byte[] plaintext, final byte[] contextInfo /* unused */)
      throws GeneralSecurityException {
    if (contextInfo != null) {
      throw new GeneralSecurityException("contextInfo must be null because it is unused");
    }

    if (plaintext.length > recordSize - WebPushConstants.CIPHERTEXT_OVERHEAD) {
      throw new GeneralSecurityException("plaintext too long");
    }

    // See https://tools.ietf.org/html/rfc8291#section-3.4.
    KeyPair keyPair = EllipticCurves.generateKeyPair(WebPushConstants.NIST_P256_CURVE_TYPE);
    ECPrivateKey ephemeralPrivateKey = (ECPrivateKey) keyPair.getPrivate();
    ECPublicKey ephemeralPublicKey = (ECPublicKey) keyPair.getPublic();
    byte[] ecdhSecret =
        EllipticCurves.computeSharedSecret(ephemeralPrivateKey, recipientPublicPoint);
    byte[] ephemeralPublicKeyBytes =
        EllipticCurves.pointEncode(
            WebPushConstants.NIST_P256_CURVE_TYPE,
            WebPushConstants.UNCOMPRESSED_POINT_FORMAT,
            ephemeralPublicKey.getW());
    byte[] ikm =
        WebPushUtil.computeIkm(ecdhSecret, authSecret, recipientPublicKey, ephemeralPublicKeyBytes);
    byte[] salt = Random.randBytes(WebPushConstants.SALT_SIZE);
    byte[] cek = WebPushUtil.computeCek(ikm, salt);
    byte[] nonce = WebPushUtil.computeNonce(ikm, salt);
    return ByteBuffer.allocate(WebPushConstants.CIPHERTEXT_OVERHEAD + plaintext.length)
        .put(salt)
        .putInt(recordSize)
        .put((byte) WebPushConstants.PUBLIC_KEY_SIZE)
        .put(ephemeralPublicKeyBytes)
        .put(encrypt(cek, nonce, plaintext))
        .array();
  }

  private  WebPushHybridEncrypt(Builder builder);

}