1// Copyright (c) 2020, Google Inc. 2// 3// Permission to use, copy, modify, and/or distribute this software for any 4// purpose with or without fee is hereby granted, provided that the above 5// copyright notice and this permission notice appear in all copies. 6// 7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 14 15package subprocess 16 17import ( 18 "bytes" 19 "encoding/hex" 20 "encoding/json" 21 "fmt" 22) 23 24// The following structures reflect the JSON of ACVP KDF tests. See 25// https://pages.nist.gov/ACVP/draft-celi-acvp-kdf-tls.html#name-test-vectors 26 27type kdfTestVectorSet struct { 28 Groups []kdfTestGroup `json:"testGroups"` 29} 30 31type kdfTestGroup struct { 32 ID uint64 `json:"tgId"` 33 // KDFMode can take the values "counter", "feedback", or 34 // "double pipeline iteration". 35 KDFMode string `json:"kdfMode"` 36 MACMode string `json:"macMode"` 37 CounterLocation string `json:"counterLocation"` 38 OutputBits uint32 `json:"keyOutLength"` 39 CounterBits uint32 `json:"counterLength"` 40 ZeroIV bool `json:"zeroLengthIv"` 41 42 Tests []struct { 43 ID uint64 `json:"tcId"` 44 Key string `json:"keyIn"` 45 Deferred bool `json:"deferred"` 46 } 47} 48 49type kdfTestGroupResponse struct { 50 ID uint64 `json:"tgId"` 51 Tests []kdfTestResponse `json:"tests"` 52} 53 54type kdfTestResponse struct { 55 ID uint64 `json:"tcId"` 56 KeyIn string `json:"keyIn,omitempty"` 57 FixedData string `json:"fixedData"` 58 KeyOut string `json:"keyOut"` 59} 60 61type kdfPrimitive struct{} 62 63func (k *kdfPrimitive) Process(vectorSet []byte, m Transactable) (interface{}, error) { 64 var parsed kdfTestVectorSet 65 if err := json.Unmarshal(vectorSet, &parsed); err != nil { 66 return nil, err 67 } 68 69 var respGroups []kdfTestGroupResponse 70 for _, group := range parsed.Groups { 71 groupResp := kdfTestGroupResponse{ID: group.ID} 72 73 if group.OutputBits%8 != 0 { 74 return nil, fmt.Errorf("%d bit key in test group %d: fractional bytes not supported", group.OutputBits, group.ID) 75 } 76 77 if group.KDFMode != "counter" { 78 // feedback mode would need the IV to be handled. 79 // double-pipeline mode is not useful. 80 return nil, fmt.Errorf("KDF mode %q not supported", group.KDFMode) 81 } 82 83 switch group.CounterLocation { 84 case "after fixed data", "before fixed data": 85 break 86 default: 87 return nil, fmt.Errorf("Label location %q not supported", group.CounterLocation) 88 } 89 90 counterBits := uint32le(group.CounterBits) 91 outputBytes := uint32le(group.OutputBits / 8) 92 93 for _, test := range group.Tests { 94 testResp := kdfTestResponse{ID: test.ID} 95 96 var key []byte 97 if test.Deferred { 98 if len(test.Key) != 0 { 99 return nil, fmt.Errorf("key provided in deferred test case %d/%d", group.ID, test.ID) 100 } 101 } else { 102 var err error 103 if key, err = hex.DecodeString(test.Key); err != nil { 104 return nil, fmt.Errorf("failed to decode Key in test case %d/%d: %v", group.ID, test.ID, err) 105 } 106 } 107 108 // Make the call to the crypto module. 109 resp, err := m.Transact("KDF-counter", 3, outputBytes, []byte(group.MACMode), []byte(group.CounterLocation), key, counterBits) 110 if err != nil { 111 return nil, fmt.Errorf("wrapper KDF operation failed: %s", err) 112 } 113 114 // Parse results. 115 testResp.ID = test.ID 116 if test.Deferred { 117 testResp.KeyIn = hex.EncodeToString(resp[0]) 118 } 119 testResp.FixedData = hex.EncodeToString(resp[1]) 120 testResp.KeyOut = hex.EncodeToString(resp[2]) 121 122 if !test.Deferred && !bytes.Equal(resp[0], key) { 123 return nil, fmt.Errorf("wrapper returned a different key for non-deferred KDF operation") 124 } 125 126 groupResp.Tests = append(groupResp.Tests, testResp) 127 } 128 respGroups = append(respGroups, groupResp) 129 } 130 131 return respGroups, nil 132} 133