package sdf;

import com.sunyard.chsm.enums.ManufacturerModelEnum;
import com.sunyard.chsm.enums.Padding;
import com.sunyard.chsm.sdf.SdfApiService;
import com.sunyard.chsm.sdf.SingleSdfApiService;
import com.sunyard.chsm.sdf.adapter.SdfApiAdapterFactory;
import com.sunyard.chsm.sdf.context.AlgId;
import com.sunyard.chsm.sdf.model.EccCipher;
import com.sunyard.chsm.sdf.model.EccKey;
import com.sunyard.chsm.sdf.model.EccPriKey;
import com.sunyard.chsm.sdf.model.EccPubKey;
import com.sunyard.chsm.sdf.model.EccSignature;
import com.sunyard.chsm.utils.CodecUtils;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

import java.util.Arrays;

/**
 * @author liulu
 * @since 2024/12/13
 */
@Slf4j
public class SdfApiServiceTest {

    private final static byte[] symKey = "nhkdhaksd4678787".getBytes();
    private final static byte[] iv = "hjdashde83252i23".getBytes();
    private final static String plain = "hello sdf api ,hello sdf api !&!----->";
    private final static String ip1 = "172.16.18.41";
    private final static int port = 8889;
    private static final SdfApiService bcService = new SingleSdfApiService(SdfApiAdapterFactory.getBcAdapter());
    private static final SdfApiService sdfService = new SingleSdfApiService(SdfApiAdapterFactory.newInstance(ManufacturerModelEnum.enc001.getModel(), ip1, port));

    private static EccPubKey pubKey;
    private static EccPriKey priKey;


    @BeforeAll
    public static void before() throws Exception {
        EccKey eccKey = sdfService.genKeyPairEcc();
        pubKey = EccPubKey.fromBytes(eccKey.getPubKey());
        priKey = EccPriKey.fromBytes(eccKey.getPriKey());
        log.info("public key: {}", pubKey.getPubKeyHex());
        log.info("private key: {}", CodecUtils.encodeHex(priKey.getD()));
    }

    @Test
    public void testRandom() {
        byte[] sdfRandom = sdfService.generateRandom(64);
        log.info("sdf Random: {}", CodecUtils.encodeHex(sdfRandom));
        Assertions.assertEquals(64, sdfRandom.length);

        byte[] bcRandom = bcService.generateRandom(64);
        log.info("bc Random: {}", CodecUtils.encodeHex(bcRandom));
        Assertions.assertEquals(64, bcRandom.length);

        Assertions.assertFalse(Arrays.equals(sdfRandom, bcRandom));
    }

    @Test
    public void testSM2EncAndDec() {
        EccCipher sdfCipher = sdfService.externalEncryptECC(pubKey.getPubKeyBytes(), plain.getBytes());
        log.info("sdf sm2 cipher: {}", sdfCipher.getC1C3C2Hex());
        byte[] bcPlain = bcService.externalDecryptECC(priKey.getD(), sdfCipher.getC1C3C2Bytes());
        log.info("bc sm2 plain: {}", new String(bcPlain));
        Assertions.assertEquals(plain, new String(bcPlain));

        EccCipher bcCipher = bcService.externalEncryptECC(pubKey.getPubKeyBytes(), plain.getBytes());
        log.info("bc sm2 cipher: {}", bcCipher.getC1C3C2Hex());
        byte[] sm2Plain = sdfService.externalDecryptECC(priKey.getD(), bcCipher.getC1C3C2Bytes());
        log.info("sdf sm2 plain: {}", new String(sm2Plain));
        Assertions.assertEquals(plain, new String(sm2Plain));
    }

    @Test
    public void testSM2SignAndVerify() {
        EccSignature bcSign = bcService.externalSignECC(priKey.getD(), plain.getBytes());
        log.info("bc sm2 signature: {}", bcSign.getRawSignHex());
        boolean sdfVerified = sdfService.externalVerifyECC(pubKey.getPubKeyBytes(), plain.getBytes(), bcSign.getDerSignBytes());
        log.info("sdf sm2 verified: {}", sdfVerified);
        Assertions.assertTrue(sdfVerified);

        EccSignature sdfSign = sdfService.externalSignECC(priKey.getD(), plain.getBytes());
        log.info("sdf sm2 signature: {}", sdfSign.getRawSignHex());
        boolean bcVerified = bcService.externalVerifyECC(pubKey.getPubKeyBytes(), plain.getBytes(), sdfSign.getRawSignBytes());
        log.info("bc sm2 verified: {}", bcVerified);
        Assertions.assertTrue(bcVerified);
    }


    @Test
    public void testSymEncAndDec() {
        byte[] ecbCipher = sdfService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, plain.getBytes());
        byte[] ecbPlain = sdfService.symDecrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, ecbCipher);
        log.info("ecb_cipher: {}", CodecUtils.encodeHex(ecbCipher));
        Assertions.assertEquals(plain, new String(ecbPlain));

        byte[] cbcCipher = sdfService.symEncrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
        log.info("cbc_cipher: {}", CodecUtils.encodeHex(cbcCipher));
        byte[] cbcPlain = sdfService.symDecrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, cbcCipher);
        Assertions.assertEquals(plain, new String(cbcPlain));

        Assertions.assertArrayEquals(ecbPlain, cbcPlain);
        Assertions.assertNotEquals(CodecUtils.encodeHex(ecbCipher), CodecUtils.encodeHex(cbcCipher));


        byte[] bcEcbCipher = bcService.symEncrypt(AlgId.SGD_SM4_ECB, Padding.PCKS7Padding, symKey, null, plain.getBytes());
        log.info("bc_ecb_cipher: {}", CodecUtils.encodeHex(bcEcbCipher));
        Assertions.assertArrayEquals(ecbCipher, bcEcbCipher);
        byte[] bcCbcCipher = bcService.symEncrypt(AlgId.SGD_SM4_CBC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
        log.info("bc_cbc_cipher: {}", CodecUtils.encodeHex(bcCbcCipher));
        Assertions.assertArrayEquals(cbcCipher, bcCbcCipher);
    }

    @Test
    public void testSm4Mac() {
        byte[] sdfMac = sdfService.calculateMAC(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
        log.info("sdf mac: {}", CodecUtils.encodeHex(sdfMac));
        Assertions.assertEquals(16, sdfMac.length);

        byte[] bcMac = bcService.calculateMAC(AlgId.SGD_SM4_MAC, Padding.PCKS7Padding, symKey, iv, plain.getBytes());
        log.info("bc mac: {}", CodecUtils.encodeHex(bcMac));
        Assertions.assertEquals(16, bcMac.length);

        Assertions.assertArrayEquals(sdfMac, bcMac);
    }

    @Test
    public void testSm3() {
        byte[] sdfHash = sdfService.hash(plain.getBytes());
        log.info("sdf hash: {}", CodecUtils.encodeHex(sdfHash));
        Assertions.assertEquals(32, sdfHash.length);

        byte[] bcHash = bcService.hash(plain.getBytes());
        log.info("bc hash: {}", CodecUtils.encodeHex(bcHash));
        Assertions.assertEquals(32, bcHash.length);

        Assertions.assertArrayEquals(sdfHash, bcHash);
    }

}
