• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright (c) 2021, 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
15// testmodulewrapper is a modulewrapper binary that works with acvptool and
16// implements the primitives that BoringSSL's modulewrapper doesn't, so that
17// we have something that can exercise all the code in avcptool.
18
19package main
20
21import (
22	"bytes"
23	"crypto/aes"
24	"crypto/cipher"
25	"crypto/hmac"
26	"crypto/rand"
27	"crypto/sha256"
28	"encoding/binary"
29	"errors"
30	"fmt"
31	"io"
32	"os"
33
34	"golang.org/x/crypto/hkdf"
35	"golang.org/x/crypto/xts"
36)
37
38var handlers = map[string]func([][]byte) error{
39	"getConfig":                getConfig,
40	"KDF-counter":              kdfCounter,
41	"AES-XTS/encrypt":          xtsEncrypt,
42	"AES-XTS/decrypt":          xtsDecrypt,
43	"HKDF/SHA2-256":            hkdfMAC,
44	"hmacDRBG-reseed/SHA2-256": hmacDRBGReseed,
45	"hmacDRBG-pr/SHA2-256":     hmacDRBGPredictionResistance,
46	"AES-CBC-CS3/encrypt":      ctsEncrypt,
47	"AES-CBC-CS3/decrypt":      ctsDecrypt,
48}
49
50func getConfig(args [][]byte) error {
51	if len(args) != 0 {
52		return fmt.Errorf("getConfig received %d args", len(args))
53	}
54
55	return reply([]byte(`[
56	{
57		"algorithm": "KDF",
58		"revision": "1.0",
59		"capabilities": [{
60			"kdfMode": "counter",
61			"macMode": [
62				"HMAC-SHA2-256"
63			],
64			"supportedLengths": [{
65				"min": 8,
66				"max": 4096,
67				"increment": 8
68			}],
69			"fixedDataOrder": [
70				"before fixed data"
71			],
72			"counterLength": [
73				32
74			]
75		}]
76	}, {
77		"algorithm": "ACVP-AES-XTS",
78		"revision": "1.0",
79		"direction": [
80		  "encrypt",
81		  "decrypt"
82		],
83		"keyLen": [
84		  128,
85		  256
86		],
87		"payloadLen": [
88		  1024
89		],
90		"tweakMode": [
91		  "number"
92		]
93	}, {
94		"algorithm": "KAS-KDF",
95		"mode": "TwoStep",
96		"revision": "Sp800-56Cr2",
97		"capabilities": [{
98			"macSaltMethods": [
99				"random",
100				"default"
101			],
102			"fixedInfoPattern": "uPartyInfo||vPartyInfo",
103			"encoding": [
104				"concatenation"
105			],
106			"kdfMode": "feedback",
107			"macMode": [
108				"HMAC-SHA2-256"
109			],
110			"supportedLengths": [{
111				"min": 128,
112				"max": 512,
113				"increment": 64
114			}],
115			"fixedDataOrder": [
116				"after fixed data"
117			],
118			"counterLength": [
119				8
120			],
121			"requiresEmptyIv": true,
122			"supportsEmptyIv": true
123		}],
124		"l": 256,
125		"z": [256, 384]
126	}, {
127		"algorithm": "hmacDRBG",
128		"revision": "1.0",
129		"predResistanceEnabled": [false, true],
130		"reseedImplemented": true,
131		"capabilities": [{
132			"mode": "SHA2-256",
133			"derFuncEnabled": false,
134			"entropyInputLen": [
135				256
136			],
137			"nonceLen": [
138				128
139			],
140			"persoStringLen": [
141				256
142			],
143			"additionalInputLen": [
144				256
145			],
146			"returnedBitsLen": 256
147		}]
148	}, {
149		"algorithm": "ACVP-AES-CBC-CS3",
150		"revision": "1.0",
151		"payloadLen": [{
152			"min": 128,
153			"max": 2048,
154			"increment": 8
155		}],
156		"direction": [
157		  "encrypt",
158		  "decrypt"
159		],
160		"keyLen": [
161		  128,
162		  256
163		]
164	}
165]`))
166}
167
168func kdfCounter(args [][]byte) error {
169	if len(args) != 5 {
170		return fmt.Errorf("KDF received %d args", len(args))
171	}
172
173	outputBytes32, prf, counterLocation, key, counterBits32 := args[0], args[1], args[2], args[3], args[4]
174	outputBytes := binary.LittleEndian.Uint32(outputBytes32)
175	counterBits := binary.LittleEndian.Uint32(counterBits32)
176
177	if !bytes.Equal(prf, []byte("HMAC-SHA2-256")) {
178		return fmt.Errorf("KDF received unsupported PRF %q", string(prf))
179	}
180	if !bytes.Equal(counterLocation, []byte("before fixed data")) {
181		return fmt.Errorf("KDF received unsupported counter location %q", counterLocation)
182	}
183	if counterBits != 32 {
184		return fmt.Errorf("KDF received unsupported counter length %d", counterBits)
185	}
186
187	if len(key) == 0 {
188		key = make([]byte, 32)
189		rand.Reader.Read(key)
190	}
191
192	// See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf section 5.1
193	if outputBytes+31 < outputBytes {
194		return fmt.Errorf("KDF received excessive output length %d", outputBytes)
195	}
196
197	n := (outputBytes + 31) / 32
198	result := make([]byte, 0, 32*n)
199	mac := hmac.New(sha256.New, key)
200	var input [4 + 8]byte
201	var digest []byte
202	rand.Reader.Read(input[4:])
203	for i := uint32(1); i <= n; i++ {
204		mac.Reset()
205		binary.BigEndian.PutUint32(input[:4], i)
206		mac.Write(input[:])
207		digest = mac.Sum(digest[:0])
208		result = append(result, digest...)
209	}
210
211	return reply(key, input[4:], result[:outputBytes])
212}
213
214func reply(responses ...[]byte) error {
215	if len(responses) > maxArgs {
216		return fmt.Errorf("%d responses is too many", len(responses))
217	}
218
219	var lengths [4 * (1 + maxArgs)]byte
220	binary.LittleEndian.PutUint32(lengths[:4], uint32(len(responses)))
221	for i, response := range responses {
222		binary.LittleEndian.PutUint32(lengths[4*(i+1):4*(i+2)], uint32(len(response)))
223	}
224
225	lengthsLength := (1 + len(responses)) * 4
226	if n, err := os.Stdout.Write(lengths[:lengthsLength]); n != lengthsLength || err != nil {
227		return fmt.Errorf("write failed: %s", err)
228	}
229
230	for _, response := range responses {
231		if n, err := os.Stdout.Write(response); n != len(response) || err != nil {
232			return fmt.Errorf("write failed: %s", err)
233		}
234	}
235
236	return nil
237}
238
239func xtsEncrypt(args [][]byte) error {
240	return doXTS(args, false)
241}
242
243func xtsDecrypt(args [][]byte) error {
244	return doXTS(args, true)
245}
246
247func doXTS(args [][]byte, decrypt bool) error {
248	if len(args) != 3 {
249		return fmt.Errorf("XTS received %d args, wanted 3", len(args))
250	}
251	key := args[0]
252	msg := args[1]
253	tweak := args[2]
254
255	if len(msg)%16 != 0 {
256		return fmt.Errorf("XTS received %d-byte msg, need multiple of 16", len(msg))
257	}
258	if len(tweak) != 16 {
259		return fmt.Errorf("XTS received %d-byte tweak, wanted 16", len(tweak))
260	}
261
262	var zeros [8]byte
263	if !bytes.Equal(tweak[8:], zeros[:]) {
264		return errors.New("XTS received tweak with invalid structure. Ensure that configuration specifies a 'number' tweak")
265	}
266
267	sectorNum := binary.LittleEndian.Uint64(tweak[:8])
268
269	c, err := xts.NewCipher(aes.NewCipher, key)
270	if err != nil {
271		return err
272	}
273
274	if decrypt {
275		c.Decrypt(msg, msg, sectorNum)
276	} else {
277		c.Encrypt(msg, msg, sectorNum)
278	}
279
280	return reply(msg)
281}
282
283func hkdfMAC(args [][]byte) error {
284	if len(args) != 4 {
285		return fmt.Errorf("HKDF received %d args, wanted 4", len(args))
286	}
287
288	key := args[0]
289	salt := args[1]
290	info := args[2]
291	lengthBytes := args[3]
292
293	if len(lengthBytes) != 4 {
294		return fmt.Errorf("uint32 length was %d bytes long", len(lengthBytes))
295	}
296
297	length := binary.LittleEndian.Uint32(lengthBytes)
298
299	mac := hkdf.New(sha256.New, key, salt, info)
300	ret := make([]byte, length)
301	mac.Read(ret)
302
303	return reply(ret)
304}
305
306func hmacDRBGReseed(args [][]byte) error {
307	if len(args) != 8 {
308		return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args))
309	}
310
311	outLenBytes, entropy, personalisation, reseedAdditionalData, reseedEntropy, additionalData1, additionalData2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]
312
313	if len(outLenBytes) != 4 {
314		return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes))
315	}
316	outLen := binary.LittleEndian.Uint32(outLenBytes)
317	out := make([]byte, outLen)
318
319	drbg := NewHMACDRBG(entropy, nonce, personalisation)
320	drbg.Reseed(reseedEntropy, reseedAdditionalData)
321	drbg.Generate(out, additionalData1)
322	drbg.Generate(out, additionalData2)
323
324	return reply(out)
325}
326
327func hmacDRBGPredictionResistance(args [][]byte) error {
328	if len(args) != 8 {
329		return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args))
330	}
331
332	outLenBytes, entropy, personalisation, additionalData1, entropy1, additionalData2, entropy2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]
333
334	if len(outLenBytes) != 4 {
335		return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes))
336	}
337	outLen := binary.LittleEndian.Uint32(outLenBytes)
338	out := make([]byte, outLen)
339
340	drbg := NewHMACDRBG(entropy, nonce, personalisation)
341	drbg.Reseed(entropy1, additionalData1)
342	drbg.Generate(out, nil)
343	drbg.Reseed(entropy2, additionalData2)
344	drbg.Generate(out, nil)
345
346	return reply(out)
347}
348
349func swapFinalTwoAESBlocks(d []byte) {
350	var blockNMinus1 [aes.BlockSize]byte
351	copy(blockNMinus1[:], d[len(d)-2*aes.BlockSize:])
352	copy(d[len(d)-2*aes.BlockSize:], d[len(d)-aes.BlockSize:])
353	copy(d[len(d)-aes.BlockSize:], blockNMinus1[:])
354}
355
356func roundUp(n, m int) int {
357	return n + (m-(n%m))%m
358}
359
360func doCTSEncrypt(key, origPlaintext, iv []byte) []byte {
361	// https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38a-add.pdf
362	if len(origPlaintext) < aes.BlockSize {
363		panic("input too small")
364	}
365
366	plaintext := make([]byte, roundUp(len(origPlaintext), aes.BlockSize))
367	copy(plaintext, origPlaintext)
368
369	block, err := aes.NewCipher(key)
370	if err != nil {
371		panic(err)
372	}
373	cbcEncryptor := cipher.NewCBCEncrypter(block, iv)
374	cbcEncryptor.CryptBlocks(plaintext, plaintext)
375	ciphertext := plaintext
376
377	if len(origPlaintext) > aes.BlockSize {
378		swapFinalTwoAESBlocks(ciphertext)
379
380		if len(origPlaintext)%16 != 0 {
381			// Truncate the ciphertext
382			ciphertext = ciphertext[:len(ciphertext)-aes.BlockSize+(len(origPlaintext)%aes.BlockSize)]
383		}
384	}
385
386	if len(ciphertext) != len(origPlaintext) {
387		panic("internal error")
388	}
389
390	return ciphertext
391}
392
393func doCTSDecrypt(key, origCiphertext, iv []byte) []byte {
394	if len(origCiphertext) < aes.BlockSize {
395		panic("input too small")
396	}
397
398	ciphertext := make([]byte, roundUp(len(origCiphertext), aes.BlockSize))
399	copy(ciphertext, origCiphertext)
400
401	if len(ciphertext) > aes.BlockSize {
402		swapFinalTwoAESBlocks(ciphertext)
403	}
404
405	block, err := aes.NewCipher(key)
406	if err != nil {
407		panic(err)
408	}
409	cbcDecrypter := cipher.NewCBCDecrypter(block, iv)
410
411	var plaintext []byte
412	if overhang := len(origCiphertext) % aes.BlockSize; overhang == 0 {
413		cbcDecrypter.CryptBlocks(ciphertext, ciphertext)
414		plaintext = ciphertext
415	} else {
416		ciphertext, finalBlock := ciphertext[:len(ciphertext)-aes.BlockSize], ciphertext[len(ciphertext)-aes.BlockSize:]
417		var plaintextFinalBlock [aes.BlockSize]byte
418		block.Decrypt(plaintextFinalBlock[:], finalBlock)
419		copy(ciphertext[len(ciphertext)-aes.BlockSize+overhang:], plaintextFinalBlock[overhang:])
420		plaintext = make([]byte, len(origCiphertext))
421		cbcDecrypter.CryptBlocks(plaintext, ciphertext)
422		for i := 0; i < overhang; i++ {
423			plaintextFinalBlock[i] ^= ciphertext[len(ciphertext)-aes.BlockSize+i]
424		}
425		copy(plaintext[len(ciphertext):], plaintextFinalBlock[:overhang])
426	}
427
428	return plaintext
429}
430
431func ctsEncrypt(args [][]byte) error {
432	if len(args) != 4 {
433		return fmt.Errorf("ctsEncrypt received %d args, wanted 4", len(args))
434	}
435
436	key, plaintext, iv, numIterations32 := args[0], args[1], args[2], args[3]
437	if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 {
438		return errors.New("only a single iteration supported for ctsEncrypt")
439	}
440
441	if len(plaintext) < aes.BlockSize {
442		return fmt.Errorf("ctsEncrypt plaintext too short: %d bytes", len(plaintext))
443	}
444
445	return reply(doCTSEncrypt(key, plaintext, iv))
446}
447
448func ctsDecrypt(args [][]byte) error {
449	if len(args) != 4 {
450		return fmt.Errorf("ctsDecrypt received %d args, wanted 4", len(args))
451	}
452
453	key, ciphertext, iv, numIterations32 := args[0], args[1], args[2], args[3]
454	if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 {
455		return errors.New("only a single iteration supported for ctsDecrypt")
456	}
457
458	if len(ciphertext) < aes.BlockSize {
459		return errors.New("ctsDecrypt ciphertext too short")
460	}
461
462	return reply(doCTSDecrypt(key, ciphertext, iv))
463}
464
465const (
466	maxArgs       = 9
467	maxArgLength  = 1 << 20
468	maxNameLength = 30
469)
470
471func main() {
472	if err := do(); err != nil {
473		fmt.Fprintf(os.Stderr, "%s.\n", err)
474		os.Exit(1)
475	}
476}
477
478func do() error {
479	var nums [4 * (1 + maxArgs)]byte
480	var argLengths [maxArgs]uint32
481	var args [maxArgs][]byte
482	var argsData []byte
483
484	for {
485		if _, err := io.ReadFull(os.Stdin, nums[:8]); err != nil {
486			return err
487		}
488
489		numArgs := binary.LittleEndian.Uint32(nums[:4])
490		if numArgs == 0 {
491			return errors.New("Invalid, zero-argument operation requested")
492		} else if numArgs > maxArgs {
493			return fmt.Errorf("Operation requested with %d args, but %d is the limit", numArgs, maxArgs)
494		}
495
496		if numArgs > 1 {
497			if _, err := io.ReadFull(os.Stdin, nums[8:4+4*numArgs]); err != nil {
498				return err
499			}
500		}
501
502		input := nums[4:]
503		var need uint64
504		for i := uint32(0); i < numArgs; i++ {
505			argLength := binary.LittleEndian.Uint32(input[:4])
506			if i == 0 && argLength > maxNameLength {
507				return fmt.Errorf("Operation with name of length %d exceeded limit of %d", argLength, maxNameLength)
508			} else if argLength > maxArgLength {
509				return fmt.Errorf("Operation with argument of length %d exceeded limit of %d", argLength, maxArgLength)
510			}
511			need += uint64(argLength)
512			argLengths[i] = argLength
513			input = input[4:]
514		}
515
516		if need > uint64(cap(argsData)) {
517			argsData = make([]byte, need)
518		} else {
519			argsData = argsData[:need]
520		}
521
522		if _, err := io.ReadFull(os.Stdin, argsData); err != nil {
523			return err
524		}
525
526		input = argsData
527		for i := uint32(0); i < numArgs; i++ {
528			args[i] = input[:argLengths[i]]
529			input = input[argLengths[i]:]
530		}
531
532		name := string(args[0])
533		if handler, ok := handlers[name]; !ok {
534			return fmt.Errorf("unknown operation %q", name)
535		} else {
536			if err := handler(args[1:numArgs]); err != nil {
537				return err
538			}
539		}
540	}
541}
542