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

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

import java.util.Arrays;
import com.google.crypto.tink.internal.Util;
import java.security.spec.EllipticCurve;
import java.security.PublicKey;
import java.security.Signature;
import com.google.crypto.tink.internal.EllipticCurvesUtil;
import com.google.crypto.tink.subtle.SubtleUtil;
import com.google.crypto.tink.AccessesPartialKey;
import java.security.spec.ECPoint;
import java.security.spec.ECParameterSpec;
import java.security.spec.KeySpec;
import com.google.crypto.tink.subtle.EngineFactory;
import java.security.KeyFactory;
import java.security.spec.ECPublicKeySpec;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.internal.ConscryptUtil;
import com.google.crypto.tink.signature.EcdsaPublicKey;
import com.google.crypto.tink.signature.EcdsaParameters;
import com.google.crypto.tink.subtle.Enums;
import com.google.crypto.tink.internal.EnumTypeProtoConverter;
import javax.annotation.Nullable;
import java.security.Provider;
import com.google.crypto.tink.subtle.EllipticCurves;
import java.security.interfaces.ECPublicKey;
import com.google.crypto.tink.config.internal.TinkFipsUtil;
import com.google.errorprone.annotations.Immutable;
import com.google.crypto.tink.PublicKeyVerify;

@Immutable
public final class EcdsaVerifyJce implements PublicKeyVerify
{
    public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS;
    private static final byte[] EMPTY;
    private static final byte[] legacyMessageSuffix;
    private final ECPublicKey publicKey;
    private final String signatureAlgorithm;
    private final EllipticCurves.EcdsaEncoding encoding;
    private final byte[] outputPrefix;
    private final byte[] messageSuffix;
    @Nullable
    private final Provider provider;
    static final EnumTypeProtoConverter<Enums.HashType, EcdsaParameters.HashType> HASH_TYPE_CONVERTER;
    static final EnumTypeProtoConverter<EllipticCurves.EcdsaEncoding, EcdsaParameters.SignatureEncoding> ENCODING_CONVERTER;
    static final EnumTypeProtoConverter<EllipticCurves.CurveType, EcdsaParameters.CurveType> CURVE_TYPE_CONVERTER;
    
    public static PublicKeyVerify create(final EcdsaPublicKey key) throws GeneralSecurityException {
        final Provider provider = ConscryptUtil.providerOrNull();
        return createWithProviderOrNull(key, provider);
    }
    
    public static PublicKeyVerify createWithProvider(final EcdsaPublicKey key, final Provider provider) throws GeneralSecurityException {
        if (provider == null) {
            throw new NullPointerException("provider must not be null");
        }
        return createWithProviderOrNull(key, provider);
    }
    
    @AccessesPartialKey
    public static PublicKeyVerify createWithProviderOrNull(final EcdsaPublicKey key, @Nullable final Provider provider) throws GeneralSecurityException {
        final ECParameterSpec ecParams = EllipticCurves.getCurveSpec(EcdsaVerifyJce.CURVE_TYPE_CONVERTER.toProtoEnum(key.getParameters().getCurveType()));
        final ECPoint publicPoint = key.getPublicPoint();
        final ECPublicKeySpec spec = new ECPublicKeySpec(publicPoint, ecParams);
        KeyFactory keyFactory;
        if (provider != null) {
            keyFactory = KeyFactory.getInstance("EC", provider);
        }
        else {
            keyFactory = EngineFactory.KEY_FACTORY.getInstance("EC");
        }
        final ECPublicKey publicKey = (ECPublicKey)keyFactory.generatePublic(spec);
        return new EcdsaVerifyJce(publicKey, EcdsaVerifyJce.HASH_TYPE_CONVERTER.toProtoEnum(key.getParameters().getHashType()), EcdsaVerifyJce.ENCODING_CONVERTER.toProtoEnum(key.getParameters().getSignatureEncoding()), key.getOutputPrefix().toByteArray(), key.getParameters().getVariant().equals(EcdsaParameters.Variant.LEGACY) ? EcdsaVerifyJce.legacyMessageSuffix : EcdsaVerifyJce.EMPTY, provider);
    }
    
