// Copyright 2023 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
//
//      http://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.crypto.tink.hybrid.internal.testing;

import static com.google.crypto.tink.internal.TinkBugException.exceptionIsBug;

import com.google.crypto.tink.AccessesPartialKey;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import com.google.crypto.tink.hybrid.HpkeParameters;
import com.google.crypto.tink.hybrid.HpkePrivateKey;
import com.google.crypto.tink.hybrid.HpkePublicKey;
import com.google.crypto.tink.subtle.EllipticCurves;
import com.google.crypto.tink.subtle.EllipticCurves.CurveType;
import com.google.crypto.tink.subtle.EllipticCurves.PointFormatType;
import com.google.crypto.tink.subtle.Hex;
import com.google.crypto.tink.util.Bytes;
import com.google.crypto.tink.util.SecretBytes;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.spec.ECPoint;

/** Test vectors for HPKE. Generated by hand from the implementation here. */
@AccessesPartialKey
public final class HpkeTestUtil {

  private HpkeTestUtil() {}

  private static Bytes getP256PublicPointAsBytes() throws GeneralSecurityException {
    return Bytes.copyFrom(
        EllipticCurves.pointEncode(
            CurveType.NIST_P256,
            PointFormatType.UNCOMPRESSED,
            new ECPoint(
                new BigInteger(
                    "60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6", 16),
                new BigInteger(
                    "7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299", 16))));
  }

  private static SecretBytes getPrivateP256Value() {
    return SecretBytes.copyFrom(
        Hex.decode("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721"),
        InsecureSecretKeyAccess.get());
  }

  private static Bytes getP384PublicPointAsBytes() throws GeneralSecurityException {
    return Bytes.copyFrom(
        EllipticCurves.pointEncode(
            CurveType.NIST_P384,
            PointFormatType.UNCOMPRESSED,
            new ECPoint(
                new BigInteger(
                    "009d92e0330dfc60ba8b2be32e10f7d2f8457678a112cafd4544b29b7e6addf0249968f54c"
                        + "732aa49bc4a38f467edb8424",
                    16),
                new BigInteger(
                    "0081a3a9c9e878b86755f018a8ec3c5e80921910af919b95f18976e35acc04efa2962e277a"
                        + "0b2c990ae92b62d6c75180ba",
                    16))));
  }

  private static SecretBytes getPrivateP384Value() {
    return SecretBytes.copyFrom(
        Hex.decode(
            "670dc60402d8a4fe52f4e552d2b71f0f81bcf195d8a71a6c7d84efb4f0e4b4a5d0f60a27c9"
                + "4caac46bdeeb79897a3ed9"),
        InsecureSecretKeyAccess.get());
  }

  private static Bytes getP521PublicPointAsBytes() throws GeneralSecurityException {
    return Bytes.copyFrom(
        EllipticCurves.pointEncode(
            CurveType.NIST_P521,
            PointFormatType.UNCOMPRESSED,
            new ECPoint(
                new BigInteger(
                    "1894550D0785932E00EAA23B694F213F8C3121F86DC97A04E5A7167DB4E5BCD3"
                        + "71123D46E45DB6B5D5370A7F20FB633155D38FFA16D2BD761DCAC474B9A2F502"
                        + "3A4",
                    16),
                new BigInteger(
                    "0493101C962CD4D2FDDF782285E64584139C2F91B47F87FF82354D6630F746A2"
                        + "8A0DB25741B5B34A828008B22ACC23F924FAAFBD4D33F81EA66956DFEAA2BFDF"
                        + "CF5",
                    16))));
  }

  private static SecretBytes getPrivateP521Value() {
    return SecretBytes.copyFrom(
        Hex.decode(
            "00FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75C"
                + "AA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83"
                + "538"),
        InsecureSecretKeyAccess.get());
  }

