• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2021 The BoringSSL Authors
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"
24	"crypto/aes"
25	"crypto/cipher"
26	"crypto/ed25519"
27	"crypto/hmac"
28	"crypto/rand"
29	"crypto/sha256"
30	"crypto/sha512"
31	"encoding/binary"
32	"errors"
33	"fmt"
34	"hash"
35	"io"
36	"os"
37
38	"filippo.io/edwards25519"
39
40	"golang.org/x/crypto/hkdf"
41	"golang.org/x/crypto/pbkdf2"
42	"golang.org/x/crypto/sha3"
43	"golang.org/x/crypto/xts"
44)
45
46var (
47	output       io.Writer
48	outputBuffer *bytes.Buffer
49)
50
51var handlers = map[string]func([][]byte) error{
52	"flush":                    flush,
53	"getConfig":                getConfig,
54	"KDF-counter":              kdfCounter,
55	"AES-XTS/encrypt":          xtsEncrypt,
56	"AES-XTS/decrypt":          xtsDecrypt,
57	"HKDF/SHA2-256":            hkdfMAC,
58	"hmacDRBG-reseed/SHA2-256": hmacDRBGReseed,
59	"hmacDRBG-pr/SHA2-256":     hmacDRBGPredictionResistance,
60	"AES-CBC-CS3/encrypt":      ctsEncrypt,
61	"AES-CBC-CS3/decrypt":      ctsDecrypt,
62	"PBKDF":                    pbkdf,
63	"EDDSA/keyGen":             eddsaKeyGen,
64	"EDDSA/keyVer":             eddsaKeyVer,
65	"EDDSA/sigGen":             eddsaSigGen,
66	"EDDSA/sigVer":             eddsaSigVer,
67	"SHAKE-128":                shakeAftVot(sha3.NewShake128),
68	"SHAKE-128/VOT":            shakeAftVot(sha3.NewShake128),
69	"SHAKE-128/MCT":            shakeMct(sha3.NewShake128),
70	"SHAKE-256":                shakeAftVot(sha3.NewShake256),
71	"SHAKE-256/VOT":            shakeAftVot(sha3.NewShake256),
72	"SHAKE-256/MCT":            shakeMct(sha3.NewShake256),
73}
74
75func flush(args [][]byte) error {
76	if outputBuffer == nil {
77		return nil
78	}
79
80	if _, err := os.Stdout.Write(outputBuffer.Bytes()); err != nil {
81		return err
82	}
83	outputBuffer = new(bytes.Buffer)
84	output = outputBuffer
85	return nil
86}
87
88func getConfig(args [][]byte) error {
89	if len(args) != 0 {
90		return fmt.Errorf("getConfig received %d args", len(args))
91	}
92
93	if err := reply([]byte(`[
94	{
95		"algorithm": "acvptool",
96		"features": ["batch"]
97	}, {
98		"algorithm": "KDF",
99		"revision": "1.0",
100		"capabilities": [{
101			"kdfMode": "counter",
102			"macMode": [
103				"HMAC-SHA2-256"
104			],
105			"supportedLengths": [{
106				"min": 8,
107				"max": 4096,
108				"increment": 8
109			}],
110			"fixedDataOrder": [
111				"before fixed data"
112			],
113			"counterLength": [
114				32
115			]
116		}]
117	}, {
118		"algorithm": "ACVP-AES-XTS",
119		"revision": "1.0",
120		"direction": [
121		  "encrypt",
122		  "decrypt"
123		],
124		"keyLen": [
125		  128,
126		  256
127		],
128		"payloadLen": [
129		  1024
130		],
131		"tweakMode": [
132		  "number"
133		]
134	}, {
135		"algorithm": "KDA",
136		"mode": "HKDF",
137		"revision": "Sp800-56Cr1",
138		"fixedInfoPattern": "uPartyInfo||vPartyInfo",
139		"encoding": [
140			"concatenation"
141		],
142		"hmacAlg": [
143			"SHA2-256"
144		],
145		"macSaltMethods": [
146			"default",
147			"random"
148		],
149		"l": 256,
150		"z": [256, 384]
151	}, {
152		"algorithm": "hmacDRBG",
153		"revision": "1.0",
154		"predResistanceEnabled": [false, true],
155		"reseedImplemented": true,
156		"capabilities": [{
157			"mode": "SHA2-256",
158			"derFuncEnabled": false,
159			"entropyInputLen": [
160				256
161			],
162			"nonceLen": [
163				128
164			],
165			"persoStringLen": [
166				256
167			],
168			"additionalInputLen": [
169				256
170			],
171			"returnedBitsLen": 256
172		}]
173	}, {
174		"algorithm": "ACVP-AES-CBC-CS3",
175		"revision": "1.0",
176		"payloadLen": [{
177			"min": 128,
178			"max": 2048,
179			"increment": 8
180		}],
181		"direction": [
182		  "encrypt",
183		  "decrypt"
184		],
185		"keyLen": [
186		  128,
187		  256
188		]
189	}, {
190		"algorithm": "PBKDF",
191		"revision":"1.0",
192		"capabilities": [{
193			"iterationCount":[{
194				"min":1,
195				"max":10000,
196				"increment":1
197			}],
198			"keyLen": [{
199				"min":112,
200				"max":4096,
201				"increment":8
202			}],
203			"passwordLen":[{
204				"min":8,
205				"max":64,
206				"increment":1
207			}],
208			"saltLen":[{
209				"min":128,
210				"max":512,
211				"increment":8
212			}],
213			"hmacAlg":[
214				"SHA2-224",
215				"SHA2-256",
216				"SHA2-384",
217				"SHA2-512",
218				"SHA2-512/224",
219				"SHA2-512/256",
220				"SHA3-224",
221				"SHA3-256",
222				"SHA3-384",
223				"SHA3-512"
224			]
225		}]
226	}, {
227		"algorithm": "EDDSA",
228		"mode": "keyVer",
229		"revision": "1.0",
230		"curve": ["ED-25519"]
231	}, {
232		"algorithm": "EDDSA",
233		"mode": "sigVer",
234		"revision": "1.0",
235		"pure": true,
236		"preHash": true,
237		"curve": ["ED-25519"]
238	}, {
239		"algorithm": "SHAKE-128",
240		"inBit": false,
241		"outBit": false,
242		"inEmpty": false,
243		"outputLen": [{
244			"min": 128,
245			"max": 4096,
246			"increment": 8
247		}],
248		"revision": "1.0"
249	}, {
250		"algorithm": "SHAKE-256",
251		"inBit": false,
252		"outBit": false,
253		"inEmpty": false,
254		"outputLen": [{
255			"min": 128,
256			"max": 4096,
257			"increment": 8
258		}],
259		"revision": "1.0"
260	}
261]`)); err != nil {
262		return err
263	}
264
265	return flush(nil)
266}
267
268func kdfCounter(args [][]byte) error {
269	if len(args) != 5 {
270		return fmt.Errorf("KDF received %d args", len(args))
271	}
272
273	outputBytes32, prf, counterLocation, key, counterBits32 := args[0], args[1], args[2], args[3], args[4]
274	outputBytes := binary.LittleEndian.Uint32(outputBytes32)
275	counterBits := binary.LittleEndian.Uint32(counterBits32)
276
277	if !bytes.Equal(prf, []byte("HMAC-SHA2-256")) {
278		return fmt.Errorf("KDF received unsupported PRF %q", string(prf))
279	}
280	if !bytes.Equal(counterLocation, []byte("before fixed data")) {
281		return fmt.Errorf("KDF received unsupported counter location %q", counterLocation)
282	}
283	if counterBits != 32 {
284		return fmt.Errorf("KDF received unsupported counter length %d", counterBits)
285	}
286
287	if len(key) == 0 {
288		key = make([]byte, 32)
289		rand.Reader.Read(key)
290	}
291
292	// See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf section 5.1
293	if outputBytes+31 < outputBytes {
294		return fmt.Errorf("KDF received excessive output length %d", outputBytes)
295	}
296
297	n := (outputBytes + 31) / 32
298	result := make([]byte, 0, 32*n)
299	mac := hmac.New(sha256.New, key)
300	var input [4 + 8]byte
301	var digest []byte
302	rand.Reader.Read(input[4:])
303	for i := uint32(1); i <= n; i++ {
304		mac.Reset()
305		binary.BigEndian.PutUint32(input[:4], i)
306		mac.Write(input[:])
307		digest = mac.Sum(digest[:0])
308		result = append(result, digest...)
309	}
310
311	return reply(key, input[4:], result[:outputBytes])
312}
313
314func reply(responses ...[]byte) error {
315	if len(responses) > maxArgs {
316		return fmt.Errorf("%d responses is too many", len(responses))
317	}
318
319	var lengths [4 * (1 + maxArgs)]byte
320	binary.LittleEndian.PutUint32(lengths[:4], uint32(len(responses)))
321	for i, response := range responses {
322		binary.LittleEndian.PutUint32(lengths[4*(i+1):4*(i+2)], uint32(len(response)))
323	}
324
325	lengthsLength := (1 + len(responses)) * 4
326	if n, err := output.Write(lengths[:lengthsLength]); n != lengthsLength || err != nil {
327		return fmt.Errorf("write failed: %s", err)
328	}
329
330	for _, response := range responses {
331		if n, err := output.Write(response); n != len(response) || err != nil {
332			return fmt.Errorf("write failed: %s", err)
333		}
334	}
335
336	return nil
337}
338
339func xtsEncrypt(args [][]byte) error {
340	return doXTS(args, false)
341}
342
343func xtsDecrypt(args [][]byte) error {
344	return doXTS(args, true)
345}
346
347func doXTS(args [][]byte, decrypt bool) error {
348	if len(args) != 3 {
349		return fmt.Errorf("XTS received %d args, wanted 3", len(args))
350	}
351	key := args[0]
352	msg := args[1]
353	tweak := args[2]
354
355	if len(msg)%16 != 0 {
356		return fmt.Errorf("XTS received %d-byte msg, need multiple of 16", len(msg))
357	}
358	if len(tweak) != 16 {
359		return fmt.Errorf("XTS received %d-byte tweak, wanted 16", len(tweak))
360	}
361
362	var zeros [8]byte
363	if !bytes.Equal(tweak[8:], zeros[:]) {
364		return errors.New("XTS received tweak with invalid structure. Ensure that configuration specifies a 'number' tweak")
365	}
366
367	sectorNum := binary.LittleEndian.Uint64(tweak[:8])
368
369	c, err := xts.NewCipher(aes.NewCipher, key)
370	if err != nil {
371		return err
372	}
373
374	if decrypt {
375		c.Decrypt(msg, msg, sectorNum)
376	} else {
377		c.Encrypt(msg, msg, sectorNum)
378	}
379
380	return reply(msg)
381}
382
383func hkdfMAC(args [][]byte) error {
384	if len(args) != 4 {
385		return fmt.Errorf("HKDF received %d args, wanted 4", len(args))
386	}
387
388	key := args[0]
389	salt := args[1]
390	info := args[2]
391	lengthBytes := args[3]
392
393	if len(lengthBytes) != 4 {
394		return fmt.Errorf("uint32 length was %d bytes long", len(lengthBytes))
395	}
396
397	length := binary.LittleEndian.Uint32(lengthBytes)
398
399	mac := hkdf.New(sha256.New, key, salt, info)
400	ret := make([]byte, length)
401	mac.Read(ret)
402
403	return reply(ret)
404}
405
406func hmacDRBGReseed(args [][]byte) error {
407	if len(args) != 8 {
408		return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args))
409	}
410
411	outLenBytes, entropy, personalisation, reseedAdditionalData, reseedEntropy, additionalData1, additionalData2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]
412
413	if len(outLenBytes) != 4 {
414		return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes))
415	}
416	outLen := binary.LittleEndian.Uint32(outLenBytes)
417	out := make([]byte, outLen)
418
419	drbg := NewHMACDRBG(entropy, nonce, personalisation)
420	drbg.Reseed(reseedEntropy, reseedAdditionalData)
421	drbg.Generate(out, additionalData1)
422	drbg.Generate(out, additionalData2)
423
424	return reply(out)
425}
426
427func hmacDRBGPredictionResistance(args [][]byte) error {
428	if len(args) != 8 {
429		return fmt.Errorf("hmacDRBG received %d args, wanted 8", len(args))
430	}
431
432	outLenBytes, entropy, personalisation, additionalData1, entropy1, additionalData2, entropy2, nonce := args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]
433
434	if len(outLenBytes) != 4 {
435		return fmt.Errorf("uint32 length was %d bytes long", len(outLenBytes))
436	}
437	outLen := binary.LittleEndian.Uint32(outLenBytes)
438	out := make([]byte, outLen)
439
440	drbg := NewHMACDRBG(entropy, nonce, personalisation)
441	drbg.Reseed(entropy1, additionalData1)
442	drbg.Generate(out, nil)
443	drbg.Reseed(entropy2, additionalData2)
444	drbg.Generate(out, nil)
445
446	return reply(out)
447}
448
449func swapFinalTwoAESBlocks(d []byte) {
450	var blockNMinus1 [aes.BlockSize]byte
451	copy(blockNMinus1[:], d[len(d)-2*aes.BlockSize:])
452	copy(d[len(d)-2*aes.BlockSize:], d[len(d)-aes.BlockSize:])
453	copy(d[len(d)-aes.BlockSize:], blockNMinus1[:])
454}
455
456func roundUp(n, m int) int {
457	return n + (m-(n%m))%m
458}
459
460func doCTSEncrypt(key, origPlaintext, iv []byte) []byte {
461	// https://nvlpubs.nist.gov/nistpubs/legacy/sp/nistspecialpublication800-38a-add.pdf
462	if len(origPlaintext) < aes.BlockSize {
463		panic("input too small")
464	}
465
466	plaintext := make([]byte, roundUp(len(origPlaintext), aes.BlockSize))
467	copy(plaintext, origPlaintext)
468
469	block, err := aes.NewCipher(key)
470	if err != nil {
471		panic(err)
472	}
473	cbcEncryptor := cipher.NewCBCEncrypter(block, iv)
474	cbcEncryptor.CryptBlocks(plaintext, plaintext)
475	ciphertext := plaintext
476
477	if len(origPlaintext) > aes.BlockSize {
478		swapFinalTwoAESBlocks(ciphertext)
479
480		if len(origPlaintext)%16 != 0 {
481			// Truncate the ciphertext
482			ciphertext = ciphertext[:len(ciphertext)-aes.BlockSize+(len(origPlaintext)%aes.BlockSize)]
483		}
484	}
485
486	if len(ciphertext) != len(origPlaintext) {
487		panic("internal error")
488	}
489
490	return ciphertext
491}
492
493func doCTSDecrypt(key, origCiphertext, iv []byte) []byte {
494	if len(origCiphertext) < aes.BlockSize {
495		panic("input too small")
496	}
497
498	ciphertext := make([]byte, roundUp(len(origCiphertext), aes.BlockSize))
499	copy(ciphertext, origCiphertext)
500
501	if len(ciphertext) > aes.BlockSize {
502		swapFinalTwoAESBlocks(ciphertext)
503	}
504
505	block, err := aes.NewCipher(key)
506	if err != nil {
507		panic(err)
508	}
509	cbcDecrypter := cipher.NewCBCDecrypter(block, iv)
510
511	var plaintext []byte
512	if overhang := len(origCiphertext) % aes.BlockSize; overhang == 0 {
513		cbcDecrypter.CryptBlocks(ciphertext, ciphertext)
514		plaintext = ciphertext
515	} else {
516		ciphertext, finalBlock := ciphertext[:len(ciphertext)-aes.BlockSize], ciphertext[len(ciphertext)-aes.BlockSize:]
517		var plaintextFinalBlock [aes.BlockSize]byte
518		block.Decrypt(plaintextFinalBlock[:], finalBlock)
519		copy(ciphertext[len(ciphertext)-aes.BlockSize+overhang:], plaintextFinalBlock[overhang:])
520		plaintext = make([]byte, len(origCiphertext))
521		cbcDecrypter.CryptBlocks(plaintext, ciphertext)
522		for i := 0; i < overhang; i++ {
523			plaintextFinalBlock[i] ^= ciphertext[len(ciphertext)-aes.BlockSize+i]
524		}
525		copy(plaintext[len(ciphertext):], plaintextFinalBlock[:overhang])
526	}
527
528	return plaintext
529}
530
531func ctsEncrypt(args [][]byte) error {
532	if len(args) != 4 {
533		return fmt.Errorf("ctsEncrypt received %d args, wanted 4", len(args))
534	}
535
536	key, plaintext, iv, numIterations32 := args[0], args[1], args[2], args[3]
537	if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 {
538		return errors.New("only a single iteration supported for ctsEncrypt")
539	}
540
541	if len(plaintext) < aes.BlockSize {
542		return fmt.Errorf("ctsEncrypt plaintext too short: %d bytes", len(plaintext))
543	}
544
545	return reply(doCTSEncrypt(key, plaintext, iv))
546}
547
548func ctsDecrypt(args [][]byte) error {
549	if len(args) != 4 {
550		return fmt.Errorf("ctsDecrypt received %d args, wanted 4", len(args))
551	}
552
553	key, ciphertext, iv, numIterations32 := args[0], args[1], args[2], args[3]
554	if len(numIterations32) != 4 || binary.LittleEndian.Uint32(numIterations32) != 1 {
555		return errors.New("only a single iteration supported for ctsDecrypt")
556	}
557
558	if len(ciphertext) < aes.BlockSize {
559		return errors.New("ctsDecrypt ciphertext too short")
560	}
561
562	return reply(doCTSDecrypt(key, ciphertext, iv))
563}
564
565func pbkdf(args [][]byte) error {
566	if len(args) != 5 {
567		return fmt.Errorf("pbkdf received %d args, wanted 5", len(args))
568	}
569
570	hmacName := args[0]
571	var h func() hash.Hash
572	switch string(hmacName) {
573	case "SHA2-224":
574		h = sha256.New224
575	case "SHA2-256":
576		h = sha256.New
577	case "SHA2-384":
578		h = sha512.New384
579	case "SHA2-512":
580		h = sha512.New
581	case "SHA2-512/224":
582		h = sha512.New512_224
583	case "SHA2-512/256":
584		h = sha512.New512_256
585	case "SHA3-224":
586		h = sha3.New224
587	case "SHA3-256":
588		h = sha3.New256
589	case "SHA3-384":
590		h = sha3.New384
591	case "SHA3-512":
592		h = sha3.New512
593	default:
594		return fmt.Errorf("pbkdf unknown HMAC algorithm: %q", hmacName)
595	}
596	keyLen := binary.LittleEndian.Uint32(args[1]) / 8
597	salt, password := args[2], args[3]
598	iterationCount := binary.LittleEndian.Uint32(args[4])
599
600	derivedKey := pbkdf2.Key(password, salt, int(iterationCount), int(keyLen), h)
601
602	return reply(derivedKey)
603}
604
605func eddsaKeyGen(args [][]byte) error {
606	if string(args[0]) != "ED-25519" {
607		return fmt.Errorf("unsupported EDDSA curve: %q", args[0])
608	}
609
610	pk, sk, err := ed25519.GenerateKey(nil)
611	if err != nil {
612		return fmt.Errorf("generating EDDSA keypair: %w", err)
613	}
614
615	// EDDSA/keyGen/AFT responses are d & q, described[0] as:
616	//   d	The encoded private key point
617	//   q	The encoded public key point
618	//
619	// Contrary to the description of a "point", d is the private key
620	// seed bytes per FIPS.186-5[1] A.2.3.
621	//
622	// [0]: https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-9.1
623	// [1]: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-5.pdf
624	return reply(sk.Seed(), pk)
625}
626
627func eddsaKeyVer(args [][]byte) error {
628	if string(args[0]) != "ED-25519" {
629		return fmt.Errorf("unsupported EDDSA curve: %q", args[0])
630	}
631
632	if len(args[1]) != ed25519.PublicKeySize {
633		return reply([]byte{0})
634	}
635
636	// Verify the point is on the curve. The higher-level ed25519 API does
637	// this at signature verification time so we have to use the lower-level
638	// edwards25519 package to do it here in absence of a signature to verify.
639	if _, err := new(edwards25519.Point).SetBytes(args[1]); err != nil {
640		return reply([]byte{0})
641	}
642
643	return reply([]byte{1})
644}
645
646func eddsaSigGen(args [][]byte) error {
647	if string(args[0]) != "ED-25519" {
648		return fmt.Errorf("unsupported EDDSA curve: %q", args[0])
649	}
650
651	sk := ed25519.NewKeyFromSeed(args[1])
652	msg := args[2]
653	prehash := args[3]
654	context := string(args[4])
655
656	var opts ed25519.Options
657	if prehash[0] == 1 {
658		opts.Hash = crypto.SHA512
659		h := sha512.New()
660		h.Write(msg)
661		msg = h.Sum(nil)
662		// With ed25519 the context is only specified for sigGen tests when using prehashing.
663		// See https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-8.6
664		opts.Context = context
665	}
666
667	sig, err := sk.Sign(nil, msg, &opts)
668	if err != nil {
669		return fmt.Errorf("error signing message: %w", err)
670	}
671
672	return reply(sig)
673}
674
675func eddsaSigVer(args [][]byte) error {
676	if string(args[0]) != "ED-25519" {
677		return fmt.Errorf("unsupported EDDSA curve: %q", args[0])
678	}
679
680	msg := args[1]
681	pk := ed25519.PublicKey(args[2])
682	sig := args[3]
683	prehash := args[4]
684
685	var opts ed25519.Options
686	if prehash[0] == 1 {
687		opts.Hash = crypto.SHA512
688		h := sha512.New()
689		h.Write(msg)
690		msg = h.Sum(nil)
691		// Context is only specified for sigGen, not sigVer.
692		// See https://pages.nist.gov/ACVP/draft-celi-acvp-eddsa.html#section-8.6
693	}
694
695	if err := ed25519.VerifyWithOptions(pk, msg, sig, &opts); err != nil {
696		return reply([]byte{0})
697	}
698
699	return reply([]byte{1})
700}
701
702func shakeAftVot(digestFn func() sha3.ShakeHash) func([][]byte) error {
703	return func(args [][]byte) error {
704		if len(args) != 2 {
705			return fmt.Errorf("shakeAftVot received %d args, wanted 2", len(args))
706		}
707
708		msg := args[0]
709		outLenBytes := binary.LittleEndian.Uint32(args[1])
710
711		h := digestFn()
712		h.Write(msg)
713		digest := make([]byte, outLenBytes)
714		h.Read(digest)
715
716		return reply(digest)
717	}
718}
719
720func shakeMct(digestFn func() sha3.ShakeHash) func([][]byte) error {
721	return func(args [][]byte) error {
722		if len(args) != 4 {
723			return fmt.Errorf("shakeMct received %d args, wanted 4", len(args))
724		}
725
726		md := args[0]
727		minOutBytes := binary.LittleEndian.Uint32(args[1])
728		maxOutBytes := binary.LittleEndian.Uint32(args[2])
729
730		outputLenBytes := binary.LittleEndian.Uint32(args[3])
731		if outputLenBytes < 2 {
732			return fmt.Errorf("invalid output length: %d", outputLenBytes)
733		}
734
735		if maxOutBytes < minOutBytes {
736			return fmt.Errorf("invalid maxOutBytes and minOutBytes: %d, %d", maxOutBytes, minOutBytes)
737		}
738
739		rangeBytes := maxOutBytes - minOutBytes + 1
740
741		for i := 0; i < 1000; i++ {
742			// "The MSG[i] input to SHAKE MUST always contain at least 128 bits. If this is not the case
743			// as the previous digest was too short, append empty bits to the rightmost side of the digest."
744			boundary := min(len(md), 16)
745			msg := make([]byte, 16)
746			copy(msg, md[:boundary])
747
748			//  MD[i] = SHAKE(MSG[i], OutputLen * 8)
749			h := digestFn()
750			h.Write(msg)
751			digest := make([]byte, outputLenBytes)
752			h.Read(digest)
753			md = digest
754
755			// RightmostOutputBits = 16 rightmost bits of MD[i] as an integer
756			// OutputLen = minOutBytes + (RightmostOutputBits % Range)
757			rightmostOutput := uint32(md[outputLenBytes-2])<<8 | uint32(md[outputLenBytes-1])
758			outputLenBytes = minOutBytes + (rightmostOutput % rangeBytes)
759		}
760
761		encodedOutputLenBytes := make([]byte, 4)
762		binary.LittleEndian.PutUint32(encodedOutputLenBytes, outputLenBytes)
763
764		return reply(md, encodedOutputLenBytes)
765	}
766}
767
768const (
769	maxArgs       = 9
770	maxArgLength  = 1 << 20
771	maxNameLength = 30
772)
773
774func main() {
775	if err := do(); err != nil {
776		fmt.Fprintf(os.Stderr, "%s.\n", err)
777		os.Exit(1)
778	}
779}
780
781func do() error {
782	// In order to exercise pipelining, all output is buffered until a "flush".
783	outputBuffer = new(bytes.Buffer)
784	output = outputBuffer
785
786	var nums [4 * (1 + maxArgs)]byte
787	var argLengths [maxArgs]uint32
788	var args [maxArgs][]byte
789	var argsData []byte
790
791	for {
792		if _, err := io.ReadFull(os.Stdin, nums[:8]); err != nil {
793			return err
794		}
795
796		numArgs := binary.LittleEndian.Uint32(nums[:4])
797		if numArgs == 0 {
798			return errors.New("Invalid, zero-argument operation requested")
799		} else if numArgs > maxArgs {
800			return fmt.Errorf("Operation requested with %d args, but %d is the limit", numArgs, maxArgs)
801		}
802
803		if numArgs > 1 {
804			if _, err := io.ReadFull(os.Stdin, nums[8:4+4*numArgs]); err != nil {
805				return err
806			}
807		}
808
809		input := nums[4:]
810		var need uint64
811		for i := uint32(0); i < numArgs; i++ {
812			argLength := binary.LittleEndian.Uint32(input[:4])
813			if i == 0 && argLength > maxNameLength {
814				return fmt.Errorf("Operation with name of length %d exceeded limit of %d", argLength, maxNameLength)
815			} else if argLength > maxArgLength {
816				return fmt.Errorf("Operation with argument of length %d exceeded limit of %d", argLength, maxArgLength)
817			}
818			need += uint64(argLength)
819			argLengths[i] = argLength
820			input = input[4:]
821		}
822
823		if need > uint64(cap(argsData)) {
824			argsData = make([]byte, need)
825		} else {
826			argsData = argsData[:need]
827		}
828
829		if _, err := io.ReadFull(os.Stdin, argsData); err != nil {
830			return err
831		}
832
833		input = argsData
834		for i := uint32(0); i < numArgs; i++ {
835			args[i] = input[:argLengths[i]]
836			input = input[argLengths[i]:]
837		}
838
839		name := string(args[0])
840		if handler, ok := handlers[name]; !ok {
841			return fmt.Errorf("unknown operation %q", name)
842		} else {
843			if err := handler(args[1:numArgs]); err != nil {
844				return err
845			}
846		}
847	}
848}
849