Pete Bentley | 0c61efe | 2019-08-13 09:32:23 +0100 | [diff] [blame^] | 1 | package subprocess |
| 2 | |
| 3 | import ( |
| 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 | |
| 12 | type hashTestVectorSet struct { |
| 13 | Groups []hashTestGroup `json:"testGroups"` |
| 14 | } |
| 15 | |
| 16 | type 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 | |
| 26 | type hashTestGroupResponse struct { |
| 27 | ID uint64 `json:"tgId"` |
| 28 | Tests []hashTestResponse `json:"tests"` |
| 29 | } |
| 30 | |
| 31 | type hashTestResponse struct { |
| 32 | ID uint64 `json:"tcId"` |
| 33 | DigestHex string `json:"md,omitempty"` |
| 34 | MCTResults []hashMCTResult `json:"resultsArray,omitempty"` |
| 35 | } |
| 36 | |
| 37 | type 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. |
| 43 | type 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. |
| 53 | func (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 | |
| 61 | func (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 | } |