package com.sunyard.chsm.sdf.adapter;

import com.sunyard.chsm.sdf.model.DeviceInfo;
import com.sunyard.chsm.sdf.model.EccKey;
import com.sunyard.chsm.sdf.util.LangUtils;
import com.sunyard.chsm.utils.gm.BCECUtils;
import com.sunyard.chsm.utils.gm.BCSM2Utils;
import lombok.SneakyThrows;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;

import java.math.BigInteger;
import java.security.KeyPair;
import java.security.SecureRandom;

/**
 * @author liulu
 * @since 2024/11/5
 */
public class BcSdfApiAdaptor implements SdfApiAdapter {


    private static final DeviceInfo deviceInfo;

    static {
        deviceInfo = new DeviceInfo();
        deviceInfo.setIssuerName("BC");
        deviceInfo.setDeviceName("BC-3000");
        deviceInfo.setDeviceSerial("BC202411051037");
        deviceInfo.setDeviceVersion(1);
        deviceInfo.setStandardVersion(1);
        deviceInfo.setAsymAlgAbility(new long[]{7493065891348563935L, 3000543215027029126L});
        deviceInfo.setSymAlgAbility(2782707529341253316L);
        deviceInfo.setHashAlgAbility(7010608454676760881L);
    }

    @Override
    public String openDevice() {
        return "c95a78d9c04a557b7b46dbcb5f36cc66";
    }

    @Override
    public boolean closeDevice(String deviceHandle) {
        return true;
    }

    @Override
    public String openSession(String deviceHandle) {
        return "6975feaffaa35b31b6d4e4555ac403a1";
    }

    @Override
    public void closeSession(String sessionHandle) {

    }

    @Override
    public DeviceInfo getDeviceInfo(String sessionHandle) {
        return deviceInfo;
    }

    @Override
    public byte[] generateRandom(String sessionHandle, int uiLength) {
        byte[] res = new byte[uiLength];
        new SecureRandom().nextBytes(res);
        return res;
    }

    @Override
    public byte[] exportEncPublicKeyECC(String sessionHandle, int uiKeyIndex) {
        BigInteger d = new BigInteger(1, getD());
        ECPoint q = BCSM2Utils.G_POINT.multiply(d).normalize();
        return LangUtils.merge(q.getXCoord().getEncoded(), q.getYCoord().getEncoded());
    }

    private byte[] getD() {
        return LangUtils.merge(LangUtils.toByteArray(deviceInfo.getAsymAlgAbility()[0]),
                LangUtils.toByteArray(deviceInfo.getAsymAlgAbility()[1]),
                LangUtils.toByteArray(deviceInfo.getSymAlgAbility()),
                LangUtils.toByteArray(deviceInfo.getHashAlgAbility()));
    }

    @SneakyThrows
    @Override
    public EccKey generateKeyPairECC(String sessionHandle, String alg, int uiKeyBits) {
        // 生成密钥对
        KeyPair keyPair = BCSM2Utils.generateKeyPair();
        BCECPublicKey pubKey = (BCECPublicKey) keyPair.getPublic();
        BCECPrivateKey priKey = (BCECPrivateKey) keyPair.getPrivate();
        byte[] x = pubKey.getQ().getXCoord().getEncoded();
        byte[] y = pubKey.getQ().getYCoord().getEncoded();
        byte[] d = BigIntegers.asUnsignedByteArray(32, priKey.getD());
        return new EccKey(LangUtils.merge(x, y), d);
    }

    @Override
    public byte[] exchangeDigitEnvelopeBaseOnECC(String sessionHandle, int uiKeyIndex, byte[] pubKey, byte[] pucEncDateIn) {
        return new byte[0];
    }

    @Override
    public byte[] externalEncryptECC(String sessionHandle, byte[] pubKey, byte[] pucData) {
        if (pubKey[0] == 4) {
            pubKey = Arrays.copyOfRange(pubKey, 1, 65);
        }
        ECPublicKeyParameters parameters = BCECUtils.createECPublicKeyParameters(
                Arrays.copyOfRange(pubKey, 0, 32),
                Arrays.copyOfRange(pubKey, 32, 64)
        );
        try {
            byte[] encrypt = BCSM2Utils.encrypt(parameters, pucData);
            return Arrays.copyOfRange(encrypt, 1, encrypt.length);
        } catch (InvalidCipherTextException e) {
            throw new IllegalArgumentException(e);
        }
    }


}
