// 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
//
//      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.signature;

import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;

import com.google.crypto.tink.KeysetHandle;
import com.google.crypto.tink.PublicKeyVerify;
import com.google.crypto.tink.RegistryConfiguration;
import com.google.crypto.tink.TinkProtoKeysetFormat;
import com.google.crypto.tink.internal.KeyManagerRegistry;
import com.google.crypto.tink.signature.internal.testing.RsaSsaPssTestUtil;
import com.google.crypto.tink.signature.internal.testing.SignatureTestVector;
import com.google.crypto.tink.subtle.Hex;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Unit tests for RsaSsaPssVerifyKeyManager. */
@RunWith(JUnit4.class)
public class RsaSsaPssVerifyKeyManagerTest {
  @BeforeClass
  public static void beforeClass() throws Exception {
    RsaSsaPssSignKeyManager.registerPair(/* newKeyAllowed= */ true);
    PublicKeySignWrapper.register();
    PublicKeyVerifyWrapper.register();
  }

  static class NistTestVector {
    byte[] msg;
    byte[] sig;
    RsaSsaPssPublicKey publicKey;

    public NistTestVector(
        String modulusHex,
        String exponentHex,
        String msg,
        String sig,
        RsaSsaPssParameters.HashType sigHash,
        RsaSsaPssParameters.HashType mgf1Hash,
        int saltLength)
        throws Exception {
      this.msg = Hex.decode(msg);
      this.sig = Hex.decode(sig);
      BigInteger modulus = new BigInteger(modulusHex, 16);
      BigInteger exponent = new BigInteger(exponentHex, 16);
      RsaSsaPssParameters parameters =
          RsaSsaPssParameters.builder()
              .setModulusSizeBits(modulus.bitLength())
              .setPublicExponent(exponent)
              .setVariant(RsaSsaPssParameters.Variant.NO_PREFIX)
              .setMgf1HashType(mgf1Hash)
              .setSigHashType(sigHash)
              .setSaltLengthBytes(saltLength)
              .build();
      this.publicKey =
          RsaSsaPssPublicKey.builder().setParameters(parameters).setModulus(modulus).build();
    }
  }

  // Test vector from
  // https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Digital-Signatures
  static NistTestVector[] nistTestVectors;

