///
/// Copyright (c) 2018 xdx. All rights reserved.
///
/// \file: cipher.go
///
/// \brief: SM4's Block interface and its implements
///
/// \author: xdx
///

package sm4

import (
	"crypto/cipher"

	"xdx.jelly/xgcl/gerrors"
)

// BlockSize is the SM4 block size in bytes.
const BlockSize = 16

// NewCipher creates and returns a new cipher.Block.
// it call the newCipher of each platform
func NewCipher(key []byte) (cipher.Block, error) {
	if len(key) < BlockSize {
		return nil, gerrors.WithAnnotatingf(ErrInvalidInput, "input key size is %d, want %d", len(key), BlockSize)
	}
	// newCipher is defined on different arch.
	return newCipher(key)
}

// newCipherGeneric creates and returns a new cipher.Block.
func newCipherGeneric(key []byte) (cipher.Block, error) {
	cp := new(sm4Cipher)
	// gen keys
	expandKey(key, cp.encKey[:], cp.decKey[:])
	return cp, nil
}

// sm4Cipher is an instance of cipher.Block interface
type sm4Cipher struct {
	encKey [32]uint32
	decKey [32]uint32
}

// Clear implements the Clearable interface
func (c *sm4Cipher) Clear() {
	for i := 0; i < len(c.decKey); i++ {
		c.decKey[i] = 0
		c.encKey[i] = 0
	}
}

func (c *sm4Cipher) BlockSize() int { return BlockSize }

// 调用前应保证输入一个block
func (c *sm4Cipher) Encrypt(dst, src []byte) {
	if len(src) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm4/Encrypt: input not full block")
	}
	if len(dst) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm4/Encrypt: output not full block")
	}
	encryptBlockGo(c.encKey[:], dst, src)
}

func (c *sm4Cipher) Decrypt(dst, src []byte) {
	if len(src) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm4/Decrypt: input not full block")
	}
	if len(dst) < BlockSize {
		panic("xdx.jelly/xgcl/sm/sm4/Decrypt: output not full block")
	}
	decryptBlockGo(c.decKey[:], dst, src)
}
