acvptool: add subprocess tests.
(Written by Dan Janni.)
Change-Id: Ice03bb3e717b361af367cce7425f43d65e79cadc
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/40724
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/util/fipstools/acvp/acvptool/acvp.go b/util/fipstools/acvp/acvptool/acvp.go
index 14ed6a9..c539b3b 100644
--- a/util/fipstools/acvp/acvptool/acvp.go
+++ b/util/fipstools/acvp/acvptool/acvp.go
@@ -432,6 +432,7 @@
}
if results.Passed {
+ log.Print("Test passed")
break
}
diff --git a/util/fipstools/acvp/acvptool/subprocess/block.go b/util/fipstools/acvp/acvptool/subprocess/block.go
index e365882..69a6517 100644
--- a/util/fipstools/acvp/acvptool/subprocess/block.go
+++ b/util/fipstools/acvp/acvptool/subprocess/block.go
@@ -26,7 +26,6 @@
algo string
blockSize int
hasIV bool
- m *Subprocess
}
type blockCipherVectorSet struct {
@@ -66,7 +65,7 @@
IVHex string `json:"iv,omitempty"`
}
-func (b *blockCipher) Process(vectorSet []byte) (interface{}, error) {
+func (b *blockCipher) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed blockCipherVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
@@ -153,9 +152,9 @@
var err error
if b.hasIV {
- result, err = b.m.transact(op, 1, key, input, iv)
+ result, err = m.Transact(op, 1, key, input, iv)
} else {
- result, err = b.m.transact(op, 1, key, input)
+ result, err = m.Transact(op, 1, key, input)
}
if err != nil {
panic("block operation failed: " + err.Error())
@@ -180,7 +179,7 @@
if !b.hasIV {
for j := 0; j < 1000; j++ {
prevResult = input
- result, err := b.m.transact(op, 1, key, input)
+ result, err := m.Transact(op, 1, key, input)
if err != nil {
panic("block operation failed")
}
@@ -201,7 +200,7 @@
}
}
- results, err := b.m.transact(op, 1, key, input, iv)
+ results, err := m.Transact(op, 1, key, input, iv)
if err != nil {
panic("block operation failed")
}
diff --git a/util/fipstools/acvp/acvptool/subprocess/drbg.go b/util/fipstools/acvp/acvptool/subprocess/drbg.go
index 83c13df..03268e4 100644
--- a/util/fipstools/acvp/acvptool/subprocess/drbg.go
+++ b/util/fipstools/acvp/acvptool/subprocess/drbg.go
@@ -69,10 +69,9 @@
// given to the subprocess to generate random bytes.
algo string
modes map[string]bool // the supported underlying primitives for the DRBG
- m *Subprocess
}
-func (d *drbg) Process(vectorSet []byte) (interface{}, error) {
+func (d *drbg) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed drbgTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
@@ -138,13 +137,13 @@
outLen := group.RetBits / 8
var outLenBytes [4]byte
binary.LittleEndian.PutUint32(outLenBytes[:], uint32(outLen))
- result, err := d.m.transact(d.algo+"/"+group.Mode, 1, outLenBytes[:], ent, perso, additionalInputs[0], additionalInputs[1], nonce)
+ result, err := m.Transact(d.algo+"/"+group.Mode, 1, outLenBytes[:], ent, perso, additionalInputs[0], additionalInputs[1], nonce)
if err != nil {
return nil, fmt.Errorf("DRBG operation failed: %s", err)
}
if l := uint64(len(result[0])); l != outLen {
- return nil, fmt.Errorf("wrong length DRBG result: %d bytes but wanted %d", l, outLenBytes)
+ return nil, fmt.Errorf("wrong length DRBG result: %d bytes but wanted %d", l, outLen)
}
// https://usnistgov.github.io/ACVP/artifacts/acvp_sub_drbg.html#rfc.section.4
diff --git a/util/fipstools/acvp/acvptool/subprocess/ecdsa.go b/util/fipstools/acvp/acvptool/subprocess/ecdsa.go
index 5b5b1d1..1ccae41 100644
--- a/util/fipstools/acvp/acvptool/subprocess/ecdsa.go
+++ b/util/fipstools/acvp/acvptool/subprocess/ecdsa.go
@@ -67,12 +67,12 @@
type ecdsa struct {
// algo is the ACVP name for this algorithm and also the command name
// given to the subprocess to hash with this hash function.
- algo string
- curves map[string]bool // supported curve names
- m *Subprocess
+ algo string
+ curves map[string]bool // supported curve names
+ primitives map[string]primitive
}
-func (e *ecdsa) Process(vectorSet []byte) (interface{}, error) {
+func (e *ecdsa) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed ecdsaTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
@@ -100,7 +100,7 @@
if group.SecretGenerationMode != "testing candidates" {
return nil, fmt.Errorf("invalid secret generation mode in test group %d: %q", group.ID, group.SecretGenerationMode)
}
- result, err := e.m.transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve))
+ result, err := m.Transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve))
if err != nil {
return nil, fmt.Errorf("key generation failed for test case %d/%d: %s", group.ID, test.ID, err)
}
@@ -117,7 +117,7 @@
if err != nil {
return nil, fmt.Errorf("failed to decode qy in test case %d/%d: %s", group.ID, test.ID, err)
}
- result, err := e.m.transact(e.algo+"/"+"keyVer", 1, []byte(group.Curve), qx, qy)
+ result, err := m.Transact(e.algo+"/"+"keyVer", 1, []byte(group.Curve), qx, qy)
if err != nil {
return nil, fmt.Errorf("key verification failed for test case %d/%d: %s", group.ID, test.ID, err)
}
@@ -134,7 +134,7 @@
}
case "sigGen":
- p := e.m.primitives[group.HashAlgo]
+ p := e.primitives[group.HashAlgo]
h, ok := p.(*hashPrimitive)
if !ok {
return nil, fmt.Errorf("unsupported hash algorithm %q in test group %d", group.HashAlgo, group.ID)
@@ -142,7 +142,7 @@
if len(sigGenPrivateKey) == 0 {
// Ask the subprocess to generate a key for this test group.
- result, err := e.m.transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve))
+ result, err := m.Transact(e.algo+"/"+"keyGen", 3, []byte(group.Curve))
if err != nil {
return nil, fmt.Errorf("key generation failed for test case %d/%d: %s", group.ID, test.ID, err)
}
@@ -163,7 +163,7 @@
}
op += "/componentTest"
}
- result, err := e.m.transact(op, 2, []byte(group.Curve), sigGenPrivateKey, []byte(group.HashAlgo), msg)
+ result, err := m.Transact(op, 2, []byte(group.Curve), sigGenPrivateKey, []byte(group.HashAlgo), msg)
if err != nil {
return nil, fmt.Errorf("signature generation failed for test case %d/%d: %s", group.ID, test.ID, err)
}
@@ -171,7 +171,7 @@
testResp.SHex = hex.EncodeToString(result[1])
case "sigVer":
- p := e.m.primitives[group.HashAlgo]
+ p := e.primitives[group.HashAlgo]
_, ok := p.(*hashPrimitive)
if !ok {
return nil, fmt.Errorf("unsupported hash algorithm %q in test group %d", group.HashAlgo, group.ID)
@@ -197,7 +197,7 @@
if err != nil {
return nil, fmt.Errorf("failed to decode S in test case %d/%d: %s", group.ID, test.ID, err)
}
- result, err := e.m.transact(e.algo+"/"+"sigVer", 1, []byte(group.Curve), []byte(group.HashAlgo), msg, qx, qy, r, s)
+ result, err := m.Transact(e.algo+"/"+"sigVer", 1, []byte(group.Curve), []byte(group.HashAlgo), msg, qx, qy, r, s)
if err != nil {
return nil, fmt.Errorf("signature verification failed for test case %d/%d: %s", group.ID, test.ID, err)
}
diff --git a/util/fipstools/acvp/acvptool/subprocess/hash.go b/util/fipstools/acvp/acvptool/subprocess/hash.go
index ea9668c..f283352 100644
--- a/util/fipstools/acvp/acvptool/subprocess/hash.go
+++ b/util/fipstools/acvp/acvptool/subprocess/hash.go
@@ -60,19 +60,18 @@
algo string
// size is the number of bytes of digest that the hash produces.
size int
- m *Subprocess
}
// hash uses the subprocess to hash msg and returns the digest.
-func (h *hashPrimitive) hash(msg []byte) []byte {
- result, err := h.m.transact(h.algo, 1, msg)
+func (h *hashPrimitive) hash(msg []byte, m Transactable) []byte {
+ result, err := m.Transact(h.algo, 1, msg)
if err != nil {
panic("hash operation failed: " + err.Error())
}
return result[0]
}
-func (h *hashPrimitive) Process(vectorSet []byte) (interface{}, error) {
+func (h *hashPrimitive) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed hashTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
@@ -101,7 +100,7 @@
case "AFT":
response.Tests = append(response.Tests, hashTestResponse{
ID: test.ID,
- DigestHex: hex.EncodeToString(h.hash(msg)),
+ DigestHex: hex.EncodeToString(h.hash(msg, m)),
})
case "MCT":
@@ -118,7 +117,7 @@
copy(buf[h.size:], msg)
copy(buf[2*h.size:], msg)
for j := 0; j < 1000; j++ {
- digest = h.hash(buf)
+ digest = h.hash(buf, m)
copy(buf, buf[h.size:])
copy(buf[2*h.size:], digest)
}
diff --git a/util/fipstools/acvp/acvptool/subprocess/hmac.go b/util/fipstools/acvp/acvptool/subprocess/hmac.go
index 09d5702..fb299c7 100644
--- a/util/fipstools/acvp/acvptool/subprocess/hmac.go
+++ b/util/fipstools/acvp/acvptool/subprocess/hmac.go
@@ -58,16 +58,15 @@
// given to the subprocess to HMAC with this hash function.
algo string
mdLen int // mdLen is the number of bytes of output that the underlying hash produces.
- m *Subprocess
}
// hmac uses the subprocess to compute HMAC and returns the result.
-func (h *hmacPrimitive) hmac(msg []byte, key []byte, outBits int) []byte {
+func (h *hmacPrimitive) hmac(msg []byte, key []byte, outBits int, m Transactable) []byte {
if outBits%8 != 0 {
panic("fractional-byte output length requested: " + strconv.Itoa(outBits))
}
outBytes := outBits / 8
- result, err := h.m.transact(h.algo, 1, msg, key)
+ result, err := m.Transact(h.algo, 1, msg, key)
if err != nil {
panic("HMAC operation failed: " + err.Error())
}
@@ -77,7 +76,7 @@
return result[0][:outBytes]
}
-func (h *hmacPrimitive) Process(vectorSet []byte) (interface{}, error) {
+func (h *hmacPrimitive) Process(vectorSet []byte, m Transactable) (interface{}, error) {
var parsed hmacTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
return nil, err
@@ -115,7 +114,7 @@
// https://usnistgov.github.io/ACVP/artifacts/acvp_sub_mac.html#hmac_vector_responses
response.Tests = append(response.Tests, hmacTestResponse{
ID: test.ID,
- MACHex: hex.EncodeToString(h.hmac(msg, key, group.MACBits)),
+ MACHex: hex.EncodeToString(h.hmac(msg, key, group.MACBits, m)),
})
}
diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
index 6f3e6ad..d22f3d5 100644
--- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go
+++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -12,6 +12,8 @@
// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+// Package subprocess contains functionality to talk to a modulewrapper for
+// testing of various algorithm implementations.
package subprocess
import (
@@ -24,6 +26,12 @@
"os/exec"
)
+// Transactable provides an interface to allow test injection of transactions
+// that don't call a server.
+type Transactable interface {
+ Transact(cmd string, expectedResults int, args ...[]byte) ([][]byte, error)
+}
+
// Subprocess is a "middle" layer that interacts with a FIPS module via running
// a command and speaking a simple protocol over stdin/stdout.
type Subprocess struct {
@@ -63,22 +71,22 @@
}
m.primitives = map[string]primitive{
- "SHA-1": &hashPrimitive{"SHA-1", 20, m},
- "SHA2-224": &hashPrimitive{"SHA2-224", 28, m},
- "SHA2-256": &hashPrimitive{"SHA2-256", 32, m},
- "SHA2-384": &hashPrimitive{"SHA2-384", 48, m},
- "SHA2-512": &hashPrimitive{"SHA2-512", 64, m},
- "ACVP-AES-ECB": &blockCipher{"AES", 16, false, m},
- "ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, true, m},
- "HMAC-SHA-1": &hmacPrimitive{"HMAC-SHA-1", 20, m},
- "HMAC-SHA2-224": &hmacPrimitive{"HMAC-SHA2-224", 28, m},
- "HMAC-SHA2-256": &hmacPrimitive{"HMAC-SHA2-256", 32, m},
- "HMAC-SHA2-384": &hmacPrimitive{"HMAC-SHA2-384", 48, m},
- "HMAC-SHA2-512": &hmacPrimitive{"HMAC-SHA2-512", 64, m},
- "ctrDRBG": &drbg{"ctrDRBG", map[string]bool{"AES-128": true, "AES-192": true, "AES-256": true}, m},
- "hmacDRBG": &drbg{"hmacDRBG", map[string]bool{"SHA-1": true, "SHA2-224": true, "SHA2-256": true, "SHA2-384": true, "SHA2-512": true}, m},
- "ECDSA": &ecdsa{"ECDSA", map[string]bool{"P-224": true, "P-256": true, "P-384": true, "P-521": true}, m},
+ "SHA-1": &hashPrimitive{"SHA-1", 20},
+ "SHA2-224": &hashPrimitive{"SHA2-224", 28},
+ "SHA2-256": &hashPrimitive{"SHA2-256", 32},
+ "SHA2-384": &hashPrimitive{"SHA2-384", 48},
+ "SHA2-512": &hashPrimitive{"SHA2-512", 64},
+ "ACVP-AES-ECB": &blockCipher{"AES", 16, false},
+ "ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, true},
+ "HMAC-SHA-1": &hmacPrimitive{"HMAC-SHA-1", 20},
+ "HMAC-SHA2-224": &hmacPrimitive{"HMAC-SHA2-224", 28},
+ "HMAC-SHA2-256": &hmacPrimitive{"HMAC-SHA2-256", 32},
+ "HMAC-SHA2-384": &hmacPrimitive{"HMAC-SHA2-384", 48},
+ "HMAC-SHA2-512": &hmacPrimitive{"HMAC-SHA2-512", 64},
+ "ctrDRBG": &drbg{"ctrDRBG", map[string]bool{"AES-128": true, "AES-192": true, "AES-256": true}},
+ "hmacDRBG": &drbg{"hmacDRBG", map[string]bool{"SHA-1": true, "SHA2-224": true, "SHA2-256": true, "SHA2-384": true, "SHA2-512": true}},
}
+ m.primitives["ECDSA"] = &ecdsa{"ECDSA", map[string]bool{"P-224": true, "P-256": true, "P-384": true, "P-521": true}, m.primitives}
return m
}
@@ -90,8 +98,8 @@
m.cmd.Wait()
}
-// transact performs a single request--response pair with the subprocess.
-func (m *Subprocess) transact(cmd string, expectedResults int, args ...[]byte) ([][]byte, error) {
+// Transact performs a single request--response pair with the subprocess.
+func (m *Subprocess) Transact(cmd string, expectedResults int, args ...[]byte) ([][]byte, error) {
argLength := len(cmd)
for _, arg := range args {
argLength += len(arg)
@@ -156,7 +164,7 @@
// format of the blob is defined by ACVP. See
// http://usnistgov.github.io/ACVP/artifacts/draft-fussell-acvp-spec-00.html#rfc.section.11.15.2.1
func (m *Subprocess) Config() ([]byte, error) {
- results, err := m.transact("getConfig", 1)
+ results, err := m.Transact("getConfig", 1)
if err != nil {
return nil, err
}
@@ -180,7 +188,7 @@
if !ok {
return nil, fmt.Errorf("unknown algorithm %q", algorithm)
}
- ret, err := prim.Process(vectorSet)
+ ret, err := prim.Process(vectorSet, m)
if err != nil {
return nil, err
}
@@ -188,5 +196,5 @@
}
type primitive interface {
- Process(vectorSet []byte) (interface{}, error)
+ Process(vectorSet []byte, t Transactable) (interface{}, error)
}
diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess_test.go b/util/fipstools/acvp/acvptool/subprocess/subprocess_test.go
new file mode 100644
index 0000000..eb70f9f
--- /dev/null
+++ b/util/fipstools/acvp/acvptool/subprocess/subprocess_test.go
@@ -0,0 +1,353 @@
+// Copyright (c) 2020, Google Inc.
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+package subprocess
+
+// NOTES:
+// - subprocess_test does not include testing for all subcomponents. It does
+// include unit tests for the following:
+// - hashPrimitive (for sha2-256 only)
+// - blockCipher (for AES)
+// - drbg (for ctrDRBG)
+// - All sample data (the valid & invalid strings) comes from calls to acvp as
+// of 2020-04-02.
+
+import (
+ "encoding/hex"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "testing"
+)
+
+var validSHA2_256 = []byte(`{
+ "vsId" : 182183,
+ "algorithm" : "SHA2-256",
+ "revision" : "1.0",
+ "isSample" : true,
+ "testGroups" : [ {
+ "tgId" : 1,
+ "testType" : "AFT",
+ "tests" : [ {
+ "tcId" : 1,
+ "msg" : "",
+ "len" : 0
+ }, {
+ "tcId" : 2,
+ "msg" : "",
+ "len" : 0
+ }, {
+ "tcId" : 3,
+ "msg" : "8E",
+ "len" : 8
+ }, {
+ "tcId" : 4,
+ "msg" : "7F10",
+ "len" : 16
+ }, {
+ "tcId" : 5,
+ "msg" : "F4422F",
+ "len" : 24
+ }, {
+ "tcId" : 6,
+ "msg" : "B3EF9698",
+ "len" : 32
+ }]
+ }]
+}`)
+
+var callsSHA2_256 = []fakeTransactCall{
+ fakeTransactCall{cmd: "SHA2-256", expectedNumResults: 1, args: [][]byte{[]byte{}}},
+ fakeTransactCall{cmd: "SHA2-256", expectedNumResults: 1, args: [][]byte{[]byte{}}},
+ fakeTransactCall{cmd: "SHA2-256", expectedNumResults: 1, args: [][]byte{fromHex("8E")}},
+ fakeTransactCall{cmd: "SHA2-256", expectedNumResults: 1, args: [][]byte{fromHex("7F10")}},
+ fakeTransactCall{cmd: "SHA2-256", expectedNumResults: 1, args: [][]byte{fromHex("F4422F")}},
+ fakeTransactCall{cmd: "SHA2-256", expectedNumResults: 1, args: [][]byte{fromHex("B3EF9698")}},
+}
+
+var invalidSHA2_256 = []byte(`{
+ "vsId" : 180207,
+ "algorithm" : "SHA2-256",
+ "revision" : "1.0",
+ "isSample" : true,
+ "testGroups" : [ {
+ "tgId" : abc,
+ "testType" : "AFT",
+ "tests" : [ {
+ "tcId" : 1,
+ "msg" : "",
+ "len" : 0
+ }, {
+ "tcId" : 2,
+ "msg" : "",
+ "len" : 0
+ }]
+ }]
+}`)
+
+var validACVPAESECB = []byte(`{
+ "vsId" : 181726,
+ "algorithm" : "ACVP-AES-ECB",
+ "revision" : "1.0",
+ "isSample" : true,
+ "testGroups" : [ {
+ "tgId" : 1,
+ "testType" : "AFT",
+ "direction" : "encrypt",
+ "keyLen" : 128,
+ "tests" : [ {
+ "tcId" : 1,
+ "pt" : "F34481EC3CC627BACD5DC3FB08F273E6",
+ "key" : "00000000000000000000000000000000"
+ }, {
+ "tcId" : 2,
+ "pt" : "9798C4640BAD75C7C3227DB910174E72",
+ "key" : "00000000000000000000000000000000"
+ }]
+ }]
+}`)
+
+var invalidACVPAESECB = []byte(`{
+ "vsId" : 181726,
+ "algorithm" : "ACVP-AES-ECB",
+ "revision" : "1.0",
+ "isSample" : true,
+ "testGroups" : [ {
+ "tgId" : 1,
+ "testType" : "AFT",
+ "direction" : "encrypt",
+ "keyLen" : 128,
+ "tests" : [ {
+ "tcId" : abc,
+ "pt" : "F34481EC3CC627BACD5DC3FB08F273E6",
+ "key" : "00000000000000000000000000000000"
+ }, {
+ "tcId" : 2,
+ "pt" : "9798C4640BAD75C7C3227DB910174E72",
+ "key" : "00000000000000000000000000000000"
+ }]
+ }]
+}`)
+
+var callsACVPAESECB = []fakeTransactCall{
+ fakeTransactCall{cmd: "AES/encrypt", expectedNumResults: 1, args: [][]byte{
+ fromHex("00000000000000000000000000000000"),
+ fromHex("F34481EC3CC627BACD5DC3FB08F273E6"),
+ }},
+ fakeTransactCall{cmd: "AES/encrypt", expectedNumResults: 1, args: [][]byte{
+ fromHex("00000000000000000000000000000000"),
+ fromHex("9798C4640BAD75C7C3227DB910174E72"),
+ }},
+}
+
+var validCTRDRBG = []byte(`{
+ "vsId" : 181791,
+ "algorithm" : "ctrDRBG",
+ "revision" : "1.0",
+ "isSample" : true,
+ "testGroups" : [ {
+ "tgId" : 1,
+ "testType" : "AFT",
+ "derFunc" : false,
+ "reSeed" : false,
+ "predResistance" : false,
+ "entropyInputLen" : 384,
+ "nonceLen" : 0,
+ "persoStringLen" : 0,
+ "additionalInputLen" : 0,
+ "returnedBitsLen" : 2048,
+ "mode" : "AES-256",
+ "tests" : [ {
+ "tcId" : 1,
+ "entropyInput" : "0D9E8EB273307D95C616C7ACC65669C246265E8A850EDCF36990D8A6F7EC3AEA0A7DDB888EE8D7ECC19EA7830310782C",
+ "nonce" : "",
+ "persoString" : "",
+ "otherInput" : [ {
+ "intendedUse" : "generate",
+ "additionalInput" : "",
+ "entropyInput" : ""
+ }, {
+ "intendedUse" : "generate",
+ "additionalInput" : "",
+ "entropyInput" : ""
+ } ]
+ }]
+ }]
+}`)
+
+var callsCTRDRBG = []fakeTransactCall{
+ fakeTransactCall{cmd: "ctrDRBG/AES-256", expectedNumResults: 1, args: [][]byte{
+ fromHex("00010000"), // uint32(256)
+ fromHex("0D9E8EB273307D95C616C7ACC65669C246265E8A850EDCF36990D8A6F7EC3AEA0A7DDB888EE8D7ECC19EA7830310782C"),
+ []byte{},
+ []byte{},
+ []byte{},
+ []byte{},
+ }},
+}
+
+var invalidCTRDRBG = []byte(`{
+ "vsId" : 181791,
+ "algorithm" : "ctrDRBG",
+ "revision" : "1.0",
+ "isSample" : true,
+ "testGroups" : [ {
+ "tgId" : 1,
+ "testType" : "AFT",
+ "derFunc" : false,
+ "reSeed" : false,
+ "predResistance" : false,
+ "entropyInputLen" : 384,
+ "nonceLen" : 0,
+ "persoStringLen" : 0,
+ "additionalInputLen" : 0,
+ "returnedBitsLen" : 2048,
+ "mode" : "AES-256",
+ "tests" : [ {
+ "tcId" : abc,
+ "entropyInput" : "0D9E8EB273307D95C616C7ACC65669C246265E8A850EDCF36990D8A6F7EC3AEA0A7DDB888EE8D7ECC19EA7830310782C",
+ "nonce" : "",
+ "persoString" : "",
+ "otherInput" : [ {
+ "intendedUse" : "generate",
+ "additionalInput" : "",
+ "entropyInput" : ""
+ }, {
+ "intendedUse" : "generate",
+ "additionalInput" : "",
+ "entropyInput" : ""
+ } ]
+ }]
+ }]
+}`)
+
+// fakeTransactable provides a fake to return results that don't go to the ACVP
+// server.
+type fakeTransactable struct {
+ calls []fakeTransactCall
+ results []fakeTransactResult
+}
+
+type fakeTransactCall struct {
+ cmd string
+ expectedNumResults int
+ args [][]byte
+}
+
+type fakeTransactResult struct {
+ bytes [][]byte
+ err error
+}
+
+func (f *fakeTransactable) Transact(cmd string, expectedNumResults int, args ...[]byte) ([][]byte, error) {
+ f.calls = append(f.calls, fakeTransactCall{cmd, expectedNumResults, args})
+
+ if len(f.results) == 0 {
+ return nil, fmt.Errorf("Transact called but no TransactResults remain")
+ }
+
+ ret := f.results[0]
+ f.results = f.results[1:]
+ return ret.bytes, ret.err
+}
+
+func newFakeTransactable(name string, numResponses int) *fakeTransactable {
+ ret := new(fakeTransactable)
+
+ // Add results requested by caller.
+ dummyResult := [][]byte{[]byte("dummy result")}
+ for i := 0; i < numResponses; i++ {
+ ret.results = append(ret.results, fakeTransactResult{bytes: dummyResult, err: nil})
+ }
+
+ return ret
+}
+
+// TestPrimitiveParsesJSON verifies that basic JSON parsing with a
+// small passing case & a single failing case.
+func TestPrimitives(t *testing.T) {
+ var tests = []struct {
+ algo string
+ p primitive
+ validJSON []byte
+ invalidJSON []byte
+ expectedCalls []fakeTransactCall
+ results []fakeTransactResult
+ }{
+ {
+ algo: "SHA2-256",
+ p: &hashPrimitive{"SHA2-256", 32},
+ validJSON: validSHA2_256,
+ invalidJSON: invalidSHA2_256,
+ expectedCalls: callsSHA2_256,
+ },
+ {
+ algo: "ACVP-AES-ECB",
+ p: &blockCipher{"AES", 16, false},
+ validJSON: validACVPAESECB,
+ invalidJSON: invalidACVPAESECB,
+ expectedCalls: callsACVPAESECB,
+ },
+ {
+ algo: "ctrDRBG",
+ p: &drbg{"ctrDRBG", map[string]bool{"AES-128": true, "AES-192": true, "AES-256": true}},
+ validJSON: validCTRDRBG,
+ invalidJSON: invalidCTRDRBG,
+ expectedCalls: callsCTRDRBG,
+ results: []fakeTransactResult{
+ fakeTransactResult{bytes: [][]byte{make([]byte, 256)}},
+ },
+ },
+ }
+
+ for _, test := range tests {
+ transactable := newFakeTransactable(test.algo, len(test.expectedCalls))
+ if len(test.results) > 0 {
+ transactable.results = test.results
+ }
+
+ if _, err := test.p.Process(test.validJSON, transactable); err != nil {
+ t.Errorf("%s: valid input failed unexpectedly: %v", test.algo, err)
+ continue
+ }
+
+ if len(transactable.calls) != len(test.expectedCalls) {
+ t.Errorf("%s: got %d results, but want %d", test.algo, len(transactable.calls), len(test.expectedCalls))
+ continue
+ }
+
+ if !reflect.DeepEqual(transactable.calls, test.expectedCalls) {
+ t.Errorf("%s: got:\n%#v\n\nwant:\n%#v", test.algo, transactable.calls, test.expectedCalls)
+ }
+
+ if _, err := test.p.Process(test.invalidJSON, transactable); !isJSONSyntaxError(err) {
+ t.Errorf("Test %v with invalid input either passed or failed with the wrong error (%v)", test.algo, err)
+ }
+ }
+}
+
+// isJSONSyntaxError returns true if the error is a json syntax error.
+func isJSONSyntaxError(err error) bool {
+ _, ok := err.(*json.SyntaxError)
+ return ok
+}
+
+// fromHex wraps hex.DecodeString so it can be used in initializers. Panics on error.
+func fromHex(s string) []byte {
+ key, err := hex.DecodeString(s)
+ if err != nil {
+ panic(fmt.Sprintf("Failed on hex.DecodeString(%q) with %v", s, err))
+ }
+ return key
+}