  private static HybridTestVector createTestVector0() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "04d7d800cab3d3c0104899e137656a3a23a58e1efe41310ea5e9ba74234494b10da4286d4baf4641c38d50"
                + "9d28cb21c4694461ccd6258864c115cf17875f59b069dffc8427cfb7f277ed4e370ae78f916e"
                + "22"));
  }

  // DHKEM_P384_HKDF_SHA384
  private static HybridTestVector createTestVector1() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP384PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP384Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "042bc538540f32860e18a5d49241179038d6edf31a1cf08009108146d70ab95503513af6220f542a6a4761"
                + "d0cbdd5377a8b2f0b9205bb1b9985045c4f08e6ed61a5087dbae277bc687ff3849f39709bcef934f"
                + "6af240d3c8073c417bf68aea8815848264ffda1173abe9e43f37203bc4093c"));
  }

  // DHKEM_P521_HKDF_SHA512
  private static HybridTestVector createTestVector2() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP521PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP521Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "04001a857acfba7c7c209794a9e1ba3bb877f4bbc6a924414a8ebd4677e06880a4dabba3e96c5aa246407d"
                + "e7ef79a52337e22db12547e765a42ee98b0bda86add36faf0023132c017661173781401e7edf2f27"
                + "d676cbf080bf697478e6b44a6820c8a0c538652e922a2ce95cb61c41e1a1f20db774a8739862213b"
                + "43de28e58e7f30d1aa2c4382cae60ea787d294b5970f0dc053adfc"));
  }

  // DHKEM_X25519_HKDF_SHA256
  private static HybridTestVector createTestVector3() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_X25519_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(
            params,
            Bytes.copyFrom(
                Hex.decode("37fda3567bdbd628e88668c3c8d7e97d1d1253b6d4ea6d44c150f741f1bf4431")),
            /* idRequirement= */ null);

    HpkePrivateKey privateKey =
        HpkePrivateKey.create(
            publicKey,
            SecretBytes.copyFrom(
                Hex.decode("52c4a758a802cd8b936eceea314432798d5baf2d7e9235dc084ab1b9cfa2f736"),
                InsecureSecretKeyAccess.get()));

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "c202f5f26a59c446531b9e4e880f8730ff0aed444699cb1cd69a2c60e07aba42d77a29b62c7af6b2cfda9c"
                + "1529bb8d23c8"));
  }

  // HKDF_SHA384
  private static HybridTestVector createTestVector4() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA384)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "0484c623788966f676b8f60bcbb80ee764abfc9b1cb1123837abd8eb4eb3a5f65c50799bb1d6ab4502e10b"
                + "76f6171288c495ffdb00f82732f05db817a4bebc63608739b27ae4d3b0263a6f155a2a7bf804"
                + "75"));
  }

  // HKDF_SHA512
  private static HybridTestVector createTestVector5() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA512)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "0497d2aacbd0988f5dcd5284ff9690aead47a405f03b558844b41153aced5c884303d3273d09a52c53f754"
                + "30ef68ebf7c6120a2412b9396a871fc0e1b6b6f0ee6bb86bbef7467aaea10274042ee7e880f5"
                + "16"));
  }

  // AES_256_GCM
  private static HybridTestVector createTestVector6() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_256_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "04b2de5915aa2bde7ad85745a632258caba46ed5be81297177dae45cdcbcf49c92431ea80763f92f6b2211"
                + "5723a7d092994d40376f7618e9f2ef82d5c44036e29eca440814ade6c8d5d9246abddaf57403"
                + "31"));
  }

  // CHACHA20_POLY1305
  private static HybridTestVector createTestVector7() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.NO_PREFIX)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.CHACHA20_POLY1305)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ null);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "04e0f41a312164058e2c36f1bc977e12a6fec8b13dc5fabc2441ec905bc432145a0a5e50929815ec6944a3"
                + "da1a186c0b9b428232086b218af061e9f814d8bd27808bce0bdb3c656d307f87ffe3bf13b0eb"
                + "19"));
  }

  // Variant.TINK
  private static HybridTestVector createTestVector8() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.TINK)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ 0x886688aa);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "01886688aa"
                + "04d7d800cab3d3c0104899e137656a3a23a58e1efe41310ea5e9ba74234494b10da4286d4baf4641"
                + "c38d509d28cb21c4694461ccd6258864c115cf17875f59b069dffc8427cfb7f277ed4e370ae78f91"
                + "6e22"));
  }

  // Variant.CRUNCHY
  private static HybridTestVector createTestVector9() throws GeneralSecurityException {
    HpkeParameters params =
        HpkeParameters.builder()
            .setVariant(HpkeParameters.Variant.CRUNCHY)
            .setKemId(HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256)
            .setKdfId(HpkeParameters.KdfId.HKDF_SHA256)
            .setAeadId(HpkeParameters.AeadId.AES_128_GCM)
            .build();
    HpkePublicKey publicKey =
        HpkePublicKey.create(params, getP256PublicPointAsBytes(), /* idRequirement= */ 0x886688aa);

    HpkePrivateKey privateKey = HpkePrivateKey.create(publicKey, getPrivateP256Value());

    return new HybridTestVector(
        privateKey,
        new byte[] {0x01},
        new byte[] {0x02},
        Hex.decode(
            "00886688aa"
                + "04d7d800cab3d3c0104899e137656a3a23a58e1efe41310ea5e9ba74234494b10da4286d4baf4641"
                + "c38d509d28cb21c4694461ccd6258864c115cf17875f59b069dffc8427cfb7f277ed4e370ae78f91"
                + "6e22"));
  }

  public static HybridTestVector[] createHpkeTestVectors() {
    return exceptionIsBug(
        () ->
            new HybridTestVector[] {
              createTestVector0(),
              createTestVector1(),
              createTestVector2(),
              createTestVector3(),
              createTestVector4(),
              createTestVector5(),
              createTestVector6(),
              createTestVector7(),
              createTestVector8(),
              createTestVector9(),
            });
  }
}
