1*e7b1675dSTing-Kang Chang// Copyright 2018 Google LLC 2*e7b1675dSTing-Kang Chang// 3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License"); 4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License. 5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at 6*e7b1675dSTing-Kang Chang// 7*e7b1675dSTing-Kang Chang// http://www.apache.org/licenses/LICENSE-2.0 8*e7b1675dSTing-Kang Chang// 9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software 10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS, 11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and 13*e7b1675dSTing-Kang Chang// limitations under the License. 14*e7b1675dSTing-Kang Chang// 15*e7b1675dSTing-Kang Chang//////////////////////////////////////////////////////////////////////////////// 16*e7b1675dSTing-Kang Chang 17*e7b1675dSTing-Kang Changpackage mac 18*e7b1675dSTing-Kang Chang 19*e7b1675dSTing-Kang Changimport ( 20*e7b1675dSTing-Kang Chang "fmt" 21*e7b1675dSTing-Kang Chang 22*e7b1675dSTing-Kang Chang "github.com/google/tink/go/core/cryptofmt" 23*e7b1675dSTing-Kang Chang "github.com/google/tink/go/core/primitiveset" 24*e7b1675dSTing-Kang Chang "github.com/google/tink/go/internal/internalregistry" 25*e7b1675dSTing-Kang Chang "github.com/google/tink/go/internal/monitoringutil" 26*e7b1675dSTing-Kang Chang "github.com/google/tink/go/keyset" 27*e7b1675dSTing-Kang Chang "github.com/google/tink/go/monitoring" 28*e7b1675dSTing-Kang Chang "github.com/google/tink/go/tink" 29*e7b1675dSTing-Kang Chang tinkpb "github.com/google/tink/go/proto/tink_go_proto" 30*e7b1675dSTing-Kang Chang) 31*e7b1675dSTing-Kang Chang 32*e7b1675dSTing-Kang Changconst ( 33*e7b1675dSTing-Kang Chang intSize = 32 << (^uint(0) >> 63) // 32 or 64 34*e7b1675dSTing-Kang Chang maxInt = 1<<(intSize-1) - 1 35*e7b1675dSTing-Kang Chang) 36*e7b1675dSTing-Kang Chang 37*e7b1675dSTing-Kang Chang// New creates a MAC primitive from the given keyset handle. 38*e7b1675dSTing-Kang Changfunc New(handle *keyset.Handle) (tink.MAC, error) { 39*e7b1675dSTing-Kang Chang ps, err := handle.Primitives() 40*e7b1675dSTing-Kang Chang if err != nil { 41*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("mac_factory: cannot obtain primitive set: %s", err) 42*e7b1675dSTing-Kang Chang } 43*e7b1675dSTing-Kang Chang return newWrappedMAC(ps) 44*e7b1675dSTing-Kang Chang} 45*e7b1675dSTing-Kang Chang 46*e7b1675dSTing-Kang Chang// wrappedMAC is a MAC implementation that uses the underlying primitive set to compute and 47*e7b1675dSTing-Kang Chang// verify MACs. 48*e7b1675dSTing-Kang Changtype wrappedMAC struct { 49*e7b1675dSTing-Kang Chang ps *primitiveset.PrimitiveSet 50*e7b1675dSTing-Kang Chang computeLogger monitoring.Logger 51*e7b1675dSTing-Kang Chang verifyLogger monitoring.Logger 52*e7b1675dSTing-Kang Chang} 53*e7b1675dSTing-Kang Chang 54*e7b1675dSTing-Kang Changvar _ (tink.MAC) = (*wrappedMAC)(nil) 55*e7b1675dSTing-Kang Chang 56*e7b1675dSTing-Kang Changfunc newWrappedMAC(ps *primitiveset.PrimitiveSet) (*wrappedMAC, error) { 57*e7b1675dSTing-Kang Chang if _, ok := (ps.Primary.Primitive).(tink.MAC); !ok { 58*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("mac_factory: not a MAC primitive") 59*e7b1675dSTing-Kang Chang } 60*e7b1675dSTing-Kang Chang for _, primitives := range ps.Entries { 61*e7b1675dSTing-Kang Chang for _, p := range primitives { 62*e7b1675dSTing-Kang Chang if _, ok := (p.Primitive).(tink.MAC); !ok { 63*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("mac_factory: not an MAC primitive") 64*e7b1675dSTing-Kang Chang } 65*e7b1675dSTing-Kang Chang } 66*e7b1675dSTing-Kang Chang } 67*e7b1675dSTing-Kang Chang computeLogger, verifyLogger, err := createLoggers(ps) 68*e7b1675dSTing-Kang Chang if err != nil { 69*e7b1675dSTing-Kang Chang return nil, err 70*e7b1675dSTing-Kang Chang } 71*e7b1675dSTing-Kang Chang return &wrappedMAC{ 72*e7b1675dSTing-Kang Chang ps: ps, 73*e7b1675dSTing-Kang Chang computeLogger: computeLogger, 74*e7b1675dSTing-Kang Chang verifyLogger: verifyLogger, 75*e7b1675dSTing-Kang Chang }, nil 76*e7b1675dSTing-Kang Chang} 77*e7b1675dSTing-Kang Chang 78*e7b1675dSTing-Kang Changfunc createLoggers(ps *primitiveset.PrimitiveSet) (monitoring.Logger, monitoring.Logger, error) { 79*e7b1675dSTing-Kang Chang if len(ps.Annotations) == 0 { 80*e7b1675dSTing-Kang Chang return &monitoringutil.DoNothingLogger{}, &monitoringutil.DoNothingLogger{}, nil 81*e7b1675dSTing-Kang Chang } 82*e7b1675dSTing-Kang Chang client := internalregistry.GetMonitoringClient() 83*e7b1675dSTing-Kang Chang keysetInfo, err := monitoringutil.KeysetInfoFromPrimitiveSet(ps) 84*e7b1675dSTing-Kang Chang if err != nil { 85*e7b1675dSTing-Kang Chang return nil, nil, err 86*e7b1675dSTing-Kang Chang } 87*e7b1675dSTing-Kang Chang computeLogger, err := client.NewLogger(&monitoring.Context{ 88*e7b1675dSTing-Kang Chang Primitive: "mac", 89*e7b1675dSTing-Kang Chang APIFunction: "compute", 90*e7b1675dSTing-Kang Chang KeysetInfo: keysetInfo, 91*e7b1675dSTing-Kang Chang }) 92*e7b1675dSTing-Kang Chang if err != nil { 93*e7b1675dSTing-Kang Chang return nil, nil, err 94*e7b1675dSTing-Kang Chang } 95*e7b1675dSTing-Kang Chang verifyLogger, err := client.NewLogger(&monitoring.Context{ 96*e7b1675dSTing-Kang Chang Primitive: "mac", 97*e7b1675dSTing-Kang Chang APIFunction: "verify", 98*e7b1675dSTing-Kang Chang KeysetInfo: keysetInfo, 99*e7b1675dSTing-Kang Chang }) 100*e7b1675dSTing-Kang Chang if err != nil { 101*e7b1675dSTing-Kang Chang return nil, nil, err 102*e7b1675dSTing-Kang Chang } 103*e7b1675dSTing-Kang Chang return computeLogger, verifyLogger, nil 104*e7b1675dSTing-Kang Chang} 105*e7b1675dSTing-Kang Chang 106*e7b1675dSTing-Kang Chang// ComputeMAC calculates a MAC over the given data using the primary primitive 107*e7b1675dSTing-Kang Chang// and returns the concatenation of the primary's identifier and the calculated mac. 108*e7b1675dSTing-Kang Changfunc (m *wrappedMAC) ComputeMAC(data []byte) ([]byte, error) { 109*e7b1675dSTing-Kang Chang primary := m.ps.Primary 110*e7b1675dSTing-Kang Chang primitive, ok := (primary.Primitive).(tink.MAC) 111*e7b1675dSTing-Kang Chang if !ok { 112*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("mac_factory: not a MAC primitive") 113*e7b1675dSTing-Kang Chang } 114*e7b1675dSTing-Kang Chang if m.ps.Primary.PrefixType == tinkpb.OutputPrefixType_LEGACY { 115*e7b1675dSTing-Kang Chang d := data 116*e7b1675dSTing-Kang Chang if len(d) >= maxInt { 117*e7b1675dSTing-Kang Chang m.computeLogger.LogFailure() 118*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("mac_factory: data too long") 119*e7b1675dSTing-Kang Chang } 120*e7b1675dSTing-Kang Chang data = make([]byte, 0, len(d)+1) 121*e7b1675dSTing-Kang Chang data = append(data, d...) 122*e7b1675dSTing-Kang Chang data = append(data, byte(0)) 123*e7b1675dSTing-Kang Chang } 124*e7b1675dSTing-Kang Chang mac, err := primitive.ComputeMAC(data) 125*e7b1675dSTing-Kang Chang if err != nil { 126*e7b1675dSTing-Kang Chang m.computeLogger.LogFailure() 127*e7b1675dSTing-Kang Chang return nil, err 128*e7b1675dSTing-Kang Chang } 129*e7b1675dSTing-Kang Chang m.computeLogger.Log(primary.KeyID, len(data)) 130*e7b1675dSTing-Kang Chang if len(primary.Prefix) == 0 { 131*e7b1675dSTing-Kang Chang return mac, nil 132*e7b1675dSTing-Kang Chang } 133*e7b1675dSTing-Kang Chang output := make([]byte, 0, len(primary.Prefix)+len(mac)) 134*e7b1675dSTing-Kang Chang output = append(output, primary.Prefix...) 135*e7b1675dSTing-Kang Chang output = append(output, mac...) 136*e7b1675dSTing-Kang Chang return output, nil 137*e7b1675dSTing-Kang Chang} 138*e7b1675dSTing-Kang Chang 139*e7b1675dSTing-Kang Changvar errInvalidMAC = fmt.Errorf("mac_factory: invalid mac") 140*e7b1675dSTing-Kang Chang 141*e7b1675dSTing-Kang Chang// VerifyMAC verifies whether the given mac is a correct authentication code 142*e7b1675dSTing-Kang Chang// for the given data. 143*e7b1675dSTing-Kang Changfunc (m *wrappedMAC) VerifyMAC(mac, data []byte) error { 144*e7b1675dSTing-Kang Chang // This also rejects raw MAC with size of 4 bytes or fewer. Those MACs are 145*e7b1675dSTing-Kang Chang // clearly insecure, thus should be discouraged. 146*e7b1675dSTing-Kang Chang prefixSize := cryptofmt.NonRawPrefixSize 147*e7b1675dSTing-Kang Chang if len(mac) <= prefixSize { 148*e7b1675dSTing-Kang Chang m.verifyLogger.LogFailure() 149*e7b1675dSTing-Kang Chang return errInvalidMAC 150*e7b1675dSTing-Kang Chang } 151*e7b1675dSTing-Kang Chang 152*e7b1675dSTing-Kang Chang // try non raw keys 153*e7b1675dSTing-Kang Chang prefix := mac[:prefixSize] 154*e7b1675dSTing-Kang Chang macNoPrefix := mac[prefixSize:] 155*e7b1675dSTing-Kang Chang entries, err := m.ps.EntriesForPrefix(string(prefix)) 156*e7b1675dSTing-Kang Chang if err == nil { 157*e7b1675dSTing-Kang Chang for i := 0; i < len(entries); i++ { 158*e7b1675dSTing-Kang Chang entry := entries[i] 159*e7b1675dSTing-Kang Chang p, ok := (entry.Primitive).(tink.MAC) 160*e7b1675dSTing-Kang Chang if !ok { 161*e7b1675dSTing-Kang Chang return fmt.Errorf("mac_factory internal error: not a MAC primitive") 162*e7b1675dSTing-Kang Chang } 163*e7b1675dSTing-Kang Chang if entry.PrefixType == tinkpb.OutputPrefixType_LEGACY { 164*e7b1675dSTing-Kang Chang d := data 165*e7b1675dSTing-Kang Chang if len(d) >= maxInt { 166*e7b1675dSTing-Kang Chang m.verifyLogger.LogFailure() 167*e7b1675dSTing-Kang Chang return fmt.Errorf("mac_factory: data too long") 168*e7b1675dSTing-Kang Chang } 169*e7b1675dSTing-Kang Chang data = make([]byte, 0, len(d)+1) 170*e7b1675dSTing-Kang Chang data = append(data, d...) 171*e7b1675dSTing-Kang Chang data = append(data, byte(0)) 172*e7b1675dSTing-Kang Chang } 173*e7b1675dSTing-Kang Chang if err = p.VerifyMAC(macNoPrefix, data); err == nil { 174*e7b1675dSTing-Kang Chang m.verifyLogger.Log(entry.KeyID, len(data)) 175*e7b1675dSTing-Kang Chang return nil 176*e7b1675dSTing-Kang Chang } 177*e7b1675dSTing-Kang Chang } 178*e7b1675dSTing-Kang Chang } 179*e7b1675dSTing-Kang Chang 180*e7b1675dSTing-Kang Chang // try raw keys 181*e7b1675dSTing-Kang Chang entries, err = m.ps.RawEntries() 182*e7b1675dSTing-Kang Chang if err == nil { 183*e7b1675dSTing-Kang Chang for i := 0; i < len(entries); i++ { 184*e7b1675dSTing-Kang Chang p, ok := (entries[i].Primitive).(tink.MAC) 185*e7b1675dSTing-Kang Chang if !ok { 186*e7b1675dSTing-Kang Chang return fmt.Errorf("mac_factory internal error: not a MAC primitive") 187*e7b1675dSTing-Kang Chang } 188*e7b1675dSTing-Kang Chang 189*e7b1675dSTing-Kang Chang if err = p.VerifyMAC(mac, data); err == nil { 190*e7b1675dSTing-Kang Chang m.verifyLogger.Log(entries[i].KeyID, len(data)) 191*e7b1675dSTing-Kang Chang return nil 192*e7b1675dSTing-Kang Chang } 193*e7b1675dSTing-Kang Chang } 194*e7b1675dSTing-Kang Chang } 195*e7b1675dSTing-Kang Chang 196*e7b1675dSTing-Kang Chang // nothing worked 197*e7b1675dSTing-Kang Chang m.verifyLogger.LogFailure() 198*e7b1675dSTing-Kang Chang return errInvalidMAC 199*e7b1675dSTing-Kang Chang} 200