summaryrefslogtreecommitdiff
path: root/src/util/fipstools/acvp/acvptool/subprocess/hash.go
blob: 7be3162696da35da68fd2ebbe0c0be53bd7a40bc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package subprocess

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
)

// The following structures reflect the JSON of ACVP hash tests. See
// https://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#test_vectors

type hashTestVectorSet struct {
	Groups []hashTestGroup `json:"testGroups"`
}

type hashTestGroup struct {
	ID    uint64 `json:"tgId"`
	Type  string `json:"testType"`
	Tests []struct {
		ID        uint64 `json:"tcId"`
		BitLength uint64 `json:"len"`
		MsgHex    string `json:"msg"`
	} `json:"tests"`
}

type hashTestGroupResponse struct {
	ID    uint64             `json:"tgId"`
	Tests []hashTestResponse `json:"tests"`
}

type hashTestResponse struct {
	ID         uint64          `json:"tcId"`
	DigestHex  string          `json:"md,omitempty"`
	MCTResults []hashMCTResult `json:"resultsArray,omitempty"`
}

type hashMCTResult struct {
	DigestHex string `json:"md"`
}

// hashPrimitive implements an ACVP algorithm by making requests to the
// subprocess to hash strings.
type hashPrimitive struct {
	// algo is the ACVP name for this algorithm and also the command name
	// given to the subprocess to hash with this hash function.
	algo string
	// size is the number of bytes of digest that the hash produces.
	size int
	m    *Subprocess
}

// hash uses the subprocess to hash msg and returns the digest.
func (h *hashPrimitive) hash(msg []byte) []byte {
	result, err := h.m.transact(h.algo, 1, msg)
	if err != nil {
		panic("hash operation failed: " + err.Error())
	}
	return result[0]
}

func (h *hashPrimitive) Process(vectorSet []byte) (interface{}, error) {
	var parsed hashTestVectorSet
	if err := json.Unmarshal(vectorSet, &parsed); err != nil {
		return nil, err
	}

	var ret []hashTestGroupResponse
	// See
	// https://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3
	// for details about the tests.
	for _, group := range parsed.Groups {
		response := hashTestGroupResponse{
			ID: group.ID,
		}

		for _, test := range group.Tests {
			if uint64(len(test.MsgHex))*4 != test.BitLength {
				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)
			}
			msg, err := hex.DecodeString(test.MsgHex)
			if err != nil {
				return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
			}

			// http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3
			switch group.Type {
			case "AFT":
				response.Tests = append(response.Tests, hashTestResponse{
					ID:        test.ID,
					DigestHex: hex.EncodeToString(h.hash(msg)),
				})

			case "MCT":
				if len(msg) != h.size {
					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)
				}

				testResponse := hashTestResponse{ID: test.ID}

				buf := make([]byte, 3*h.size)
				var digest []byte
				for i := 0; i < 100; i++ {
					copy(buf, msg)
					copy(buf[h.size:], msg)
					copy(buf[2*h.size:], msg)
					for j := 0; j < 1000; j++ {
						digest = h.hash(buf)
						copy(buf, buf[h.size:])
						copy(buf[2*h.size:], digest)
					}

					testResponse.MCTResults = append(testResponse.MCTResults, hashMCTResult{hex.EncodeToString(digest)})
					msg = digest
				}

				response.Tests = append(response.Tests, testResponse)

			default:
				return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
			}
		}

		ret = append(ret, response)
	}

	return ret, nil
}