1*e7b1675dSTing-Kang Chang// Copyright 2020 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 Chang// Package subtle provides subtle implementations of the MAC primitive. 18*e7b1675dSTing-Kang Changpackage subtle 19*e7b1675dSTing-Kang Chang 20*e7b1675dSTing-Kang Changimport ( 21*e7b1675dSTing-Kang Chang "crypto/hmac" 22*e7b1675dSTing-Kang Chang "errors" 23*e7b1675dSTing-Kang Chang "fmt" 24*e7b1675dSTing-Kang Chang "hash" 25*e7b1675dSTing-Kang Chang 26*e7b1675dSTing-Kang Chang "github.com/google/tink/go/subtle" 27*e7b1675dSTing-Kang Chang) 28*e7b1675dSTing-Kang Chang 29*e7b1675dSTing-Kang Changconst ( 30*e7b1675dSTing-Kang Chang // Minimum key size in bytes. 31*e7b1675dSTing-Kang Chang minKeySizeInBytes = uint32(16) 32*e7b1675dSTing-Kang Chang 33*e7b1675dSTing-Kang Chang // Minimum tag size in bytes. This provides minimum 80-bit security strength. 34*e7b1675dSTing-Kang Chang minTagSizeInBytes = uint32(10) 35*e7b1675dSTing-Kang Chang) 36*e7b1675dSTing-Kang Chang 37*e7b1675dSTing-Kang Changvar errHMACInvalidInput = errors.New("HMAC: invalid input") 38*e7b1675dSTing-Kang Chang 39*e7b1675dSTing-Kang Chang// HMAC implementation of interface tink.MAC 40*e7b1675dSTing-Kang Changtype HMAC struct { 41*e7b1675dSTing-Kang Chang HashFunc func() hash.Hash 42*e7b1675dSTing-Kang Chang Key []byte 43*e7b1675dSTing-Kang Chang TagSize uint32 44*e7b1675dSTing-Kang Chang} 45*e7b1675dSTing-Kang Chang 46*e7b1675dSTing-Kang Chang// NewHMAC creates a new instance of HMAC with the specified key and tag size. 47*e7b1675dSTing-Kang Changfunc NewHMAC(hashAlg string, key []byte, tagSize uint32) (*HMAC, error) { 48*e7b1675dSTing-Kang Chang keySize := uint32(len(key)) 49*e7b1675dSTing-Kang Chang if err := ValidateHMACParams(hashAlg, keySize, tagSize); err != nil { 50*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("hmac: %s", err) 51*e7b1675dSTing-Kang Chang } 52*e7b1675dSTing-Kang Chang hashFunc := subtle.GetHashFunc(hashAlg) 53*e7b1675dSTing-Kang Chang if hashFunc == nil { 54*e7b1675dSTing-Kang Chang return nil, fmt.Errorf("hmac: invalid hash algorithm") 55*e7b1675dSTing-Kang Chang } 56*e7b1675dSTing-Kang Chang return &HMAC{ 57*e7b1675dSTing-Kang Chang HashFunc: hashFunc, 58*e7b1675dSTing-Kang Chang Key: key, 59*e7b1675dSTing-Kang Chang TagSize: tagSize, 60*e7b1675dSTing-Kang Chang }, nil 61*e7b1675dSTing-Kang Chang} 62*e7b1675dSTing-Kang Chang 63*e7b1675dSTing-Kang Chang// ValidateHMACParams validates parameters of HMAC constructor. 64*e7b1675dSTing-Kang Changfunc ValidateHMACParams(hash string, keySize uint32, tagSize uint32) error { 65*e7b1675dSTing-Kang Chang // validate tag size 66*e7b1675dSTing-Kang Chang digestSize, err := subtle.GetHashDigestSize(hash) 67*e7b1675dSTing-Kang Chang if err != nil { 68*e7b1675dSTing-Kang Chang return err 69*e7b1675dSTing-Kang Chang } 70*e7b1675dSTing-Kang Chang if tagSize > digestSize { 71*e7b1675dSTing-Kang Chang return fmt.Errorf("tag size too big") 72*e7b1675dSTing-Kang Chang } 73*e7b1675dSTing-Kang Chang if tagSize < minTagSizeInBytes { 74*e7b1675dSTing-Kang Chang return fmt.Errorf("tag size too small") 75*e7b1675dSTing-Kang Chang } 76*e7b1675dSTing-Kang Chang // validate key size 77*e7b1675dSTing-Kang Chang if keySize < minKeySizeInBytes { 78*e7b1675dSTing-Kang Chang return fmt.Errorf("key too short") 79*e7b1675dSTing-Kang Chang } 80*e7b1675dSTing-Kang Chang return nil 81*e7b1675dSTing-Kang Chang} 82*e7b1675dSTing-Kang Chang 83*e7b1675dSTing-Kang Chang// ComputeMAC computes message authentication code (MAC) for the given data. 84*e7b1675dSTing-Kang Changfunc (h *HMAC) ComputeMAC(data []byte) ([]byte, error) { 85*e7b1675dSTing-Kang Chang mac := hmac.New(h.HashFunc, h.Key) 86*e7b1675dSTing-Kang Chang if _, err := mac.Write(data); err != nil { 87*e7b1675dSTing-Kang Chang return nil, err 88*e7b1675dSTing-Kang Chang } 89*e7b1675dSTing-Kang Chang tag := mac.Sum(nil) 90*e7b1675dSTing-Kang Chang return tag[:h.TagSize], nil 91*e7b1675dSTing-Kang Chang} 92*e7b1675dSTing-Kang Chang 93*e7b1675dSTing-Kang Chang// VerifyMAC verifies whether the given MAC is a correct message authentication 94*e7b1675dSTing-Kang Chang// code (MAC) the given data. 95*e7b1675dSTing-Kang Changfunc (h *HMAC) VerifyMAC(mac []byte, data []byte) error { 96*e7b1675dSTing-Kang Chang expectedMAC, err := h.ComputeMAC(data) 97*e7b1675dSTing-Kang Chang if err != nil { 98*e7b1675dSTing-Kang Chang return err 99*e7b1675dSTing-Kang Chang } 100*e7b1675dSTing-Kang Chang if hmac.Equal(expectedMAC, mac) { 101*e7b1675dSTing-Kang Chang return nil 102*e7b1675dSTing-Kang Chang } 103*e7b1675dSTing-Kang Chang return errors.New("HMAC: invalid MAC") 104*e7b1675dSTing-Kang Chang} 105