package blockmode_test

import (
	"bytes"
	"encoding/hex"
	"fmt"

	"xdx.jelly/xgcl/sm/sm4"
	"xdx.jelly/xgcl/utils/blockmode"
)

func ExampleWrap() {
	// 将一个cipher.Block接口包装为EcbCbcEncBlockMode。适用于软实现的cipher.Block。
	// 如果是密码机，则应该实现自己的EcbCbcEncBlockMode。因为cipher.Block只是对一个分
	// 组进行计算，多个分组会反复调用密码机。
	key, _ := hex.DecodeString("2d3edc27837fac8325261729fb875a09")
	block, _ := sm4.NewCipher(key)
	blockMode := blockmode.Wrap(block)

	fmt.Println(blockMode.BlockSize())
	// Output: 16
}

func ExampleNewGCM() {
	// ECBBlockMode 可以调用密码机实现.
	key, _ := hex.DecodeString("2d3edc27837fac8325261729fb875a09")
	block, _ := sm4.NewCipher(key)
	ecbBlockMode := blockmode.Wrap(block)

	nonce, _ := hex.DecodeString("5b5e2f4b0e204cc7d2db5170")
	// plaintext也可以为空，则为对additional data做mac
	plaintext, _ := hex.DecodeString("010203040506070809")
	// ad也可以为空
	ad, _ := hex.DecodeString("0a0b0c0d0e0f")

	gcm, err := blockmode.NewGCM(ecbBlockMode)
	if err != nil {
		panic(err)
	}
	// 用cipher.AEAD.Seal一次性完成加密计算. ct可以预先分配好，也可以传nil。
	// ct1 = ciphertext || tag
	ct1 := make([]byte, len(plaintext)+gcm.Overhead())
	ct1 = gcm.Seal(ct1[:0], nonce, plaintext, ad) // 密文会append在dst后
	// ct1 := gcm.Seal(nil, nonce, plaintext, ad) // 由Seal分配内存

	pt1, err := gcm.Open(nil, nonce, ct1, ad)
	if err != nil {
		panic(err)
	}

	fmt.Println(bytes.Compare(pt1, plaintext) == 0)

	// 也可以用Init-(SpecifyADD)-Update-Final三步完成
	if err := gcm.EncryptInit(nonce); err != nil {
		panic(err)
	}
	// 如果有additional data，必须在Init后，Update前调用SpecifyADD传入additional data.
	// 不调用则表示additional data为空
	gcm.SpecifyADD(ad)
	var ct2 []byte
	// Update明文
	for i := 0; i < len(plaintext); i++ {
		ct, err := gcm.EncryptUpdate(nil, []byte{plaintext[i]})
		if err != nil {
			panic(err)
		}
		// 处理本次Update得到的密文
		ct2 = append(ct2, ct...)
	}

	ct, err := gcm.EncryptFinal(nil)
	if err != nil {
		panic(err)
	}
	ct2 = append(ct2, ct...)

	if err := gcm.DecryptInit(nonce); err != nil {
		panic(err)
	}
	gcm.SpecifyADD(ad)

	pt2, err := gcm.DecryptUpdate(nil, ct2)
	if err != nil {
		panic(err)
	}
	pt2, err = gcm.DecryptFinal(pt2)

	fmt.Println(bytes.Compare(pt2, plaintext) == 0)
	// Output: true
	// true
}

func ExampleNewCCM() {

	// Output:
}

func ExampleNewCTR() {
	// Output:
}

func ExampleNewXTS() {
	// Output:
}
