xref: /aosp_15_r20/external/tink/go/mac/mac_factory.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1// Copyright 2018 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 mac
18
19import (
20	"fmt"
21
22	"github.com/google/tink/go/core/cryptofmt"
23	"github.com/google/tink/go/core/primitiveset"
24	"github.com/google/tink/go/internal/internalregistry"
25	"github.com/google/tink/go/internal/monitoringutil"
26	"github.com/google/tink/go/keyset"
27	"github.com/google/tink/go/monitoring"
28	"github.com/google/tink/go/tink"
29	tinkpb "github.com/google/tink/go/proto/tink_go_proto"
30)
31
32const (
33	intSize = 32 << (^uint(0) >> 63) // 32 or 64
34	maxInt  = 1<<(intSize-1) - 1
35)
36
37// New creates a MAC primitive from the given keyset handle.
38func New(handle *keyset.Handle) (tink.MAC, error) {
39	ps, err := handle.Primitives()
40	if err != nil {
41		return nil, fmt.Errorf("mac_factory: cannot obtain primitive set: %s", err)
42	}
43	return newWrappedMAC(ps)
44}
45
46// wrappedMAC is a MAC implementation that uses the underlying primitive set to compute and
47// verify MACs.
48type wrappedMAC struct {
49	ps            *primitiveset.PrimitiveSet
50	computeLogger monitoring.Logger
51	verifyLogger  monitoring.Logger
52}
53
54var _ (tink.MAC) = (*wrappedMAC)(nil)
55
56func newWrappedMAC(ps *primitiveset.PrimitiveSet) (*wrappedMAC, error) {
57	if _, ok := (ps.Primary.Primitive).(tink.MAC); !ok {
58		return nil, fmt.Errorf("mac_factory: not a MAC primitive")
59	}
60	for _, primitives := range ps.Entries {
61		for _, p := range primitives {
62			if _, ok := (p.Primitive).(tink.MAC); !ok {
63				return nil, fmt.Errorf("mac_factory: not an MAC primitive")
64			}
65		}
66	}
67	computeLogger, verifyLogger, err := createLoggers(ps)
68	if err != nil {
69		return nil, err
70	}
71	return &wrappedMAC{
72		ps:            ps,
73		computeLogger: computeLogger,
74		verifyLogger:  verifyLogger,
75	}, nil
76}
77
78func createLoggers(ps *primitiveset.PrimitiveSet) (monitoring.Logger, monitoring.Logger, error) {
79	if len(ps.Annotations) == 0 {
80		return &monitoringutil.DoNothingLogger{}, &monitoringutil.DoNothingLogger{}, nil
81	}
82	client := internalregistry.GetMonitoringClient()
83	keysetInfo, err := monitoringutil.KeysetInfoFromPrimitiveSet(ps)
84	if err != nil {
85		return nil, nil, err
86	}
87	computeLogger, err := client.NewLogger(&monitoring.Context{
88		Primitive:   "mac",
89		APIFunction: "compute",
90		KeysetInfo:  keysetInfo,
91	})
92	if err != nil {
93		return nil, nil, err
94	}
95	verifyLogger, err := client.NewLogger(&monitoring.Context{
96		Primitive:   "mac",
97		APIFunction: "verify",
98		KeysetInfo:  keysetInfo,
99	})
100	if err != nil {
101		return nil, nil, err
102	}
103	return computeLogger, verifyLogger, nil
104}
105
106// ComputeMAC calculates a MAC over the given data using the primary primitive
107// and returns the concatenation of the primary's identifier and the calculated mac.
108func (m *wrappedMAC) ComputeMAC(data []byte) ([]byte, error) {
109	primary := m.ps.Primary
110	primitive, ok := (primary.Primitive).(tink.MAC)
111	if !ok {
112		return nil, fmt.Errorf("mac_factory: not a MAC primitive")
113	}
114	if m.ps.Primary.PrefixType == tinkpb.OutputPrefixType_LEGACY {
115		d := data
116		if len(d) >= maxInt {
117			m.computeLogger.LogFailure()
118			return nil, fmt.Errorf("mac_factory: data too long")
119		}
120		data = make([]byte, 0, len(d)+1)
121		data = append(data, d...)
122		data = append(data, byte(0))
123	}
124	mac, err := primitive.ComputeMAC(data)
125	if err != nil {
126		m.computeLogger.LogFailure()
127		return nil, err
128	}
129	m.computeLogger.Log(primary.KeyID, len(data))
130	if len(primary.Prefix) == 0 {
131		return mac, nil
132	}
133	output := make([]byte, 0, len(primary.Prefix)+len(mac))
134	output = append(output, primary.Prefix...)
135	output = append(output, mac...)
136	return output, nil
137}
138
139var errInvalidMAC = fmt.Errorf("mac_factory: invalid mac")
140
141// VerifyMAC verifies whether the given mac is a correct authentication code
142// for the given data.
143func (m *wrappedMAC) VerifyMAC(mac, data []byte) error {
144	// This also rejects raw MAC with size of 4 bytes or fewer. Those MACs are
145	// clearly insecure, thus should be discouraged.
146	prefixSize := cryptofmt.NonRawPrefixSize
147	if len(mac) <= prefixSize {
148		m.verifyLogger.LogFailure()
149		return errInvalidMAC
150	}
151
152	// try non raw keys
153	prefix := mac[:prefixSize]
154	macNoPrefix := mac[prefixSize:]
155	entries, err := m.ps.EntriesForPrefix(string(prefix))
156	if err == nil {
157		for i := 0; i < len(entries); i++ {
158			entry := entries[i]
159			p, ok := (entry.Primitive).(tink.MAC)
160			if !ok {
161				return fmt.Errorf("mac_factory internal error: not a MAC primitive")
162			}
163			if entry.PrefixType == tinkpb.OutputPrefixType_LEGACY {
164				d := data
165				if len(d) >= maxInt {
166					m.verifyLogger.LogFailure()
167					return fmt.Errorf("mac_factory: data too long")
168				}
169				data = make([]byte, 0, len(d)+1)
170				data = append(data, d...)
171				data = append(data, byte(0))
172			}
173			if err = p.VerifyMAC(macNoPrefix, data); err == nil {
174				m.verifyLogger.Log(entry.KeyID, len(data))
175				return nil
176			}
177		}
178	}
179
180	// try raw keys
181	entries, err = m.ps.RawEntries()
182	if err == nil {
183		for i := 0; i < len(entries); i++ {
184			p, ok := (entries[i].Primitive).(tink.MAC)
185			if !ok {
186				return fmt.Errorf("mac_factory internal error: not a MAC primitive")
187			}
188
189			if err = p.VerifyMAC(mac, data); err == nil {
190				m.verifyLogger.Log(entries[i].KeyID, len(data))
191				return nil
192			}
193		}
194	}
195
196	// nothing worked
197	m.verifyLogger.LogFailure()
198	return errInvalidMAC
199}
200