  @BeforeClass
  public static void setUpNistTestVectors() throws Exception {
    nistTestVectors =
        new NistTestVector[] {
          new NistTestVector(
              "a47d04e7cacdba4ea26eca8a4c6e14563c2ce03b623b768c0d49868a57121301dbf783d82f4c055e73960e70550187d0af62ac3496f0a3d9103c2eb7919a72752fa7ce8c688d81e3aee99468887a15288afbb7acb845b7c522b5c64e678fcd3d22feb84b44272700be527d2b2025a3f83c2383bf6a39cf5b4e48b3cf2f56eef0dfff18555e31037b915248694876f3047814415164f2c660881e694b58c28038a032ad25634aad7b39171dee368e3d59bfb7299e4601d4587e68caaf8db457b75af42fc0cf1ae7caced286d77fac6cedb03ad94f1433d2c94d08e60bc1fdef0543cd2951e765b38230fdd18de5d2ca627ddc032fe05bbd2ff21e2db1c2f94d8b",
              "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010e43f",
              "e002377affb04f0fe4598de9d92d31d6c786040d5776976556a2cfc55e54a1dcb3cb1b126bd6a4bed2a184990ccea773fcc79d246553e6c64f686d21ad4152673cafec22aeb40f6a084e8a5b4991f4c64cf8a927effd0fd775e71e8329e41fdd4457b3911173187b4f09a817d79ea2397fc12dfe3d9c9a0290c8ead31b6690a6",
              "4f9b425c2058460e4ab2f5c96384da2327fd29150f01955a76b4efe956af06dc08779a374ee4607eab61a93adc5608f4ec36e47f2a0f754e8ff839a8a19b1db1e884ea4cf348cd455069eb87afd53645b44e28a0a56808f5031da5ba9112768dfbfca44ebe63a0c0572b731d66122fb71609be1480faa4e4f75e43955159d70f081e2a32fbb19a48b9f162cf6b2fb445d2d6994bc58910a26b5943477803cdaaa1bd74b0da0a5d053d8b1dc593091db5388383c26079f344e2aea600d0e324164b450f7b9b465111b7265f3b1b063089ae7e2623fc0fda8052cf4bf3379102fbf71d7c98e8258664ceed637d20f95ff0111881e650ce61f251d9c3a629ef222d",
              RsaSsaPssParameters.HashType.SHA256,
              RsaSsaPssParameters.HashType.SHA256,
              32),
          new NistTestVector(
              "99a5c8d094a5f917034667a0408b7ecfcaacc3f9784444e21773c3461ec355f0d0f52a5db0568a71d388696788ef66ae7340c6b28dbf925fe83557986575f79cca69217221397ed5808a26f7e7e714c93235f914d45c4a9af4619b20f511ad644bd3412dfdf0ff717f7aac746f310bfa9a141ac3dbf01c1fc74febd197938419c262293505c35f402f9053ad13c51a5960ecde55ec829e953f941af733e58705913767e7a7200d1d09e7e7e2d269fa29a558bb16304b059f13f4ca560a8101fe3720b4a779ec126427326caa132a3d3611d7dbc50336fac789ec406b397e1e36d7daf9b624bf639c82b859288747690c730c980b2f5a239dd95ad5389a2ec90c5778604713710383ae55d4d28c06d4ac26f0d1231f1d6762c8e0d918118156bc637760daea184746b8dcf6f61db274a7ddceaa074937ababad4549b97ab992494a807208abd789823f5d75c4b994089c8072cfc254e0d8202fd896476e96ad9d309a0e8e7301282f07eb2ae8edefb7dbbe13b96e8b4024c6b84de0a05e150285",
              "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000008a649",
              "cc21593a6a0f737e2970b7c07984b070d761726296a07e24e056e68ff846b29cc1548179843d74dcee86479858b2c16e4cb84f2544b4ecdcb4dd43a04bb7183a768ae44a2712bf9ad47883acc2812f958306890ebea408c92eb4f001ed7dbf55f3a9c8d6d9f61e5fe32eb3253e59c18e863169478cd69b9155c335db66016f96",
              "0aa572a6845b870b8909a683bb7e6e7616f77beff28746116d8bc4b7335546b51e8006ed0fc9a0d66f63ce0b9ebf792d7efd4305d7624d545400a5fd6a06b78f174b86803f7cd1cc93e3a97286f0ea590e40ff26195aa219fe1510a016785223606d9311a16c59a8fe4a6da6ecd0c1d7775039290c2aaa17ed1eb1b54374f7e572db13cca3a638575f8004aa54a2fa98422fc07e43ad3a20dd93001493442677d883914dc74ec1cbebbbd3d2b6bad4666d91457b69b46a1a61f21298f1a67942ec86c876322dd366ed167814e9c8fc9040c5b4b7a859bbd880cb6bc241b9e327ce779e0783b1cf445e0b2f5771b3f5822a1364391c154dc506fff1fb9d9a35f80199a6b30b4b92b92619a40e21aea19284015863c44866c61ed904a7ad19ee04d966c0aae390636243565581ff20bd6e3cfb6e31f5afba964b311dc2d023a21998c8dd50ca453699190bd467429e2f88ace29c4d1da4da61aac1eda2380230aa8dbb63c75a3c1ec04da3a1f880c9c747acdb74a8395af58f5f044015ccaf6e94",
              RsaSsaPssParameters.HashType.SHA512,
              RsaSsaPssParameters.HashType.SHA512,
              0),
        };
  }

  @Test
  public void testNistTestVector() throws Exception {
    for (NistTestVector t : nistTestVectors) {
      KeysetHandle handle =
          KeysetHandle.newBuilder()
              .addEntry(KeysetHandle.importKey(t.publicKey).withRandomId().makePrimary())
              .build();
      PublicKeyVerify verifier =
          handle.getPrimitive(RegistryConfiguration.get(), PublicKeyVerify.class);
      try {
        verifier.verify(t.sig, t.msg);
      } catch (GeneralSecurityException e) {
        fail("Valid signature, should not throw exception" + e);
      }
    }
  }

  @Test
  public void test_serializeAndParse_works() throws Exception {
    SignatureTestVector testVector = RsaSsaPssTestUtil.createRsaPssTestVectors()[0];
    RsaSsaPssPublicKey key = (RsaSsaPssPublicKey) testVector.getPrivateKey().getPublicKey();
    KeysetHandle.Builder.Entry entry = KeysetHandle.importKey(key).withFixedId(1951).makePrimary();
    KeysetHandle handle = KeysetHandle.newBuilder().addEntry(entry).build();

    byte[] serializedHandle = TinkProtoKeysetFormat.serializeKeysetWithoutSecret(handle);
    KeysetHandle parsedHandle = TinkProtoKeysetFormat.parseKeysetWithoutSecret(serializedHandle);
    assertThat(parsedHandle.equalsKeyset(handle)).isTrue();
  }

  @Test
  public void testKeyManagerRegistered() throws Exception {
    assertThat(
            KeyManagerRegistry.globalInstance()
                .getKeyManager(
                    "type.googleapis.com/google.crypto.tink.RsaSsaPssPublicKey",
                    PublicKeyVerify.class))
        .isNotNull();
  }
}
