blob: a5ed2cbd68a7163843b81ec06960a5d37e3377f7 [file] [log] [blame]
Pete Bentleya5c947b2019-08-09 14:24:27 +00001package subprocess
2
3import (
4 "encoding/hex"
5 "encoding/json"
6 "fmt"
7)
8
9// blockCipher implements an ACVP algorithm by making requests to the subprocess
10// to encrypt and decrypt with a block cipher.
11type blockCipher struct {
12 algo string
13 blockSize int
14 hasIV bool
15 m *Subprocess
16}
17
18type blockCipherVectorSet struct {
19 Groups []blockCipherTestGroup `json:"testGroups"`
20}
21
22type blockCipherTestGroup struct {
23 ID uint64 `json:"tgId"`
24 Type string `json:"testType"`
25 Direction string `json:"direction"`
26 KeyBits int `json:"keylen"`
27 Tests []struct {
28 ID uint64 `json:"tcId"`
29 PlaintextHex string `json:"pt"`
30 CiphertextHex string `json:"ct"`
31 IVHex string `json:"iv"`
32 KeyHex string `json:"key"`
33 } `json:"tests"`
34}
35
36type blockCipherTestGroupResponse struct {
37 ID uint64 `json:"tgId"`
38 Tests []blockCipherTestResponse `json:"tests"`
39}
40
41type blockCipherTestResponse struct {
42 ID uint64 `json:"tcId"`
43 CiphertextHex string `json:"ct,omitempty"`
44 PlaintextHex string `json:"pt,omitempty"`
45 MCTResults []blockCipherMCTResult `json:"resultsArray,omitempty"`
46}
47
48type blockCipherMCTResult struct {
49 KeyHex string `json:"key"`
50 PlaintextHex string `json:"pt"`
51 CiphertextHex string `json:"ct"`
52 IVHex string `json:"iv,omitempty"`
53}
54
55func (b *blockCipher) Process(vectorSet []byte) (interface{}, error) {
56 var parsed blockCipherVectorSet
57 if err := json.Unmarshal(vectorSet, &parsed); err != nil {
58 return nil, err
59 }
60
61 var ret []blockCipherTestGroupResponse
62 // See
63 // http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-block-ciph-00.html#rfc.section.5.2
64 // for details about the tests.
65 for _, group := range parsed.Groups {
66 response := blockCipherTestGroupResponse{
67 ID: group.ID,
68 }
69
70 var encrypt bool
71 switch group.Direction {
72 case "encrypt":
73 encrypt = true
74 case "decrypt":
75 encrypt = false
76 default:
77 return nil, fmt.Errorf("test group %d has unknown direction %q", group.ID, group.Direction)
78 }
79
80 op := b.algo + "/encrypt"
81 if !encrypt {
82 op = b.algo + "/decrypt"
83 }
84
85 var mct bool
86 switch group.Type {
87 case "AFT":
88 mct = false
89 case "MCT":
90 mct = true
91 default:
92 return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
93 }
94
95 if group.KeyBits%8 != 0 {
96 return nil, fmt.Errorf("test group %d contains non-byte-multiple key length %d", group.ID, group.KeyBits)
97 }
98 keyBytes := group.KeyBits / 8
99
100 for _, test := range group.Tests {
101 if len(test.KeyHex) != keyBytes*2 {
102 return nil, fmt.Errorf("test case %d/%d contains key %q of length %d, but expected %d-bit key", group.ID, test.ID, test.KeyHex, len(test.KeyHex), group.KeyBits)
103 }
104
105 key, err := hex.DecodeString(test.KeyHex)
106 if err != nil {
107 return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
108 }
109
110 var inputHex string
111 if encrypt {
112 inputHex = test.PlaintextHex
113 } else {
114 inputHex = test.CiphertextHex
115 }
116
117 input, err := hex.DecodeString(inputHex)
118 if err != nil {
119 return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
120 }
121
122 if len(input)%b.blockSize != 0 {
123 return nil, fmt.Errorf("test case %d/%d has input of length %d, but expected multiple of %d", group.ID, test.ID, len(input), b.blockSize)
124 }
125
126 var iv []byte
127 if b.hasIV {
128 if iv, err = hex.DecodeString(test.IVHex); err != nil {
129 return nil, fmt.Errorf("failed to decode hex in test case %d/%d: %s", group.ID, test.ID, err)
130 }
131 if len(iv) != b.blockSize {
132 return nil, fmt.Errorf("test case %d/%d has IV of length %d, but expected %d", group.ID, test.ID, len(iv), b.blockSize)
133 }
134 }
135
136 testResp := blockCipherTestResponse{ID: test.ID}
137 if !mct {
138 var result [][]byte
139 var err error
140
141 if b.hasIV {
142 result, err = b.m.transact(op, 1, key, input, iv)
143 } else {
144 result, err = b.m.transact(op, 1, key, input)
145 }
146 if err != nil {
147 panic("block operation failed: " + err.Error())
148 }
149
150 if encrypt {
151 testResp.CiphertextHex = hex.EncodeToString(result[0])
152 } else {
153 testResp.PlaintextHex = hex.EncodeToString(result[0])
154 }
155 } else {
156 for i := 0; i < 100; i++ {
157 var iteration blockCipherMCTResult
158 iteration.KeyHex = hex.EncodeToString(key)
159 if encrypt {
160 iteration.PlaintextHex = hex.EncodeToString(input)
161 } else {
162 iteration.CiphertextHex = hex.EncodeToString(input)
163 }
164
165 var result, prevResult []byte
166 if !b.hasIV {
167 for j := 0; j < 1000; j++ {
168 prevResult = input
169 result, err := b.m.transact(op, 1, key, input)
170 if err != nil {
171 panic("block operation failed")
172 }
173 input = result[0]
174 }
175 result = input
176 } else {
177 iteration.IVHex = hex.EncodeToString(iv)
178
179 var prevInput []byte
180 for j := 0; j < 1000; j++ {
181 prevResult = result
182 if j > 0 {
183 if encrypt {
184 iv = result
185 } else {
186 iv = prevInput
187 }
188 }
189
190 results, err := b.m.transact(op, 1, key, input, iv)
191 if err != nil {
192 panic("block operation failed")
193 }
194 result = results[0]
195
196 prevInput = input
197 if j == 0 {
198 input = iv
199 } else {
200 input = prevResult
201 }
202 }
203 }
204
205 if encrypt {
206 iteration.CiphertextHex = hex.EncodeToString(result)
207 } else {
208 iteration.PlaintextHex = hex.EncodeToString(result)
209 }
210
211 switch keyBytes {
212 case 16:
213 for i := range key {
214 key[i] ^= result[i]
215 }
216 case 24:
217 for i := 0; i < 8; i++ {
218 key[i] ^= prevResult[i+8]
219 }
220 for i := range result {
221 key[i+8] ^= result[i]
222 }
223 case 32:
224 for i, b := range prevResult {
225 key[i] ^= b
226 }
227 for i, b := range result {
228 key[i+16] ^= b
229 }
230 default:
231 panic("unhandled key length")
232 }
233
234 if !b.hasIV {
235 input = result
236 } else {
237 iv = result
238 input = prevResult
239 }
240
241 testResp.MCTResults = append(testResp.MCTResults, iteration)
242 }
243 }
244
245 response.Tests = append(response.Tests, testResp)
246 }
247
248 ret = append(ret, response)
249 }
250
251 return ret, nil
252}