blob: 284b5d378bebf1f948fdc85d3fe1bbefe7df1da9 [file] [log] [blame]
// testmodulewrapper is a modulewrapper binary that works with acvptool and
// implements the primitives that BoringSSL's modulewrapper doesn't, so that
// we have something that can exercise all the code in avcptool.
package main
import (
"bytes"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"os"
)
var handlers = map[string]func([][]byte) error{
"getConfig": getConfig,
"KDF-counter": kdfCounter,
}
func getConfig(args [][]byte) error {
if len(args) != 0 {
return fmt.Errorf("getConfig received %d args", len(args))
}
return reply([]byte(`[
{
"algorithm": "KDF",
"revision": "1.0",
"capabilities": [{
"kdfMode": "counter",
"macMode": [
"HMAC-SHA2-256"
],
"supportedLengths": [{
"min": 8,
"max": 4096,
"increment": 8
}],
"fixedDataOrder": [
"before fixed data"
],
"counterLength": [
32
]
}]
}
]`))
}
func kdfCounter(args [][]byte) error {
if len(args) != 5 {
return fmt.Errorf("KDF received %d args", len(args))
}
outputBytes32, prf, counterLocation, key, counterBits32 := args[0], args[1], args[2], args[3], args[4]
outputBytes := binary.LittleEndian.Uint32(outputBytes32)
counterBits := binary.LittleEndian.Uint32(counterBits32)
if !bytes.Equal(prf, []byte("HMAC-SHA2-256")) {
return fmt.Errorf("KDF received unsupported PRF %q", string(prf))
}
if !bytes.Equal(counterLocation, []byte("before fixed data")) {
return fmt.Errorf("KDF received unsupported counter location %q", counterLocation)
}
if counterBits != 32 {
return fmt.Errorf("KDF received unsupported counter length %d", counterBits)
}
if len(key) == 0 {
key = make([]byte, 32)
rand.Reader.Read(key)
}
// See https://nvlpubs.nist.gov/nistpubs/Legacy/SP/nistspecialpublication800-108.pdf section 5.1
if outputBytes+31 < outputBytes {
return fmt.Errorf("KDF received excessive output length %d", outputBytes)
}
n := (outputBytes + 31) / 32
result := make([]byte, 0, 32*n)
mac := hmac.New(sha256.New, key)
var input [4 + 8]byte
var digest []byte
rand.Reader.Read(input[4:])
for i := uint32(1); i <= n; i++ {
mac.Reset()
binary.BigEndian.PutUint32(input[:4], i)
mac.Write(input[:])
digest = mac.Sum(digest[:0])
result = append(result, digest...)
}
return reply(key, input[4:], result[:outputBytes])
}
func reply(responses ...[]byte) error {
if len(responses) > maxArgs {
return fmt.Errorf("%d responses is too many", len(responses))
}
var lengths [4 * (1 + maxArgs)]byte
binary.LittleEndian.PutUint32(lengths[:4], uint32(len(responses)))
for i, response := range responses {
binary.LittleEndian.PutUint32(lengths[4*(i+1):4*(i+2)], uint32(len(response)))
}
lengthsLength := (1 + len(responses)) * 4
if n, err := os.Stdout.Write(lengths[:lengthsLength]); n != lengthsLength || err != nil {
return fmt.Errorf("write failed: %s", err)
}
for _, response := range responses {
if n, err := os.Stdout.Write(response); n != len(response) || err != nil {
return fmt.Errorf("write failed: %s", err)
}
}
return nil
}
const (
maxArgs = 8
maxArgLength = 1 << 20
maxNameLength = 30
)
func main() {
if err := do(); err != nil {
fmt.Fprintf(os.Stderr, "%s.\n", err)
os.Exit(1)
}
}
func do() error {
var nums [4 * (1 + maxArgs)]byte
var argLengths [maxArgs]uint32
var args [maxArgs][]byte
var argsData []byte
for {
if _, err := io.ReadFull(os.Stdin, nums[:8]); err != nil {
return err
}
numArgs := binary.LittleEndian.Uint32(nums[:4])
if numArgs == 0 {
return errors.New("Invalid, zero-argument operation requested")
} else if numArgs > maxArgs {
return fmt.Errorf("Operation requested with %d args, but %d is the limit", numArgs, maxArgs)
}
if numArgs > 1 {
if _, err := io.ReadFull(os.Stdin, nums[8:4+4*numArgs]); err != nil {
return err
}
}
input := nums[4:]
var need uint64
for i := uint32(0); i < numArgs; i++ {
argLength := binary.LittleEndian.Uint32(input[:4])
if i == 0 && argLength > maxNameLength {
return fmt.Errorf("Operation with name of length %d exceeded limit of %d", argLength, maxNameLength)
} else if argLength > maxArgLength {
return fmt.Errorf("Operation with argument of length %d exceeded limit of %d", argLength, maxArgLength)
}
need += uint64(argLength)
argLengths[i] = argLength
input = input[4:]
}
if need > uint64(cap(argsData)) {
argsData = make([]byte, need)
} else {
argsData = argsData[:need]
}
if _, err := io.ReadFull(os.Stdin, argsData); err != nil {
return err
}
input = argsData
for i := uint32(0); i < numArgs; i++ {
args[i] = input[:argLengths[i]]
input = input[argLengths[i]:]
}
name := string(args[0])
if handler, ok := handlers[name]; !ok {
return fmt.Errorf("unknown operation %q", name)
} else {
if err := handler(args[1:numArgs]); err != nil {
return err
}
}
}
}