// 
// Decompiled by Procyon v0.6.0
// 

package com.google.crypto.tink.signature.internal;

import java.security.PublicKey;
import java.security.Signature;
import com.google.crypto.tink.AccessesPartialKey;
import java.security.spec.KeySpec;
import java.security.spec.RSAPublicKeySpec;
import java.security.KeyFactory;
import java.security.NoSuchProviderException;
import com.google.crypto.tink.signature.RsaSsaPssPublicKey;
import com.google.crypto.tink.subtle.Validators;
import java.security.GeneralSecurityException;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.MGF1ParameterSpec;
import com.google.crypto.tink.signature.RsaSsaPssParameters;
import javax.annotation.Nullable;
import com.google.crypto.tink.internal.ConscryptUtil;
import com.google.crypto.tink.internal.Util;
import java.security.Provider;
import java.security.spec.PSSParameterSpec;
import java.security.interfaces.RSAPublicKey;
import com.google.crypto.tink.config.internal.TinkFipsUtil;
import com.google.errorprone.annotations.Immutable;
import com.google.crypto.tink.PublicKeyVerify;

@Immutable
public final class RsaSsaPssVerifyConscrypt implements PublicKeyVerify
{
    public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS;
    private static final byte[] EMPTY;
    private static final byte[] legacyMessageSuffix;
    private static final String MGF_1 = "MGF1";
    private static final int TRAILER_FIELD_BC = 1;
    private final RSAPublicKey publicKey;
    private final String signatureAlgorithm;
    private final PSSParameterSpec parameterSpec;
    private final byte[] outputPrefix;
    private final byte[] messageSuffix;
    private final Provider conscrypt;
    
    @Nullable
    static Provider conscryptProviderOrNull() {
        if (Util.isAndroid() && Util.getAndroidApiLevel() <= 23) {
            return null;
        }
        return ConscryptUtil.providerOrNull();
    }
    
    static String getConscryptRsaSsaPssAlgo(final RsaSsaPssParameters.HashType hash) {
        if (hash == RsaSsaPssParameters.HashType.SHA256) {
            return "SHA256withRSA/PSS";
        }
        if (hash == RsaSsaPssParameters.HashType.SHA384) {
            return "SHA384withRSA/PSS";
        }
        if (hash == RsaSsaPssParameters.HashType.SHA512) {
            return "SHA512withRSA/PSS";
        }
        throw new IllegalArgumentException("Unsupported hash: " + hash);
    }
    
    private static String getMdName(final RsaSsaPssParameters.HashType sigHash) {
        if (sigHash == RsaSsaPssParameters.HashType.SHA256) {
            return "SHA-256";
        }
        if (sigHash == RsaSsaPssParameters.HashType.SHA384) {
            return "SHA-384";
        }
        if (sigHash == RsaSsaPssParameters.HashType.SHA512) {
            return "SHA-512";
        }
        throw new IllegalArgumentException("Unsupported MD hash: " + sigHash);
    }
    
    private static MGF1ParameterSpec getMgf1Hash(final RsaSsaPssParameters.HashType mgf1Hash) {
        if (mgf1Hash == RsaSsaPssParameters.HashType.SHA256) {
            return MGF1ParameterSpec.SHA256;
        }
        if (mgf1Hash == RsaSsaPssParameters.HashType.SHA384) {
            return MGF1ParameterSpec.SHA384;
        }
        if (mgf1Hash == RsaSsaPssParameters.HashType.SHA512) {
            return MGF1ParameterSpec.SHA512;
        }
        throw new IllegalArgumentException("Unsupported MGF1 hash: " + mgf1Hash);
    }
    
    static PSSParameterSpec getPssParameterSpec(final RsaSsaPssParameters.HashType sigHash, final RsaSsaPssParameters.HashType mgf1Hash, final int saltLength) {
        return new PSSParameterSpec(getMdName(sigHash), "MGF1", getMgf1Hash(mgf1Hash), saltLength, 1);
    }
    
    private RsaSsaPssVerifyConscrypt(final RSAPublicKey pubKey, final RsaSsaPssParameters.HashType sigHash, final RsaSsaPssParameters.HashType mgf1Hash, final int saltLength, final byte[] outputPrefix, final byte[] messageSuffix, final Provider conscrypt) throws GeneralSecurityException {
        if (!RsaSsaPssVerifyConscrypt.FIPS.isCompatible()) {
            throw new GeneralSecurityException("Cannot use RSA SSA PSS in FIPS-mode, as BoringCrypto module is not available.");
        }
        if (!sigHash.equals(mgf1Hash)) {
            throw new GeneralSecurityException("sigHash and mgf1Hash must be the same");
        }
        Validators.validateRsaModulusSize(pubKey.getModulus().bitLength());
        Validators.validateRsaPublicExponent(pubKey.getPublicExponent());
        this.publicKey = pubKey;
        this.signatureAlgorithm = getConscryptRsaSsaPssAlgo(sigHash);
        this.parameterSpec = getPssParameterSpec(sigHash, mgf1Hash, saltLength);
        this.outputPrefix = outputPrefix;
        this.messageSuffix = messageSuffix;
        this.conscrypt = conscrypt;
    }
    
    public static PublicKeyVerify create(final RsaSsaPssPublicKey key) throws GeneralSecurityException {
        final Provider conscrypt = conscryptProviderOrNull();
        return createWithProvider(key, conscrypt);
    }
    
    @AccessesPartialKey
    public static PublicKeyVerify createWithProvider(final RsaSsaPssPublicKey key, final Provider conscrypt) throws GeneralSecurityException {
        if (conscrypt == null) {
            throw new NoSuchProviderException("RSA SSA PSS using Conscrypt is not supported.");
        }
        final KeyFactory keyFactory = KeyFactory.getInstance("RSA", conscrypt);
        final RSAPublicKey publicKey = (RSAPublicKey)keyFactory.generatePublic(new RSAPublicKeySpec(key.getModulus(), key.getParameters().getPublicExponent()));
        final RsaSsaPssParameters params = key.getParameters();
        return new RsaSsaPssVerifyConscrypt(publicKey, params.getSigHashType(), params.getMgf1HashType(), params.getSaltLengthBytes(), key.getOutputPrefix().toByteArray(), key.getParameters().getVariant().equals(RsaSsaPssParameters.Variant.LEGACY) ? RsaSsaPssVerifyConscrypt.legacyMessageSuffix : RsaSsaPssVerifyConscrypt.EMPTY, conscrypt);
    }
    
    @Override
    public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException {
        if (!Util.isPrefix(this.outputPrefix, signature)) {
            throw new GeneralSecurityException("Invalid signature (output prefix mismatch)");
        }
        final Signature verifier = Signature.getInstance(this.signatureAlgorithm, this.conscrypt);
        verifier.initVerify(this.publicKey);
        verifier.setParameter(this.parameterSpec);
        verifier.update(data);
        if (this.messageSuffix.length > 0) {
            verifier.update(this.messageSuffix);
        }
        if (!verifier.verify(signature, this.outputPrefix.length, signature.length - this.outputPrefix.length)) {
            throw new GeneralSecurityException("signature verification failed");
        }
    }
    
    static {
        FIPS = TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;
        EMPTY = new byte[0];
        legacyMessageSuffix = new byte[] { 0 };
    }
}
