blob: 1e9031424fd10fab17e7cdd21b683db077379ade [file] [log] [blame]
Matteo Scandoloa6a3aee2019-11-26 13:30:14 -07001// Copyright (c) 2016 Andreas Auernhammer. All rights reserved.
2// Use of this source code is governed by a license that can be
3// found in the LICENSE file.
4
5// Package cmac implements the fast CMAC MAC based on
6// a block cipher. This mode of operation fixes security
7// deficiencies of CBC-MAC (CBC-MAC is secure only for
8// fixed-length messages). CMAC is equal to OMAC1.
9// This implementations supports block ciphers with a
10// block size of:
11// - 64 bit
12// - 128 bit
13// - 256 bit
14// - 512 bit
15// - 1024 bit
16// Common ciphers like AES, Serpent etc. operate on 128 bit
17// blocks. 256, 512 and 1024 are supported for the Threefish
18// tweakable block cipher. Ciphers with 64 bit blocks are
19// supported, but not recommened.
20// CMAC (with AES) is specified in RFC 4493 and RFC 4494.
21package cmac // import "github.com/aead/cmac"
22
23import (
24 "crypto/cipher"
25 "crypto/subtle"
26 "errors"
27 "hash"
28)
29
30const (
31 // minimal irreducible polynomial for blocksize
32 p64 = 0x1b // for 64 bit block ciphers
33 p128 = 0x87 // for 128 bit block ciphers (like AES)
34 p256 = 0x425 // special for large block ciphers (Threefish)
35 p512 = 0x125 // special for large block ciphers (Threefish)
36 p1024 = 0x80043 // special for large block ciphers (Threefish)
37)
38
39var (
40 errUnsupportedCipher = errors.New("cipher block size not supported")
41 errInvalidTagSize = errors.New("tags size must between 1 and the cipher's block size")
42)
43
44// Sum computes the CMAC checksum with the given tagsize of msg using the cipher.Block.
45func Sum(msg []byte, c cipher.Block, tagsize int) ([]byte, error) {
46 h, err := NewWithTagSize(c, tagsize)
47 if err != nil {
48 return nil, err
49 }
50 h.Write(msg)
51 return h.Sum(nil), nil
52}
53
54// Verify computes the CMAC checksum with the given tagsize of msg and compares
55// it with the given mac. This functions returns true if and only if the given mac
56// is equal to the computed one.
57func Verify(mac, msg []byte, c cipher.Block, tagsize int) bool {
58 sum, err := Sum(msg, c, tagsize)
59 if err != nil {
60 return false
61 }
62 return subtle.ConstantTimeCompare(mac, sum) == 1
63}
64
65// New returns a hash.Hash computing the CMAC checksum.
66func New(c cipher.Block) (hash.Hash, error) {
67 return NewWithTagSize(c, c.BlockSize())
68}
69
70// NewWithTagSize returns a hash.Hash computing the CMAC checksum with the
71// given tag size. The tag size must between the 1 and the cipher's block size.
72func NewWithTagSize(c cipher.Block, tagsize int) (hash.Hash, error) {
73 blocksize := c.BlockSize()
74
75 if tagsize <= 0 || tagsize > blocksize {
76 return nil, errInvalidTagSize
77 }
78
79 var p int
80 switch blocksize {
81 default:
82 return nil, errUnsupportedCipher
83 case 8:
84 p = p64
85 case 16:
86 p = p128
87 case 32:
88 p = p256
89 case 64:
90 p = p512
91 case 128:
92 p = p1024
93 }
94
95 m := &macFunc{
96 cipher: c,
97 k0: make([]byte, blocksize),
98 k1: make([]byte, blocksize),
99 buf: make([]byte, blocksize),
100 }
101 m.tagsize = tagsize
102 c.Encrypt(m.k0, m.k0)
103
104 v := shift(m.k0, m.k0)
105 m.k0[blocksize-1] ^= byte(subtle.ConstantTimeSelect(v, p, 0))
106
107 v = shift(m.k1, m.k0)
108 m.k1[blocksize-1] ^= byte(subtle.ConstantTimeSelect(v, p, 0))
109
110 return m, nil
111}
112
113// The CMAC message auth. function
114type macFunc struct {
115 cipher cipher.Block
116 k0, k1 []byte
117 buf []byte
118 off int
119 tagsize int
120}
121
122func (h *macFunc) Size() int { return h.cipher.BlockSize() }
123
124func (h *macFunc) BlockSize() int { return h.cipher.BlockSize() }
125
126func (h *macFunc) Reset() {
127 for i := range h.buf {
128 h.buf[i] = 0
129 }
130 h.off = 0
131}
132
133func (h *macFunc) Write(msg []byte) (int, error) {
134 bs := h.BlockSize()
135 n := len(msg)
136
137 if h.off > 0 {
138 dif := bs - h.off
139 if n > dif {
140 xor(h.buf[h.off:], msg[:dif])
141 msg = msg[dif:]
142 h.cipher.Encrypt(h.buf, h.buf)
143 h.off = 0
144 } else {
145 xor(h.buf[h.off:], msg)
146 h.off += n
147 return n, nil
148 }
149 }
150
151 if length := len(msg); length > bs {
152 nn := length & (^(bs - 1))
153 if length == nn {
154 nn -= bs
155 }
156 for i := 0; i < nn; i += bs {
157 xor(h.buf, msg[i:i+bs])
158 h.cipher.Encrypt(h.buf, h.buf)
159 }
160 msg = msg[nn:]
161 }
162
163 if length := len(msg); length > 0 {
164 xor(h.buf[h.off:], msg)
165 h.off += length
166 }
167
168 return n, nil
169}
170
171func (h *macFunc) Sum(b []byte) []byte {
172 blocksize := h.cipher.BlockSize()
173
174 // Don't change the buffer so the
175 // caller can keep writing and suming.
176 hash := make([]byte, blocksize)
177
178 if h.off < blocksize {
179 copy(hash, h.k1)
180 } else {
181 copy(hash, h.k0)
182 }
183
184 xor(hash, h.buf)
185 if h.off < blocksize {
186 hash[h.off] ^= 0x80
187 }
188
189 h.cipher.Encrypt(hash, hash)
190 return append(b, hash[:h.tagsize]...)
191}
192
193func shift(dst, src []byte) int {
194 var b, bit byte
195 for i := len(src) - 1; i >= 0; i-- { // a range would be nice
196 bit = src[i] >> 7
197 dst[i] = src[i]<<1 | b
198 b = bit
199 }
200 return int(b)
201}