blob: 7be3162696da35da68fd2ebbe0c0be53bd7a40bc [file] [log] [blame]
Pete Bentley0c61efe2019-08-13 09:32:23 +01001package 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}