    private EcdsaVerifyJce(final ECPublicKey publicKey, final Enums.HashType hash, final EllipticCurves.EcdsaEncoding encoding, final byte[] outputPrefix, final byte[] messageSuffix, final Provider provider) throws GeneralSecurityException {
        if (!EcdsaVerifyJce.FIPS.isCompatible()) {
            throw new GeneralSecurityException("Can not use ECDSA in FIPS-mode, as BoringCrypto is not available.");
        }
        this.signatureAlgorithm = SubtleUtil.toEcdsaAlgo(hash);
        this.publicKey = publicKey;
        this.encoding = encoding;
        this.outputPrefix = outputPrefix;
        this.messageSuffix = messageSuffix;
        this.provider = provider;
    }
    
    public EcdsaVerifyJce(final ECPublicKey publicKey, final Enums.HashType hash, final EllipticCurves.EcdsaEncoding encoding) throws GeneralSecurityException {
        this(publicKey, hash, encoding, EcdsaVerifyJce.EMPTY, EcdsaVerifyJce.EMPTY, ConscryptUtil.providerOrNull());
        EllipticCurvesUtil.checkPointOnCurve(publicKey.getW(), publicKey.getParams().getCurve());
    }
    
    private Signature getInstance(final String signatureAlgorithm) throws GeneralSecurityException {
        if (this.provider != null) {
            return Signature.getInstance(signatureAlgorithm, this.provider);
        }
        return EngineFactory.SIGNATURE.getInstance(signatureAlgorithm);
    }
    
    private void noPrefixVerify(final byte[] signature, final byte[] data) throws GeneralSecurityException {
        byte[] derSignature = signature;
        if (this.encoding == EllipticCurves.EcdsaEncoding.IEEE_P1363) {
            final EllipticCurve curve = this.publicKey.getParams().getCurve();
            if (signature.length != 2 * EllipticCurves.fieldSizeInBytes(curve)) {
                throw new GeneralSecurityException("Invalid signature");
            }
            derSignature = EllipticCurves.ecdsaIeee2Der(signature);
        }
        if (!EllipticCurves.isValidDerEncoding(derSignature)) {
            throw new GeneralSecurityException("Invalid signature");
        }
        final Signature verifier = this.getInstance(this.signatureAlgorithm);
        verifier.initVerify(this.publicKey);
        verifier.update(data);
        if (this.messageSuffix.length > 0) {
            verifier.update(this.messageSuffix);
        }
        boolean verified = false;
        try {
            verified = verifier.verify(derSignature);
        }
        catch (final RuntimeException ex) {
            verified = false;
        }
        if (!verified) {
            throw new GeneralSecurityException("Invalid signature");
        }
    }
    
    @Override
    public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException {
        if (this.outputPrefix.length == 0) {
            this.noPrefixVerify(signature, data);
            return;
        }
        if (!Util.isPrefix(this.outputPrefix, signature)) {
            throw new GeneralSecurityException("Invalid signature (output prefix mismatch)");
        }
        final byte[] signatureNoPrefix = Arrays.copyOfRange(signature, this.outputPrefix.length, signature.length);
        this.noPrefixVerify(signatureNoPrefix, data);
    }
    
    static {
        FIPS = TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_REQUIRES_BORINGCRYPTO;
        EMPTY = new byte[0];
        legacyMessageSuffix = new byte[] { 0 };
        HASH_TYPE_CONVERTER = EnumTypeProtoConverter.builder().add(Enums.HashType.SHA256, EcdsaParameters.HashType.SHA256).add(Enums.HashType.SHA384, EcdsaParameters.HashType.SHA384).add(Enums.HashType.SHA512, EcdsaParameters.HashType.SHA512).build();
        ENCODING_CONVERTER = EnumTypeProtoConverter.builder().add(EllipticCurves.EcdsaEncoding.IEEE_P1363, EcdsaParameters.SignatureEncoding.IEEE_P1363).add(EllipticCurves.EcdsaEncoding.DER, EcdsaParameters.SignatureEncoding.DER).build();
        CURVE_TYPE_CONVERTER = EnumTypeProtoConverter.builder().add(EllipticCurves.CurveType.NIST_P256, EcdsaParameters.CurveType.NIST_P256).add(EllipticCurves.CurveType.NIST_P384, EcdsaParameters.CurveType.NIST_P384).add(EllipticCurves.CurveType.NIST_P521, EcdsaParameters.CurveType.NIST_P521).build();
    }
}
