mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-27 04:36:19 +08:00
cbcmac: implement hash interface for cmac
This commit is contained in:
parent
379396b688
commit
f644a483e3
131
cbcmac/cbcmac.go
131
cbcmac/cbcmac.go
@ -184,16 +184,21 @@ func (m *macDES) MAC(src []byte) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type cmac struct {
|
type cmac struct {
|
||||||
b cipher.Block
|
b cipher.Block
|
||||||
k1, k2 []byte
|
k1, k2 []byte
|
||||||
size int
|
size int
|
||||||
|
blockSize int
|
||||||
|
tag []byte
|
||||||
|
x []byte
|
||||||
|
nx int
|
||||||
|
len uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewCMAC returns a CMAC instance that implements MAC with the given block cipher.
|
// NewCMAC returns a CMAC instance that implements MAC with the given block cipher.
|
||||||
// GB/T 15821.1-2020 MAC scheme 5
|
// GB/T 15821.1-2020 MAC scheme 5
|
||||||
//
|
//
|
||||||
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
|
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
|
||||||
func NewCMAC(b cipher.Block, size int) BockCipherMAC {
|
func NewCMAC(b cipher.Block, size int) *cmac {
|
||||||
if size <= 0 || size > b.BlockSize() {
|
if size <= 0 || size > b.BlockSize() {
|
||||||
panic("cbcmac: invalid size")
|
panic("cbcmac: invalid size")
|
||||||
}
|
}
|
||||||
@ -208,40 +213,110 @@ func NewCMAC(b cipher.Block, size int) BockCipherMAC {
|
|||||||
msb = shiftLeft(k2)
|
msb = shiftLeft(k2)
|
||||||
k2[len(k2)-1] ^= msb * 0b10000111
|
k2[len(k2)-1] ^= msb * 0b10000111
|
||||||
|
|
||||||
return &cmac{b: b, k1: k1, k2: k2, size: size}
|
d := &cmac{b: b, k1: k1, k2: k2, size: size}
|
||||||
|
d.blockSize = blockSize
|
||||||
|
d.tag = make([]byte, blockSize)
|
||||||
|
d.x = make([]byte, blockSize)
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmac) Reset() {
|
||||||
|
for i := range c.tag {
|
||||||
|
c.tag[i] = 0
|
||||||
|
}
|
||||||
|
c.nx = 0
|
||||||
|
c.len = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmac) BlockSize() int {
|
||||||
|
return c.blockSize
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmac) Size() int {
|
func (c *cmac) Size() int {
|
||||||
return c.size
|
return c.size
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *cmac) MAC(src []byte) []byte {
|
func (d *cmac) Write(p []byte) (nn int, err error) {
|
||||||
blockSize := c.b.BlockSize()
|
nn = len(p)
|
||||||
tag := make([]byte, blockSize)
|
if nn == 0 {
|
||||||
if len(src) == 0 {
|
// nothing to do
|
||||||
// Special-cased as a single empty partial final block.
|
return
|
||||||
copy(tag, c.k2)
|
|
||||||
tag[len(src)] ^= 0b10000000
|
|
||||||
c.b.Encrypt(tag, tag)
|
|
||||||
return tag
|
|
||||||
}
|
}
|
||||||
for len(src) >= blockSize {
|
d.len += uint64(nn)
|
||||||
subtle.XORBytes(tag, src[:blockSize], tag)
|
if d.nx == d.blockSize {
|
||||||
if len(src) == blockSize {
|
// handle remaining full block
|
||||||
// Final complete block.
|
d.block(d.x)
|
||||||
subtle.XORBytes(tag, c.k1, tag)
|
d.nx = 0
|
||||||
|
} else if d.nx > 0 {
|
||||||
|
// handle remaining incomplete block
|
||||||
|
n := copy(d.x[d.nx:], p)
|
||||||
|
d.nx += n
|
||||||
|
p = p[n:]
|
||||||
|
if len(p) > 0 {
|
||||||
|
d.block(d.x)
|
||||||
|
d.nx = 0
|
||||||
}
|
}
|
||||||
c.b.Encrypt(tag, tag)
|
|
||||||
src = src[blockSize:]
|
|
||||||
}
|
}
|
||||||
if len(src) > 0 {
|
lenP := len(p)
|
||||||
// Final incomplete block.
|
if lenP > d.blockSize {
|
||||||
subtle.XORBytes(tag, src, tag)
|
n := lenP &^ (d.blockSize - 1)
|
||||||
subtle.XORBytes(tag, c.k2, tag)
|
if n == lenP {
|
||||||
tag[len(src)] ^= 0b10000000
|
n -= d.blockSize
|
||||||
c.b.Encrypt(tag, tag)
|
}
|
||||||
|
d.block(p[:n])
|
||||||
|
p = p[n:]
|
||||||
}
|
}
|
||||||
return tag[:c.size]
|
// save remaining partial/full block
|
||||||
|
if len(p) > 0 {
|
||||||
|
d.nx = copy(d.x[:], p)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmac) block(p []byte) {
|
||||||
|
for len(p) >= c.blockSize {
|
||||||
|
subtle.XORBytes(c.tag, p[:c.blockSize], c.tag)
|
||||||
|
c.b.Encrypt(c.tag, c.tag)
|
||||||
|
p = p[c.blockSize:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sum appends the current hash to in and returns the resulting slice.
|
||||||
|
// It does not change the underlying hash state.
|
||||||
|
func (d *cmac) Sum(in []byte) []byte {
|
||||||
|
// Make a copy of d so that caller can keep writing and summing.
|
||||||
|
// shared block cipher and k1, k2, x
|
||||||
|
d0 := *d
|
||||||
|
// use slices.Clone() later
|
||||||
|
d0.tag = make([]byte, d.blockSize)
|
||||||
|
copy(d0.tag, d.tag)
|
||||||
|
hash := d0.checkSum()
|
||||||
|
return append(in, hash[:]...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmac) checkSum() []byte {
|
||||||
|
tag := make([]byte, c.size)
|
||||||
|
if c.nx == 0 {
|
||||||
|
// Special-cased as a single empty partial final block.
|
||||||
|
copy(c.tag, c.k2)
|
||||||
|
c.tag[0] ^= 0b10000000
|
||||||
|
} else if c.nx == c.blockSize {
|
||||||
|
subtle.XORBytes(c.tag, c.x, c.tag)
|
||||||
|
subtle.XORBytes(c.tag, c.k1, c.tag)
|
||||||
|
} else {
|
||||||
|
subtle.XORBytes(c.tag, c.x, c.tag)
|
||||||
|
c.tag[c.nx] ^= 0b10000000
|
||||||
|
subtle.XORBytes(c.tag, c.k2, c.tag)
|
||||||
|
}
|
||||||
|
c.b.Encrypt(c.tag, c.tag)
|
||||||
|
copy(tag, c.tag[:c.size])
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *cmac) MAC(src []byte) []byte {
|
||||||
|
c.Reset()
|
||||||
|
c.Write(src)
|
||||||
|
return c.Sum(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// shiftLeft sets x to x << 1, and returns MSB₁(x).
|
// shiftLeft sets x to x << 1, and returns MSB₁(x).
|
||||||
|
@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"crypto/aes"
|
"crypto/aes"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
|
"hash"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/emmansun/gmsm/internal/cryptotest"
|
"github.com/emmansun/gmsm/internal/cryptotest"
|
||||||
@ -438,7 +439,7 @@ var testVectors = []struct {
|
|||||||
"f69f2445df4f9b17ad2b417be66c3710",
|
"f69f2445df4f9b17ad2b417be66c3710",
|
||||||
hash: "e1992190549f6ed5696a2c056c315410",
|
hash: "e1992190549f6ed5696a2c056c315410",
|
||||||
tagsize: 16,
|
tagsize: 16,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "603deb1015ca71be2b73aef0857d7781" +
|
key: "603deb1015ca71be2b73aef0857d7781" +
|
||||||
"1f352c073b6108d72d9810a30914dff4",
|
"1f352c073b6108d72d9810a30914dff4",
|
||||||
@ -475,3 +476,47 @@ func TestCMACAES(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCMACHash(t *testing.T) {
|
||||||
|
t.Run("CMAC Hash", func(t *testing.T) {
|
||||||
|
cryptotest.TestHash(t, func() hash.Hash {
|
||||||
|
key := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
return NewCMAC(block, 16)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
var buf = make([]byte, 8192)
|
||||||
|
|
||||||
|
func benchmarkSize(hash hash.Hash, b *testing.B, size int) {
|
||||||
|
b.SetBytes(int64(size))
|
||||||
|
sum := make([]byte, hash.Size())
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
hash.Reset()
|
||||||
|
hash.Write(buf[:size])
|
||||||
|
hash.Sum(sum[:0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkAESCMAC1K(b *testing.B) {
|
||||||
|
key := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
benchmarkSize(NewCMAC(block, 16), b, 1024)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkSM4CMAC1K(b *testing.B) {
|
||||||
|
key := []byte{0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}
|
||||||
|
block, err := sm4.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
b.Fatal(err)
|
||||||
|
}
|
||||||
|
benchmarkSize(NewCMAC(block, 16), b, 1024)
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user