1package subprocess 2 3import ( 4 "encoding/hex" 5 "encoding/json" 6 "fmt" 7) 8 9// The following structures reflect the JSON of ACVP hash tests. See 10// https://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#test_vectors 11 12type hashTestVectorSet struct { 13 Groups []hashTestGroup `json:"testGroups"` 14} 15 16type hashTestGroup struct { 17 ID uint64 `json:"tgId"` 18 Type string `json:"testType"` 19 Tests []struct { 20 ID uint64 `json:"tcId"` 21 BitLength uint64 `json:"len"` 22 MsgHex string `json:"msg"` 23 } `json:"tests"` 24} 25 26type hashTestGroupResponse struct { 27 ID uint64 `json:"tgId"` 28 Tests []hashTestResponse `json:"tests"` 29} 30 31type hashTestResponse struct { 32 ID uint64 `json:"tcId"` 33 DigestHex string `json:"md,omitempty"` 34 MCTResults []hashMCTResult `json:"resultsArray,omitempty"` 35} 36 37type hashMCTResult struct { 38 DigestHex string `json:"md"` 39} 40 41// hashPrimitive implements an ACVP algorithm by making requests to the 42// subprocess to hash strings. 43type hashPrimitive struct { 44 // algo is the ACVP name for this algorithm and also the command name 45 // given to the subprocess to hash with this hash function. 46 algo string 47 // size is the number of bytes of digest that the hash produces. 48 size int 49 m *Subprocess 50} 51 52// hash uses the subprocess to hash msg and returns the digest. 53func (h *hashPrimitive) hash(msg []byte) []byte { 54 result, err := h.m.transact(h.algo, 1, msg) 55 if err != nil { 56 panic("hash operation failed: " + err.Error()) 57 } 58 return result[0] 59} 60 61func (h *hashPrimitive) Process(vectorSet []byte) (interface{}, error) { 62 var parsed hashTestVectorSet 63 if err := json.Unmarshal(vectorSet, &parsed); err != nil { 64 return nil, err 65 } 66 67 var ret []hashTestGroupResponse 68 // See 69 // https://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3 70 // for details about the tests. 71 for _, group := range parsed.Groups { 72 response := hashTestGroupResponse{ 73 ID: group.ID, 74 } 75 76 for _, test := range group.Tests { 77 if uint64(len(test.MsgHex))*4 != test.BitLength { 78 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), test.BitLength) 79 } 80 msg, err := hex.DecodeString(test.MsgHex) 81 if err != nil { 82 return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err) 83 } 84 85 // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3 86 switch group.Type { 87 case "AFT": 88 response.Tests = append(response.Tests, hashTestResponse{ 89 ID: test.ID, 90 DigestHex: hex.EncodeToString(h.hash(msg)), 91 }) 92 93 case "MCT": 94 if len(msg) != h.size { 95 return nil, fmt.Errorf("MCT test case %d/%d contains message of length %d but the digest length is %d", group.ID, test.ID, len(msg), h.size) 96 } 97 98 testResponse := hashTestResponse{ID: test.ID} 99 100 buf := make([]byte, 3*h.size) 101 var digest []byte 102 for i := 0; i < 100; i++ { 103 copy(buf, msg) 104 copy(buf[h.size:], msg) 105 copy(buf[2*h.size:], msg) 106 for j := 0; j < 1000; j++ { 107 digest = h.hash(buf) 108 copy(buf, buf[h.size:]) 109 copy(buf[2*h.size:], digest) 110 } 111 112 testResponse.MCTResults = append(testResponse.MCTResults, hashMCTResult{hex.EncodeToString(digest)}) 113 msg = digest 114 } 115 116 response.Tests = append(response.Tests, testResponse) 117 118 default: 119 return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type) 120 } 121 } 122 123 ret = append(ret, response) 124 } 125 126 return ret, nil 127} 128