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

package com.google.crypto.tink.hybrid;

import com.google.crypto.tink.Parameters;
import com.google.crypto.tink.Key;
import com.google.errorprone.annotations.RestrictedApi;
import com.google.crypto.tink.AccessesPartialKey;
import com.google.crypto.tink.InsecureSecretKeyAccess;
import java.security.spec.ECPoint;
import java.math.BigInteger;
import java.util.Arrays;
import com.google.crypto.tink.subtle.X25519;
import com.google.crypto.tink.internal.EllipticCurvesUtil;
import com.google.crypto.tink.internal.BigIntegerEncoding;
import com.google.crypto.tink.subtle.EllipticCurves;
import java.security.spec.ECParameterSpec;
import java.security.GeneralSecurityException;
import com.google.crypto.tink.util.SecretBytes;
import com.google.errorprone.annotations.Immutable;

@Immutable
public final class HpkePrivateKey extends HybridPrivateKey
{
    private final HpkePublicKey publicKey;
    private final SecretBytes privateKeyBytes;
    
    private HpkePrivateKey(final HpkePublicKey publicKey, final SecretBytes privateKeyBytes) {
        this.publicKey = publicKey;
        this.privateKeyBytes = privateKeyBytes;
    }
    
    private static void validatePrivateKeyByteLength(final HpkeParameters.KemId kemId, final SecretBytes privateKeyBytes) throws GeneralSecurityException {
        final int keyLengthInBytes = privateKeyBytes.size();
        final String parameterizedErrorMessage = "Encoded private key byte length for " + kemId + " must be %d, not " + keyLengthInBytes;
        if (kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256) {
            if (keyLengthInBytes != 32) {
                throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 32));
            }
        }
        else if (kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384) {
            if (keyLengthInBytes != 48) {
                throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 48));
            }
        }
        else if (kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512) {
            if (keyLengthInBytes != 66) {
                throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 66));
            }
        }
        else {
            if (kemId != HpkeParameters.KemId.DHKEM_X25519_HKDF_SHA256) {
                throw new GeneralSecurityException("Unable to validate private key length for " + kemId);
            }
            if (keyLengthInBytes != 32) {
                throw new GeneralSecurityException(String.format(parameterizedErrorMessage, 32));
            }
        }
    }
    
    private static boolean isNistKem(final HpkeParameters.KemId kemId) {
        return kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256 || kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384 || kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512;
    }
    
    private static ECParameterSpec getNistCurveParams(final HpkeParameters.KemId kemId) {
        if (kemId == HpkeParameters.KemId.DHKEM_P256_HKDF_SHA256) {
            return EllipticCurves.getNistP256Params();
        }
        if (kemId == HpkeParameters.KemId.DHKEM_P384_HKDF_SHA384) {
            return EllipticCurves.getNistP384Params();
        }
        if (kemId == HpkeParameters.KemId.DHKEM_P521_HKDF_SHA512) {
            return EllipticCurves.getNistP521Params();
        }
        throw new IllegalArgumentException("Unable to determine NIST curve params for " + kemId);
    }
    
    private static void validateKeyPair(final HpkeParameters.KemId kemId, final byte[] publicKeyBytes, final byte[] privateKeyBytes) throws GeneralSecurityException {
        if (isNistKem(kemId)) {
            final ECParameterSpec spec = getNistCurveParams(kemId);
            final BigInteger order = spec.getOrder();
            final BigInteger privateKey = BigIntegerEncoding.fromUnsignedBigEndianBytes(privateKeyBytes);
            if (privateKey.signum() <= 0 || privateKey.compareTo(order) >= 0) {
                throw new GeneralSecurityException("Invalid private key.");
            }
            final ECPoint expectedPoint = EllipticCurvesUtil.multiplyByGenerator(privateKey, spec);
            final ECPoint publicPoint = EllipticCurves.pointDecode(spec.getCurve(), EllipticCurves.PointFormatType.UNCOMPRESSED, publicKeyBytes);
            if (!expectedPoint.equals(publicPoint)) {
                throw new GeneralSecurityException("Invalid private key for public key.");
            }
        }
        else {
            if (kemId != HpkeParameters.KemId.DHKEM_X25519_HKDF_SHA256) {
                throw new IllegalArgumentException("Unable to validate key pair for " + kemId);
            }
            final byte[] expectedPublicKeyBytes = X25519.publicFromPrivate(privateKeyBytes);
            if (!Arrays.equals(expectedPublicKeyBytes, publicKeyBytes)) {
                throw new GeneralSecurityException("Invalid private key for public key.");
            }
        }
    }
    
    @AccessesPartialKey
    @RestrictedApi(explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", allowedOnPath = ".*Test\\.java", allowlistAnnotations = { AccessesPartialKey.class })
    public static HpkePrivateKey create(final HpkePublicKey publicKey, final SecretBytes privateKeyBytes) throws GeneralSecurityException {
        if (publicKey == null) {
            throw new GeneralSecurityException("HPKE private key cannot be constructed without an HPKE public key");
        }
        if (privateKeyBytes == null) {
            throw new GeneralSecurityException("HPKE private key cannot be constructed without secret");
        }
        validatePrivateKeyByteLength(publicKey.getParameters().getKemId(), privateKeyBytes);
        validateKeyPair(publicKey.getParameters().getKemId(), publicKey.getPublicKeyBytes().toByteArray(), privateKeyBytes.toByteArray(InsecureSecretKeyAccess.get()));
        return new HpkePrivateKey(publicKey, privateKeyBytes);
    }
    
    @RestrictedApi(explanation = "Accessing parts of keys can produce unexpected incompatibilities, annotate the function with @AccessesPartialKey", link = "https://developers.google.com/tink/design/access_control#accessing_partial_keys", allowedOnPath = ".*Test\\.java", allowlistAnnotations = { AccessesPartialKey.class })
    public SecretBytes getPrivateKeyBytes() {
        return this.privateKeyBytes;
    }
    
    @Override
    public HpkeParameters getParameters() {
        return this.publicKey.getParameters();
    }
    
    @Override
    public HpkePublicKey getPublicKey() {
        return this.publicKey;
    }
    
    @Override
    public boolean equalsKey(final Key o) {
        if (!(o instanceof HpkePrivateKey)) {
            return false;
        }
        final HpkePrivateKey other = (HpkePrivateKey)o;
        return this.publicKey.equalsKey(other.publicKey) && this.privateKeyBytes.equalsSecretBytes(other.privateKeyBytes);
    }
}
