1// Copyright 2019 The BoringSSL Authors 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// https://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 15package subprocess 16 17import ( 18 "encoding/hex" 19 "encoding/json" 20 "fmt" 21 "strconv" 22) 23 24// The following structures reflect the JSON of ACVP HMAC tests. See 25// https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#name-test-vectors 26 27type hmacTestVectorSet struct { 28 Groups []hmacTestGroup `json:"testGroups"` 29} 30 31type hmacTestGroup struct { 32 ID uint64 `json:"tgId"` 33 Type string `json:"testType"` 34 MsgBits int `json:"msgLen"` 35 KeyBits int `json:"keyLen"` // maximum possible value is 524288 36 MACBits int `json:"macLen"` // maximum possible value is 512 37 Tests []struct { 38 ID uint64 `json:"tcId"` 39 KeyHex string `json:"key"` 40 MsgHex string `json:"msg"` 41 } `json:"tests"` 42} 43 44type hmacTestGroupResponse struct { 45 ID uint64 `json:"tgId"` 46 Tests []hmacTestResponse `json:"tests"` 47} 48 49type hmacTestResponse struct { 50 ID uint64 `json:"tcId"` 51 MACHex string `json:"mac,omitempty"` 52} 53 54// hmacPrimitive implements an ACVP algorithm by making requests to the 55// subprocess to HMAC strings with the given key. 56type hmacPrimitive struct { 57 // algo is the ACVP name for this algorithm and also the command name 58 // given to the subprocess to HMAC with this hash function. 59 algo string 60 mdLen int // mdLen is the number of bytes of output that the underlying hash produces. 61} 62 63// hmac uses the subprocess to compute HMAC and returns the result. 64func (h *hmacPrimitive) hmac(msg []byte, key []byte, outBits int, m Transactable) []byte { 65 if outBits%8 != 0 { 66 panic("fractional-byte output length requested: " + strconv.Itoa(outBits)) 67 } 68 outBytes := outBits / 8 69 result, err := m.Transact(h.algo, 1, msg, key) 70 if err != nil { 71 panic("HMAC operation failed: " + err.Error()) 72 } 73 if l := len(result[0]); l < outBytes { 74 panic(fmt.Sprintf("HMAC result too short: %d bytes but wanted %d", l, outBytes)) 75 } 76 return result[0][:outBytes] 77} 78 79func (h *hmacPrimitive) Process(vectorSet []byte, m Transactable) (any, error) { 80 var parsed hmacTestVectorSet 81 if err := json.Unmarshal(vectorSet, &parsed); err != nil { 82 return nil, err 83 } 84 85 var ret []hmacTestGroupResponse 86 // See 87 // https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#name-test-vectors 88 // for details about the tests. 89 for _, group := range parsed.Groups { 90 group := group 91 response := hmacTestGroupResponse{ 92 ID: group.ID, 93 } 94 if group.MACBits > h.mdLen*8 { 95 return nil, fmt.Errorf("test group %d specifies MAC length should be %d, but maximum possible length is %d", group.ID, group.MACBits, h.mdLen*8) 96 } 97 if group.MACBits%8 != 0 { 98 return nil, fmt.Errorf("fractional-byte HMAC output length requested: %d", group.MACBits) 99 } 100 outBytes := group.MACBits / 8 101 102 for _, test := range group.Tests { 103 test := test 104 105 if len(test.MsgHex)*4 != group.MsgBits { 106 return nil, fmt.Errorf("test case %d/%d contains hex message of length %d but specifies a bit length of %d", group.ID, test.ID, len(test.MsgHex), group.MsgBits) 107 } 108 msg, err := hex.DecodeString(test.MsgHex) 109 if err != nil { 110 return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err) 111 } 112 113 if len(test.KeyHex)*4 != group.KeyBits { 114 return nil, fmt.Errorf("test case %d/%d contains hex key of length %d but specifies a bit length of %d", group.ID, test.ID, len(test.KeyHex), group.KeyBits) 115 } 116 key, err := hex.DecodeString(test.KeyHex) 117 if err != nil { 118 return nil, fmt.Errorf("failed to decode key in test case %d/%d: %s", group.ID, test.ID, err) 119 } 120 121 m.TransactAsync(h.algo, 1, [][]byte{msg, key}, func(result [][]byte) error { 122 if l := len(result[0]); l != outBytes { 123 return fmt.Errorf("incorrect HMAC length: %d bytes but wanted %d", l, outBytes) 124 } 125 126 // https://pages.nist.gov/ACVP/draft-fussell-acvp-mac.html#name-test-vectors 127 response.Tests = append(response.Tests, hmacTestResponse{ 128 ID: test.ID, 129 MACHex: hex.EncodeToString(result[0]), 130 }) 131 return nil 132 }) 133 } 134 135 m.Barrier(func() { 136 ret = append(ret, response) 137 }) 138 } 139 140 if err := m.Flush(); err != nil { 141 return nil, err 142 } 143 144 return ret, nil 145} 146