blob: 1f34d1a9063c60bf85f69cf71d828ad1a06e069c [file] [log] [blame]
Adam Langleyd709b0d2019-11-05 11:37:22 -08001// Copyright (c) 2019, 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
Adam Langleyb7f0c1b2019-07-09 18:02:14 -070015package subprocess
16
17import (
18 "encoding/hex"
19 "encoding/json"
20 "fmt"
21)
22
23// The following structures reflect the JSON of ACVP hash tests. See
Adam Langleyf0e5ea22021-05-10 13:04:58 -070024// https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#name-test-vectors
Adam Langleyb7f0c1b2019-07-09 18:02:14 -070025
26type hashTestVectorSet struct {
27 Groups []hashTestGroup `json:"testGroups"`
28}
29
30type hashTestGroup struct {
31 ID uint64 `json:"tgId"`
32 Type string `json:"testType"`
33 Tests []struct {
34 ID uint64 `json:"tcId"`
35 BitLength uint64 `json:"len"`
36 MsgHex string `json:"msg"`
37 } `json:"tests"`
38}
39
40type hashTestGroupResponse struct {
41 ID uint64 `json:"tgId"`
42 Tests []hashTestResponse `json:"tests"`
43}
44
45type hashTestResponse struct {
46 ID uint64 `json:"tcId"`
47 DigestHex string `json:"md,omitempty"`
48 MCTResults []hashMCTResult `json:"resultsArray,omitempty"`
49}
50
51type hashMCTResult struct {
52 DigestHex string `json:"md"`
53}
54
55// hashPrimitive implements an ACVP algorithm by making requests to the
56// subprocess to hash strings.
57type hashPrimitive struct {
58 // algo is the ACVP name for this algorithm and also the command name
59 // given to the subprocess to hash with this hash function.
60 algo string
61 // size is the number of bytes of digest that the hash produces.
62 size int
Adam Langleyb7f0c1b2019-07-09 18:02:14 -070063}
64
David Benjamin77b6f252023-05-02 10:14:11 -040065func (h *hashPrimitive) Process(vectorSet []byte, m Transactable) (any, error) {
Adam Langleyb7f0c1b2019-07-09 18:02:14 -070066 var parsed hashTestVectorSet
67 if err := json.Unmarshal(vectorSet, &parsed); err != nil {
68 return nil, err
69 }
70
71 var ret []hashTestGroupResponse
72 // See
Adam Langleyf0e5ea22021-05-10 13:04:58 -070073 // https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#name-test-vectors
Adam Langleyb7f0c1b2019-07-09 18:02:14 -070074 // for details about the tests.
75 for _, group := range parsed.Groups {
76 response := hashTestGroupResponse{
77 ID: group.ID,
78 }
79
80 for _, test := range group.Tests {
81 if uint64(len(test.MsgHex))*4 != test.BitLength {
82 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)
83 }
84 msg, err := hex.DecodeString(test.MsgHex)
85 if err != nil {
86 return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
87 }
88
89 // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3
90 switch group.Type {
91 case "AFT":
Adam Langleye24491a2022-11-25 21:23:03 +000092 m.TransactAsync(h.algo, 1, [][]byte{msg}, func(result [][]byte) error {
93 response.Tests = append(response.Tests, hashTestResponse{
94 ID: test.ID,
95 DigestHex: hex.EncodeToString(result[0]),
96 })
97 return nil
Adam Langleyb7f0c1b2019-07-09 18:02:14 -070098 })
99
100 case "MCT":
101 if len(msg) != h.size {
102 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)
103 }
104
105 testResponse := hashTestResponse{ID: test.ID}
106
Adam Langley9fc61742021-05-06 12:15:07 -0700107 digest := msg
Adam Langleyb7f0c1b2019-07-09 18:02:14 -0700108 for i := 0; i < 100; i++ {
Adam Langley9fc61742021-05-06 12:15:07 -0700109 result, err := m.Transact(h.algo+"/MCT", 1, digest)
110 if err != nil {
111 panic(h.algo + " hash operation failed: " + err.Error())
Adam Langleyb7f0c1b2019-07-09 18:02:14 -0700112 }
113
Adam Langley9fc61742021-05-06 12:15:07 -0700114 digest = result[0]
Adam Langleyb7f0c1b2019-07-09 18:02:14 -0700115 testResponse.MCTResults = append(testResponse.MCTResults, hashMCTResult{hex.EncodeToString(digest)})
Adam Langleyb7f0c1b2019-07-09 18:02:14 -0700116 }
117
118 response.Tests = append(response.Tests, testResponse)
119
120 default:
121 return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
122 }
123 }
124
Adam Langleye24491a2022-11-25 21:23:03 +0000125 m.Barrier(func() {
126 ret = append(ret, response)
127 })
128 }
129
130 if err := m.Flush(); err != nil {
131 return nil, err
Adam Langleyb7f0c1b2019-07-09 18:02:14 -0700132 }
133
134 return ret, nil
135}