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 +}