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

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

import com.google.crypto.tink.internal.Util;
import java.security.Signature;
import java.security.spec.KeySpec;
import java.security.KeyFactory;
import java.security.spec.X509EncodedKeySpec;
import com.google.crypto.tink.signature.Ed25519Parameters;
import com.google.crypto.tink.AccessesPartialKey;
import com.google.crypto.tink.signature.Ed25519PublicKey;
import java.security.NoSuchProviderException;
import com.google.crypto.tink.internal.ConscryptUtil;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.subtle.Bytes;
import java.security.Provider;
import java.security.PublicKey;
import com.google.crypto.tink.config.internal.TinkFipsUtil;
import com.google.errorprone.annotations.Immutable;
import com.google.crypto.tink.PublicKeyVerify;

@Immutable
public final class Ed25519VerifyJce implements PublicKeyVerify
{
    public static final TinkFipsUtil.AlgorithmFipsCompatibility FIPS;
    private static final int PUBLIC_KEY_LEN = 32;
    private static final int SIGNATURE_LEN = 64;
    private static final String ALGORITHM_NAME = "Ed25519";
    private static final byte[] ed25519X509Prefix;
    private final PublicKey publicKey;
    private final byte[] outputPrefix;
    private final byte[] messageSuffix;
    private final Provider provider;
    
    static byte[] x509EncodePublicKey(final byte[] publicKey) throws GeneralSecurityException {
        if (publicKey.length != 32) {
            throw new IllegalArgumentException(String.format("Given public key's length is not %s.", 32));
        }
        return Bytes.concat(new byte[][] { Ed25519VerifyJce.ed25519X509Prefix, publicKey });
    }
    
    static Provider conscryptProvider() throws GeneralSecurityException {
        final Provider provider = ConscryptUtil.providerOrNull();
        if (provider == null) {
            throw new NoSuchProviderException("Ed25519VerifyJce requires the Conscrypt provider.");
        }
        return provider;
    }
    
    @AccessesPartialKey
    public static PublicKeyVerify create(final Ed25519PublicKey key) throws GeneralSecurityException {
        final Provider provider = conscryptProvider();
        return createWithProvider(key, provider);
    }
    
    @AccessesPartialKey
    public static PublicKeyVerify createWithProvider(final Ed25519PublicKey key, final Provider provider) throws GeneralSecurityException {
        if (!Ed25519VerifyJce.FIPS.isCompatible()) {
            throw new GeneralSecurityException("Can not use Ed25519 in FIPS-mode.");
        }
        return new Ed25519VerifyJce(key.getPublicKeyBytes().toByteArray(), key.getOutputPrefix().toByteArray(), key.getParameters().getVariant().equals(Ed25519Parameters.Variant.LEGACY) ? new byte[] { 0 } : new byte[0], provider);
    }
    
    Ed25519VerifyJce(final byte[] publicKey) throws GeneralSecurityException {
        this(publicKey, new byte[0], new byte[0], conscryptProvider());
    }
    
    private Ed25519VerifyJce(final byte[] publicKey, final byte[] outputPrefix, final byte[] messageSuffix, final Provider provider) throws GeneralSecurityException {
        if (!Ed25519VerifyJce.FIPS.isCompatible()) {
            throw new GeneralSecurityException("Can not use Ed25519 in FIPS-mode.");
        }
        final KeySpec spec = new X509EncodedKeySpec(x509EncodePublicKey(publicKey));
        final KeyFactory keyFactory = KeyFactory.getInstance("Ed25519", provider);
        this.publicKey = keyFactory.generatePublic(spec);
        this.outputPrefix = outputPrefix;
        this.messageSuffix = messageSuffix;
        this.provider = provider;
    }
    
    public static boolean isSupported() {
        final Provider provider = ConscryptUtil.providerOrNull();
        if (provider == null) {
            return false;
        }
        try {
            final KeyFactory unusedKeyFactory = KeyFactory.getInstance("Ed25519", provider);
            final Signature unusedSignature = Signature.getInstance("Ed25519", provider);
            return true;
        }
        catch (final GeneralSecurityException e) {
            return false;
        }
    }
    
    @Override
    public void verify(final byte[] signature, final byte[] data) throws GeneralSecurityException {
        if (signature.length != this.outputPrefix.length + 64) {
            throw new GeneralSecurityException(String.format("Invalid signature length: %s", 64));
        }
        if (!Util.isPrefix(this.outputPrefix, signature)) {
            throw new GeneralSecurityException("Invalid signature (output prefix mismatch)");
        }
        final Signature verifier = Signature.getInstance("Ed25519", this.provider);
        verifier.initVerify(this.publicKey);
        verifier.update(data);
        verifier.update(this.messageSuffix);
        boolean verified;
        try {
            verified = verifier.verify(signature, this.outputPrefix.length, 64);
        }
        catch (final RuntimeException ex) {
            verified = false;
        }
        if (!verified) {
            throw new GeneralSecurityException("Signature check failed.");
        }
    }
    
    static {
        FIPS = TinkFipsUtil.AlgorithmFipsCompatibility.ALGORITHM_NOT_FIPS;
        ed25519X509Prefix = new byte[] { 48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0 };
    }
}
