gmsm/cbcmac/cbcmac.go

401 lines
9.7 KiB
Go

// Copyright 2024 Sun Yimin. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
// Package cbcmac implements the Message Authentication Code with the block chipher mechanisms.
package cbcmac
import (
"crypto/cipher"
"github.com/emmansun/gmsm/internal/subtle"
"github.com/emmansun/gmsm/padding"
)
// Reference: GB/T 15821.1-2020 Security techniques
// Message authentication codes - Part 1: Mechanisms using block ciphers
// BockCipherMAC is the interface that wraps the basic MAC method.
type BockCipherMAC interface {
// Size returns the MAC value's number of bytes.
Size() int
// MAC calculates the MAC of the given data.
// The MAC value's number of bytes is returned by Size.
// Intercept message authentication code as needed.
MAC(src []byte) []byte
}
// cbcmac implements the basic CBC-MAC mode of operation for block ciphers.
type cbcmac struct {
b cipher.Block
pad padding.Padding
size int
}
// NewCBCMAC returns a CBC-MAC instance that implements the MAC with the given block cipher.
// The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 1
func NewCBCMAC(b cipher.Block, size int) BockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
return &cbcmac{b: b, pad: padding.NewISO9797M2Padding(uint(b.BlockSize())), size: size}
}
func (c *cbcmac) Size() int {
return c.size
}
// MAC calculates the MAC of the given data.
// The data is padded with the padding scheme of the block cipher before processing.
func (c *cbcmac) MAC(src []byte) []byte {
src = c.pad.Pad(src)
blockSize := c.b.BlockSize()
tag := make([]byte, blockSize)
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
c.b.Encrypt(tag, tag)
src = src[blockSize:]
}
return tag[:c.size]
}
// emac implements the EMAC mode of operation for block ciphers.
type emac struct {
pad padding.Padding
b1, b2 cipher.Block
size int
}
// NewEMAC returns an EMAC instance that implements MAC with the given block cipher.
// The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 2
func NewEMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC {
var b1, b2 cipher.Block
var err error
if b1, err = creator(key1); err != nil {
panic(err)
}
if size <= 0 || size > b1.BlockSize() {
panic("cbcmac: invalid size")
}
if b2, err = creator(key2); err != nil {
panic(err)
}
return &emac{pad: padding.NewISO9797M2Padding(uint(b1.BlockSize())), b1: b1, b2: b2, size: size}
}
func (e *emac) Size() int {
return e.size
}
func (e *emac) MAC(src []byte) []byte {
src = e.pad.Pad(src)
blockSize := e.b1.BlockSize()
tag := make([]byte, blockSize)
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
e.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
e.b2.Encrypt(tag, tag)
return tag[:e.size]
}
type ansiRetailMAC emac
// NewANSIRetailMAC returns an ANSI Retail MAC instance that implements MAC with the given block cipher.
// The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 3
func NewANSIRetailMAC(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC {
return (*ansiRetailMAC)(NewEMAC(creator, key1, key2, size).(*emac))
}
func (e *ansiRetailMAC) Size() int {
return e.size
}
func (e *ansiRetailMAC) MAC(src []byte) []byte {
src = e.pad.Pad(src)
blockSize := e.b1.BlockSize()
tag := make([]byte, blockSize)
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
e.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
e.b2.Decrypt(tag, tag)
e.b1.Encrypt(tag, tag)
return tag[:e.size]
}
type macDES struct {
pad padding.Padding
b1, b2, b3 cipher.Block
size int
}
// NewMACDES returns a MAC-DES instance that implements MAC with the given block cipher.
// The padding scheme is ISO/IEC 9797-1 method 2.
// GB/T 15821.1-2020 MAC scheme 4
func NewMACDES(creator func(key []byte) (cipher.Block, error), key1, key2 []byte, size int) BockCipherMAC {
var b1, b2, b3 cipher.Block
var err error
if b1, err = creator(key1); err != nil {
panic(err)
}
if size <= 0 || size > b1.BlockSize() {
panic("cbcmac: invalid size")
}
if b2, err = creator(key2); err != nil {
panic(err)
}
key3 := make([]byte, len(key2))
copy(key3, key2)
for i := 0; i < len(key3); i++ {
key3[i] ^= 0xF0
}
if b3, err = creator(key3); err != nil {
panic(err)
}
return &macDES{pad: padding.NewISO9797M2Padding(uint(b1.BlockSize())), b1: b1, b2: b2, b3: b3, size: size}
}
func (m *macDES) Size() int {
return m.size
}
func (m *macDES) MAC(src []byte) []byte {
src = m.pad.Pad(src)
blockSize := m.b1.BlockSize()
tag := make([]byte, blockSize)
copy(tag, src[:blockSize])
m.b1.Encrypt(tag, tag)
m.b3.Encrypt(tag, tag)
src = src[blockSize:]
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
m.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
m.b2.Encrypt(tag, tag)
return tag[:m.size]
}
type cmac struct {
b cipher.Block
k1, k2 []byte
size int
}
// NewCMAC returns a CMAC instance that implements MAC with the given block cipher.
// GB/T 15821.1-2020 MAC scheme 5
//
// Reference: https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-38B.pdf
func NewCMAC(b cipher.Block, size int) BockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
blockSize := b.BlockSize()
k1 := make([]byte, blockSize)
k2 := make([]byte, blockSize)
b.Encrypt(k1, k1)
msb := shiftLeft(k1)
k1[len(k1)-1] ^= msb * 0b10000111
copy(k2, k1)
msb = shiftLeft(k2)
k2[len(k2)-1] ^= msb * 0b10000111
return &cmac{b: b, k1: k1, k2: k2, size: size}
}
func (c *cmac) Size() int {
return c.size
}
func (c *cmac) MAC(src []byte) []byte {
blockSize := c.b.BlockSize()
tag := make([]byte, blockSize)
if len(src) == 0 {
// Special-cased as a single empty partial final block.
copy(tag, c.k2)
tag[len(src)] ^= 0b10000000
c.b.Encrypt(tag, tag)
return tag
}
for len(src) >= blockSize {
subtle.XORBytes(tag, src[:blockSize], tag)
if len(src) == blockSize {
// Final complete block.
subtle.XORBytes(tag, c.k1, tag)
}
c.b.Encrypt(tag, tag)
src = src[blockSize:]
}
if len(src) > 0 {
// Final incomplete block.
subtle.XORBytes(tag, src, tag)
subtle.XORBytes(tag, c.k2, tag)
tag[len(src)] ^= 0b10000000
c.b.Encrypt(tag, tag)
}
return tag[:c.size]
}
// shiftLeft sets x to x << 1, and returns MSB₁(x).
func shiftLeft(x []byte) byte {
var msb byte
for i := len(x) - 1; i >= 0; i-- {
msb, x[i] = x[i]>>7, x[i]<<1|msb
}
return msb
}
type lmac struct {
b1, b2 cipher.Block
pad padding.Padding
size int
}
// NewLMAC returns an LMAC instance that implements MAC with the given block cipher.
// GB/T 15821.1-2020 MAC scheme 6
func NewLMAC(creator func(key []byte) (cipher.Block, error), key []byte, size int) BockCipherMAC {
var b, b1, b2 cipher.Block
var err error
if b, err = creator(key); err != nil {
panic(err)
}
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
blockSize := b.BlockSize()
key1 := make([]byte, blockSize)
key1[blockSize-1] = 0x01
key2 := make([]byte, blockSize)
key2[blockSize-1] = 0x02
b.Encrypt(key1, key1)
b.Encrypt(key2, key2)
if b1, err = creator(key1); err != nil {
panic(err)
}
if b2, err = creator(key2); err != nil {
panic(err)
}
return &lmac{b1: b1, b2: b2, pad: padding.NewISO9797M2Padding(uint(blockSize)), size: size}
}
func (l *lmac) Size() int {
return l.b1.BlockSize()
}
func (l *lmac) MAC(src []byte) []byte {
src = l.pad.Pad(src)
blockSize := l.b1.BlockSize()
tag := make([]byte, blockSize)
for len(src) > blockSize {
subtle.XORBytes(tag, tag, src[:blockSize])
l.b1.Encrypt(tag, tag)
src = src[blockSize:]
}
subtle.XORBytes(tag, tag, src[:blockSize])
l.b2.Encrypt(tag, tag)
return tag
}
type trCBCMAC struct {
b cipher.Block
size int
}
// NewTRCBCMAC returns a TR-CBC-MAC instance that implements MAC with the given block cipher.
// GB/T 15821.1-2020 MAC scheme 7
//
// Reference: TrCBC: Another look at CBC-MAC.
func NewTRCBCMAC(b cipher.Block, size int) BockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
return &trCBCMAC{b: b, size: size}
}
func (t *trCBCMAC) Size() int {
return t.size
}
func (t *trCBCMAC) MAC(src []byte) []byte {
blockSize := t.b.BlockSize()
tag := make([]byte, blockSize)
padded := false
if len(src) == 0 || len(src)%blockSize != 0 {
pad := padding.NewISO9797M2Padding(uint(blockSize))
src = pad.Pad(src)
padded = true
}
for len(src) > 0 {
subtle.XORBytes(tag, tag, src[:blockSize])
t.b.Encrypt(tag, tag)
src = src[blockSize:]
}
if padded {
return tag[blockSize-t.size:]
}
return tag[:t.size]
}
type cbcrMAC struct {
b cipher.Block
size int
}
// NewCBCRMAC returns a CBCRMAC instance that implements MAC with the given block cipher.
// GB/T 15821.1-2020 MAC scheme 8
//
// Reference: CBCR: CBC MAC with rotating transformations.
func NewCBCRMAC(b cipher.Block, size int) BockCipherMAC {
if size <= 0 || size > b.BlockSize() {
panic("cbcmac: invalid size")
}
return &cbcrMAC{b: b, size: size}
}
func (c *cbcrMAC) Size() int {
return c.size
}
func (c *cbcrMAC) MAC(src []byte) []byte {
blockSize := c.b.BlockSize()
tag := make([]byte, blockSize)
c.b.Encrypt(tag, tag)
padded := false
if len(src) == 0 || len(src)%blockSize != 0 {
pad := padding.NewISO9797M2Padding(uint(blockSize))
src = pad.Pad(src)
padded = true
}
for len(src) > blockSize {
subtle.XORBytes(tag, tag, src[:blockSize])
c.b.Encrypt(tag, tag)
src = src[blockSize:]
}
subtle.XORBytes(tag, tag, src[:blockSize])
if padded {
shiftLeft(tag)
} else {
shiftRight(tag)
}
c.b.Encrypt(tag, tag)
return tag[:c.size]
}
func shiftRight(x []byte) {
var lsb byte
for i := 0; i < len(x); i++ {
lsb, x[i] = x[i]<<7, x[i]>>1|lsb
}
x[0] ^= lsb
}