init: v1.0.0

This commit is contained in:
yaole
2026-05-27 23:03:00 +08:00
commit 8d97f750eb
466 changed files with 80067 additions and 0 deletions
+147
View File
@@ -0,0 +1,147 @@
package cbcmac
import (
"bytes"
"crypto/cipher"
"errors"
"xdx.jelly/xgcl/utils/padding"
)
// CalculateMAC 计算输入iv和data的cbc Mac.
// 备注:
// 1. data必须是block.BlockSize的整数倍
// 2. iv必须是block.BlockSize大小
// 3. 第一个分组,iv输入全0
// 4. 第i个分组调用后,iv值被更新, 作为第i+1次分组的输入iv
// 5. mac必须为block.BlockSize大小
func CalculateMAC(data []byte, iv []byte, block cipher.Block, mac []byte) error {
blockSize := block.BlockSize()
if len(data) == 0 || len(data)%blockSize != 0 || len(iv) != blockSize || len(mac) != blockSize {
return errors.New("input arguments invalid")
}
blockMode := cipher.NewCBCEncrypter(block, iv)
blockMode.CryptBlocks(mac, data[:blockSize])
data = data[blockSize:]
for len(data) > 0 {
blockMode.CryptBlocks(mac, data[:blockSize])
data = data[blockSize:]
}
return nil
}
const maxChunk = 64
type CbcMac struct {
mac []byte
pad padding.Padding
block cipher.Block
blockMode cipher.BlockMode
x [maxChunk]byte
nx int
}
// pad 填充方式。忽略的话为不填充
func New(block cipher.Block, pad ...padding.Padding) (*CbcMac, error) {
var p padding.Padding
if len(pad) > 0 {
p = pad[0]
} else {
p = padding.NonePadding{}
}
return &CbcMac{
pad: p,
block: block,
blockMode: cipher.NewCBCEncrypter(block, make([]byte, block.BlockSize())),
mac: make([]byte, block.BlockSize()),
}, nil
}
// ComputeMAC computes message authentication code (MAC) for the given data.
// data must paded
func (c *CbcMac) ComputeMAC(data []byte) ([]byte, error) {
c.Reset()
_, err := c.Write(data)
if err != nil {
return nil, err
}
defer c.Reset()
return c.Mac([]byte{})
}
// VerifyMAC verifies whether the given MAC is a correct message authentication
// code (MAC) the given data.
func (c *CbcMac) VerifyMAC(mac []byte, data []byte) error {
c.Reset()
mac2, err := c.ComputeMAC(data)
if err != nil {
return err
}
if bytes.Compare(mac2, mac) != 0 {
return errors.New("Mac verify failed")
}
return nil
}
func (c *CbcMac) Write(p []byte) (n int, err error) {
blockSize := c.block.BlockSize()
if len(p)+c.nx < blockSize {
copy(c.x[c.nx:], p)
c.nx += len(p)
} else {
n = copy(c.x[c.nx:blockSize], p)
p = p[n:]
c.blockMode.CryptBlocks(c.mac, c.x[:blockSize])
for len(p) >= blockSize {
c.blockMode.CryptBlocks(c.mac, p[:blockSize])
p = p[blockSize:]
}
c.nx = copy(c.x[:], p)
}
return len(p), nil
}
// Mac appends the current mac to b and returns the resulting slice.
// tail is the un-computed data c.x[:c.nx].
func (c *CbcMac) Mac(b []byte) ([]byte, error) {
x := c.pad.Pad(c.x[:c.nx], c.block.BlockSize())
if len(x)%c.block.BlockSize() != 0 {
return nil, errors.New("invalid data length with no padding")
}
c.blockMode.CryptBlocks(c.mac, x)
return append(b, c.mac...), nil
}
// Sum is used to compute the mac but return nil while an error occur.
// It's prefered to use Mac instead.
func (c *CbcMac) Sum(b []byte) []byte {
m, err := c.Mac(b)
if err != nil {
return nil
}
return m
}
// Reset resets the Hash to its initial state.
func (c *CbcMac) Reset() {
c.mac = make([]byte, c.block.BlockSize())
c.blockMode = cipher.NewCBCEncrypter(c.block, c.mac)
}
// Size returns the number of bytes Sum will return.
func (c *CbcMac) Size() int {
return c.block.BlockSize()
}
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
func (c *CbcMac) BlockSize() int {
return c.block.BlockSize()
}
+27
View File
@@ -0,0 +1,27 @@
package cbcmac
import (
"fmt"
"testing"
"xdx.jelly/xgcl/sm/sm4"
)
func TestCbcMac(t *testing.T) {
key := make([]byte, 16)
msg := make([]byte, 16)
c, err := sm4.NewCipher(key)
if err != nil {
t.Fatal(err)
}
mac, err := New(c)
if err != nil {
t.Fatal(err)
}
mac.Write(msg)
m := mac.Sum(nil)
if len(m) == 0 {
t.Fatal("compute mac error")
}
fmt.Printf("%x\n", m)
}
+123
View File
@@ -0,0 +1,123 @@
package hmac
/*
hmac package are deprecated. Use crypto/hmac instead
example:
func ComputeMac(message, key[]byte) []byte{
mac := hmac.New(sm3.New, key)
mac.Write(message) // could write multiple messages
return mac.Sum(nil)
}
func ValidMAC(message, messageMAC, key []byte) bool {
mac := hmac.New(sha256.New, key)
mac.Write(message)
expectedMAC := mac.Sum(nil)
return hmac.Equal(messageMAC, expectedMAC) // must use hmac.Equal to compare with constant time.
}
*/
import (
"crypto/hmac"
"errors"
"fmt"
"hash"
"xdx.jelly/xgcl/mac/subtle"
"xdx.jelly/xgcl/sm/sm3"
)
const (
// Minimum key size in bytes.
minKeySizeInBytes = uint32(16)
// Minimum tag size in bytes. This provides minimum 80-bit security strength.
minTagSizeInBytes = uint32(10)
)
var errHMACInvalidInput = errors.New("HMAC: invalid input")
// HMAC implementation of interface gcl.mac.MAC
type HMAC struct {
HashFunc func() hash.Hash
Key []byte
TagSize uint32
}
// NewHMAC creates a new instance of HMAC with the specified key and tag size.
// key should be at least 16 bytes and no longer then hash's digest size.
// tag size should be at least 10 bytes and no longer then hash's digest size.
func NewHMAC(hashAlg string, key []byte, tagSize uint32) (*HMAC, error) {
keySize := uint32(len(key))
if err := ValidateHMACParams(hashAlg, keySize, tagSize); err != nil {
return nil, fmt.Errorf("hmac: %s", err)
}
hashFunc := subtle.GetHashFunc(hashAlg)
if hashFunc == nil {
return nil, fmt.Errorf("hmac: invalid hash algorithm")
}
return &HMAC{
HashFunc: hashFunc,
Key: key,
TagSize: tagSize,
}, nil
}
// ValidateHMACParams validates parameters of HMAC constructor.
func ValidateHMACParams(hash string, keySize uint32, tagSize uint32) error {
// validate tag size
digestSize, err := subtle.GetHashDigestSize(hash)
if err != nil {
return err
}
if tagSize > digestSize {
return fmt.Errorf("tag size too big")
}
if tagSize < minTagSizeInBytes {
return fmt.Errorf("tag size too small")
}
// validate key size
if keySize < minKeySizeInBytes {
return fmt.Errorf("key too short")
}
return nil
}
// ComputeMAC computes message authentication code (MAC) for the given data.
func (h *HMAC) ComputeMAC(data []byte) ([]byte, error) {
if data == nil {
return nil, errHMACInvalidInput
}
mac := hmac.New(h.HashFunc, h.Key)
if _, err := mac.Write(data); err != nil {
return nil, err
}
tag := mac.Sum(nil)
return tag[:h.TagSize], nil
}
// VerifyMAC verifies whether the given MAC is a correct message authentication
// code (MAC) the given data.
func (h *HMAC) VerifyMAC(mac []byte, data []byte) error {
if mac == nil || data == nil {
return errHMACInvalidInput
}
expectedMAC, err := h.ComputeMAC(data)
if err != nil {
return err
}
if hmac.Equal(expectedMAC, mac) {
return nil
}
return errors.New("HMAC: invalid MAC")
}
// HMacSM3 returns the HMacSM3 with tag size 32
func HMacSM3(data []byte, key []byte) ([]byte, error) {
mac := hmac.New(sm3.New, key)
if _, err := mac.Write(data); err != nil {
return nil, err
}
return mac.Sum(nil), nil
}
+117
View File
@@ -0,0 +1,117 @@
package hmac_test
import (
"encoding/hex"
"fmt"
"testing"
"xdx.jelly/xgcl/mac/hmac"
)
var key, _ = hex.DecodeString("000102030405060708090a0b0c0d0e0f")
var data = []byte("Hello")
var hmacTests = []struct {
hashAlg string
tagSize uint32
key []byte
data []byte
expectedMac string
}{
{
hashAlg: "SHA256",
tagSize: 32,
data: data,
key: key,
expectedMac: "e0ff02553d9a619661026c7aa1ddf59b7b44eac06a9908ff9e19961d481935d4",
},
{
hashAlg: "SHA512",
tagSize: 64,
data: data,
key: key,
expectedMac: "481e10d823ba64c15b94537a3de3f253c16642451ac45124dd4dde120bf1e5c15" +
"e55487d55ba72b43039f235226e7954cd5854b30abc4b5b53171a4177047c9b",
},
// empty data
{
hashAlg: "SHA256",
tagSize: 32,
data: []byte{},
key: key,
expectedMac: "07eff8b326b7798c9ccfcbdbe579489ac785a7995a04618b1a2813c26744777d",
},
{
hashAlg: "SM3",
tagSize: 32,
data: data,
key: key,
expectedMac: "06d19e9ee3a3db273490fb6cf15d001fc3a9dfa9288f4dd801c60f9c8176b8ab",
},
}
func TestHMACBasic(t *testing.T) {
for i, test := range hmacTests {
cipher, err := hmac.NewHMAC(test.hashAlg, test.key, test.tagSize)
if err != nil {
t.Errorf("cannot create new mac in test case %d: %s", i, err)
}
mac, err := cipher.ComputeMAC(test.data)
if err != nil {
t.Errorf("mac computation failed in test case %d: %s", i, err)
}
if hex.EncodeToString(mac) != test.expectedMac[:(test.tagSize*2)] {
t.Errorf("incorrect mac in test case %d: expect %s, got %s",
i, test.expectedMac[:(test.tagSize*2)], hex.EncodeToString(mac))
}
if err := cipher.VerifyMAC(mac, test.data); err != nil {
t.Errorf("mac verification failed in test case %d: %s", i, err)
}
}
}
// 密钥= EFC0D9722E2F539C52E1B40E2F20E73D
// IV= C06B0C0A5295E37A6839EF67CBDA1A2C
// 明文长度= 16
// 明文= 7685B2CE7A74A215E3A4788D50CA6197
// MAC值= BCA3412199BA34A85FAE161AC054B8A5715D4BE6FB302B1C445D056A82EBC140
func TestHMAC(t *testing.T) {
var key, _ = hex.DecodeString("C62BB9801E85FF7C4CCAEC6152C2E0D4")
var data, _ = hex.DecodeString("476670281EC8B07402DD3664A3AE83028F251890F408AFE4CBC7FF9DA554EE813A9ECC953299E765ED2597D734226706E54785BCB3671DD908D40C4DCF70985B7B729974EFC71E49251DB5A3501F7A82")
cipher, err := hmac.NewHMAC("SM3", key, 32)
if err != nil {
t.Errorf("cannot create new mac in test case: %s", err)
}
mac, err := cipher.ComputeMAC(data)
if err != nil {
t.Errorf("mac computation failed in test case: %s", err)
}
fmt.Printf("mac: %02x\n", mac)
// if hex.EncodeToString(mac) != test.expectedMac[:(test.tagSize*2)] {
// t.Errorf("incorrect mac in test case %d: expect %s, got %s",
// i, test.expectedMac[:(test.tagSize*2)], hex.EncodeToString(mac))
// }
// if err := cipher.VerifyMAC(mac, test.data); err != nil {
// t.Errorf("mac verification failed in test case %d: %s", i, err)
// }
}
func TestHMAC2(t *testing.T) {
var key, _ = hex.DecodeString("12345678901234561234567890123456")
var data, _ = hex.DecodeString("123456789012345612345678901234560102")
cipher, err := hmac.NewHMAC("SM3", key, 32)
if err != nil {
t.Errorf("cannot create new mac in test case: %s", err)
}
mac, err := cipher.ComputeMAC(data)
if err != nil {
t.Errorf("mac computation failed in test case: %s", err)
}
fmt.Printf("mac: %02x\n", mac)
// if hex.EncodeToString(mac) != test.expectedMac[:(test.tagSize*2)] {
// t.Errorf("incorrect mac in test case %d: expect %s, got %s",
// i, test.expectedMac[:(test.tagSize*2)], hex.EncodeToString(mac))
// }
// if err := cipher.VerifyMAC(mac, test.data); err != nil {
// t.Errorf("mac verification failed in test case %d: %s", i, err)
// }
}
+110
View File
@@ -0,0 +1,110 @@
// package mac is the the implement of Message Authentication Code.
// Includes cbcmac and hmac.
//
// 一般实际应用中我们只使用两种MAC:
// 1) CBCMac, 实际上是使用全0的iv对原文进行CBC加密的最后一个分组。其中原文由调用者进行补位(padding),输出
// 为128比特;
// 2) HMac_SM3, HMac对key和输出tag长度并没有限定 ,但一般key不能太小(< 80 bits), tag也不能小于80比特,
// 并且tag长度不超过SM3杂凑值长度,即256比特。
// 注:hmac可以使用标准库的方式:crypto/hmac.New(SM3.New, key).
// 本package只是对crypto/hmac的包装.
package mac
import (
crypto_hmac "crypto/hmac"
"crypto/sha256"
"errors"
"hash"
"xdx.jelly/xgcl/identifier"
"xdx.jelly/xgcl/mac/cbcmac"
"xdx.jelly/xgcl/mac/hmac"
"xdx.jelly/xgcl/sm/sm1"
"xdx.jelly/xgcl/sm/sm3"
"xdx.jelly/xgcl/sm/sm4"
)
/*
MAC is the interface for MACs (Message Authentication Codes).
This interface should be used for authentication only, and not for other purposes
(for example, it should not be used to generate pseudorandom bytes).
MAC is "easy to use, hard to mistake".
PS: The interface is from "Tink"
*/
type MAC interface {
// ComputeMAC computes message authentication code (MAC) for code data.
ComputeMAC(data []byte) ([]byte, error)
// Verify returns nil if mac is a correct authentication code (MAC) for data,
// otherwise it returns an error.
VerifyMAC(mac, data []byte) error
}
// NewHMacSm3 is a simple wrapper of hmac.NewHMAC
// key 16- bytes
// tagSize 10-32
//
// !Deprecated. Use NewMac instead.
func NewHMacSm3(key []byte, tagSize uint32) (MAC, error) {
return hmac.NewHMAC("SM3", key, tagSize)
}
type MacType uint32
const (
HMAC_SM3 = MacType(identifier.SGDSM3)
HMAC_SHA256 = MacType(identifier.SGDSHA256)
CBCMAC_SM1 = MacType(identifier.SGDSM1Mac)
CBCMAC_SM4 = MacType(identifier.SGDSM4Mac)
)
// NewMac returns a MAC interface, which is easy to use, hard to mistake.
func NewMac(tpe MacType, key []byte) (MAC, error) {
switch tpe {
case HMAC_SM3:
return hmac.NewHMAC("SM3", key, sm3.Size)
case HMAC_SHA256:
return hmac.NewHMAC("SHA256", key, sha256.Size)
case CBCMAC_SM1:
b, err := sm1.NewCipher(key)
if err != nil {
return nil, err
}
return cbcmac.New(b)
case CBCMAC_SM4:
b, err := sm4.NewCipher(key)
if err != nil {
return nil, err
}
return cbcmac.New(b)
default:
return nil, errors.New("Unsurported mac type.")
}
}
// Return a hash.Hash interface, for multiple messages.
func NewMacHash(tpe MacType, key []byte) (hash.Hash, error) {
switch tpe {
case HMAC_SM3:
return crypto_hmac.New(sm3.New, key), nil
case HMAC_SHA256:
return crypto_hmac.New(sha256.New, key), nil
// case CBCMAC_SM1:
// b, err := sm1.NewCipher(key)
// if err != nil {
// return nil, err
// }
// return cbcmac.New(b)
// case CBCMAC_SM4:
// b, err := sm4.NewCipher(key)
// if err != nil {
// return nil, err
// }
// return cbcmac.New(b)
default:
return nil, errors.New("Unsurported mac type.")
}
}
+48
View File
@@ -0,0 +1,48 @@
package subtle
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"errors"
"hash"
"xdx.jelly/xgcl/sm/sm3"
)
// hashDigestSize maps hash algorithms to their digest size in bytes.
var hashDigestSize = map[string]uint32{
"SHA1": uint32(20),
"SHA256": uint32(32),
"SHA384": uint32(48),
"SHA512": uint32(64),
"SM3": uint32(32),
}
var errNilHashFunc = errors.New("nil hash function")
// GetHashDigestSize returns the digest size of the specified hash algorithm.
func GetHashDigestSize(hash string) (uint32, error) {
digestSize, ok := hashDigestSize[hash]
if !ok {
return 0, errors.New("invalid hash algorithm")
}
return digestSize, nil
}
// GetHashFunc returns the corresponding hash function of the given hash name.
func GetHashFunc(hash string) func() hash.Hash {
switch hash {
case "SHA1":
return sha1.New
case "SHA256":
return sha256.New
case "SHA384":
return sha512.New384
case "SHA512":
return sha512.New
case "SM3":
return sm3.New
default:
return nil
}
}