xref: /aosp_15_r20/external/tink/go/mac/subtle/cmac.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//      http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15////////////////////////////////////////////////////////////////////////////////
16
17package subtle
18
19import (
20	"crypto/subtle"
21	"fmt"
22
23	subtleprf "github.com/google/tink/go/prf/subtle"
24
25	// Placeholder for internal crypto/subtle allowlist, please ignore.
26)
27
28const (
29	minCMACKeySizeInBytes         = 16
30	recommendedCMACKeySizeInBytes = uint32(32)
31	minTagLengthInBytes           = uint32(10)
32	maxTagLengthInBytes           = uint32(16)
33)
34
35// AESCMAC represents an AES-CMAC struct that implements the MAC interface.
36type AESCMAC struct {
37	prf       *subtleprf.AESCMACPRF
38	tagLength uint32
39}
40
41// NewAESCMAC creates a new AESCMAC object that implements the MAC interface.
42func NewAESCMAC(key []byte, tagLength uint32) (*AESCMAC, error) {
43	if len(key) < minCMACKeySizeInBytes {
44		return nil, fmt.Errorf("Only 256 but keys are allowed with AES-CMAC")
45	}
46	if tagLength < minTagLengthInBytes {
47		return nil, fmt.Errorf("Tag length %d is shorter than minimum tag length %d", tagLength, minTagLengthInBytes)
48	}
49	if tagLength > maxTagLengthInBytes {
50		return nil, fmt.Errorf("Tag length %d is longer than maximum tag length %d", tagLength, minTagLengthInBytes)
51	}
52	ac := &AESCMAC{}
53	var err error
54	ac.prf, err = subtleprf.NewAESCMACPRF(key)
55	if err != nil {
56		return nil, fmt.Errorf("Could not create AES-CMAC prf: %v", err)
57	}
58	ac.tagLength = tagLength
59	return ac, nil
60}
61
62// ComputeMAC computes message authentication code (MAC) for code data.
63func (a AESCMAC) ComputeMAC(data []byte) ([]byte, error) {
64	return a.prf.ComputePRF(data, a.tagLength)
65}
66
67// VerifyMAC returns nil if mac is a correct authentication code (MAC) for data,
68// otherwise it returns an error.
69func (a AESCMAC) VerifyMAC(mac, data []byte) error {
70	computed, err := a.prf.ComputePRF(data, a.tagLength)
71	if err != nil {
72		return fmt.Errorf("Could not compute MAC: %v", err)
73	}
74	if subtle.ConstantTimeCompare(mac, computed) != 1 {
75		return fmt.Errorf("CMAC: Invalid MAC")
76	}
77	return nil
78}
79
80// ValidateCMACParams validates the parameters for an AES-CMAC against the recommended parameters.
81func ValidateCMACParams(keySize, tagSize uint32) error {
82	if keySize != recommendedCMACKeySizeInBytes {
83		return fmt.Errorf("Only %d sized keys are allowed with Tink's AES-CMAC", recommendedCMACKeySizeInBytes)
84	}
85	if tagSize < minTagLengthInBytes {
86		return fmt.Errorf("Tag size too short")
87	}
88	if tagSize > maxTagLengthInBytes {
89		return fmt.Errorf("Tag size too long")
90	}
91	return nil
92}
93