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