acvp: abstract out MCT iteration functions.
(There's going to be more and it was getting too big.)
Change-Id: I16a49f77975697bb5a04f2adfd465b09c2a09ef3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/43404
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/util/fipstools/acvp/acvptool/subprocess/block.go b/util/fipstools/acvp/acvptool/subprocess/block.go
index 9e51078..fa933c6 100644
--- a/util/fipstools/acvp/acvptool/subprocess/block.go
+++ b/util/fipstools/acvp/acvptool/subprocess/block.go
@@ -20,6 +20,126 @@
"fmt"
)
+// aesKeyShuffle is the "AES Monte Carlo Key Shuffle" from the ACVP
+// specification.
+func aesKeyShuffle(key, result, prevResult []byte) {
+ switch len(key) {
+ case 16:
+ for i := range key {
+ key[i] ^= result[i]
+ }
+ case 24:
+ for i := 0; i < 8; i++ {
+ key[i] ^= prevResult[i+8]
+ }
+ for i := range result {
+ key[i+8] ^= result[i]
+ }
+ case 32:
+ for i, b := range prevResult {
+ key[i] ^= b
+ }
+ for i, b := range result {
+ key[i+16] ^= b
+ }
+ default:
+ panic("unhandled key length")
+ }
+}
+
+// IterateAES implements the "AES Monte Carlo Test - ECB mode" from the ACVP
+// specification.
+func IterateAES(transact func(n int, args ...[]byte) ([][]byte, error), encrypt bool, key, input, iv []byte) (mctResults []blockCipherMCTResult) {
+ for i := 0; i < 100; i++ {
+ var iteration blockCipherMCTResult
+ iteration.KeyHex = hex.EncodeToString(key)
+ if encrypt {
+ iteration.PlaintextHex = hex.EncodeToString(input)
+ } else {
+ iteration.CiphertextHex = hex.EncodeToString(input)
+ }
+
+ var result, prevResult []byte
+ for j := 0; j < 1000; j++ {
+ prevResult = input
+ result, err := transact(1, key, input)
+ if err != nil {
+ panic("block operation failed")
+ }
+ input = result[0]
+ }
+ result = input
+
+ if encrypt {
+ iteration.CiphertextHex = hex.EncodeToString(result)
+ } else {
+ iteration.PlaintextHex = hex.EncodeToString(result)
+ }
+
+ aesKeyShuffle(key, result, prevResult)
+ mctResults = append(mctResults, iteration)
+ }
+
+ return mctResults
+}
+
+// IterateAESCBC implements the "AES Monte Carlo Test - CBC mode" from the ACVP
+// specification.
+func IterateAESCBC(transact func(n int, args ...[]byte) ([][]byte, error), encrypt bool, key, input, iv []byte) (mctResults []blockCipherMCTResult) {
+ for i := 0; i < 100; i++ {
+ var iteration blockCipherMCTResult
+ iteration.KeyHex = hex.EncodeToString(key)
+ if encrypt {
+ iteration.PlaintextHex = hex.EncodeToString(input)
+ } else {
+ iteration.CiphertextHex = hex.EncodeToString(input)
+ }
+
+ var result, prevResult []byte
+ iteration.IVHex = hex.EncodeToString(iv)
+
+ var prevInput []byte
+ for j := 0; j < 1000; j++ {
+ prevResult = result
+ if j > 0 {
+ if encrypt {
+ iv = result
+ } else {
+ iv = prevInput
+ }
+ }
+
+ results, err := transact(1, key, input, iv)
+ if err != nil {
+ panic("block operation failed")
+ }
+ result = results[0]
+
+ prevInput = input
+ if j == 0 {
+ input = iv
+ } else {
+ input = prevResult
+ }
+ }
+
+ if encrypt {
+ iteration.CiphertextHex = hex.EncodeToString(result)
+ } else {
+ iteration.PlaintextHex = hex.EncodeToString(result)
+ }
+
+ aesKeyShuffle(key, result, prevResult)
+
+ iv = result
+ input = prevResult
+
+ mctResults = append(mctResults, iteration)
+ }
+
+ return mctResults
+}
+
// blockCipher implements an ACVP algorithm by making requests to the subprocess
// to encrypt and decrypt with a block cipher.
type blockCipher struct {
@@ -27,6 +147,7 @@
blockSize int
inputsAreBlockMultiples bool
hasIV bool
+ mctFunc func(transact func(n int, args ...[]byte) ([][]byte, error), encrypt bool, key, input, iv []byte) (result []blockCipherMCTResult)
}
type blockCipherVectorSet struct {
@@ -101,6 +222,9 @@
case "AFT", "CTR":
mct = false
case "MCT":
+ if b.mctFunc == nil {
+ return nil, fmt.Errorf("test group %d has type MCT which is unsupported for %q", group.ID, op)
+ }
mct = true
default:
return nil, fmt.Errorf("test group %d has unknown type %q", group.ID, group.Type)
@@ -111,6 +235,10 @@
}
keyBytes := group.KeyBits / 8
+ transact := func(n int, args ...[]byte) ([][]byte, error) {
+ return m.Transact(op, n, args...)
+ }
+
for _, test := range group.Tests {
if len(test.KeyHex) != keyBytes*2 {
return nil, fmt.Errorf("test case %d/%d contains key %q of length %d, but expected %d-bit key", group.ID, test.ID, test.KeyHex, len(test.KeyHex), group.KeyBits)
@@ -167,93 +295,7 @@
testResp.PlaintextHex = hex.EncodeToString(result[0])
}
} else {
- for i := 0; i < 100; i++ {
- var iteration blockCipherMCTResult
- iteration.KeyHex = hex.EncodeToString(key)
- if encrypt {
- iteration.PlaintextHex = hex.EncodeToString(input)
- } else {
- iteration.CiphertextHex = hex.EncodeToString(input)
- }
-
- var result, prevResult []byte
- if !b.hasIV {
- for j := 0; j < 1000; j++ {
- prevResult = input
- result, err := m.Transact(op, 1, key, input)
- if err != nil {
- panic("block operation failed")
- }
- input = result[0]
- }
- result = input
- } else {
- iteration.IVHex = hex.EncodeToString(iv)
-
- var prevInput []byte
- for j := 0; j < 1000; j++ {
- prevResult = result
- if j > 0 {
- if encrypt {
- iv = result
- } else {
- iv = prevInput
- }
- }
-
- results, err := m.Transact(op, 1, key, input, iv)
- if err != nil {
- panic("block operation failed")
- }
- result = results[0]
-
- prevInput = input
- if j == 0 {
- input = iv
- } else {
- input = prevResult
- }
- }
- }
-
- if encrypt {
- iteration.CiphertextHex = hex.EncodeToString(result)
- } else {
- iteration.PlaintextHex = hex.EncodeToString(result)
- }
-
- switch keyBytes {
- case 16:
- for i := range key {
- key[i] ^= result[i]
- }
- case 24:
- for i := 0; i < 8; i++ {
- key[i] ^= prevResult[i+8]
- }
- for i := range result {
- key[i+8] ^= result[i]
- }
- case 32:
- for i, b := range prevResult {
- key[i] ^= b
- }
- for i, b := range result {
- key[i+16] ^= b
- }
- default:
- panic("unhandled key length")
- }
-
- if !b.hasIV {
- input = result
- } else {
- iv = result
- input = prevResult
- }
-
- testResp.MCTResults = append(testResp.MCTResults, iteration)
- }
+ testResp.MCTResults = b.mctFunc(transact, encrypt, key, input, iv)
}
response.Tests = append(response.Tests, testResp)
diff --git a/util/fipstools/acvp/acvptool/subprocess/subprocess.go b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
index c713ea8..cfa300f 100644
--- a/util/fipstools/acvp/acvptool/subprocess/subprocess.go
+++ b/util/fipstools/acvp/acvptool/subprocess/subprocess.go
@@ -76,9 +76,9 @@
"SHA2-256": &hashPrimitive{"SHA2-256", 32},
"SHA2-384": &hashPrimitive{"SHA2-384", 48},
"SHA2-512": &hashPrimitive{"SHA2-512", 64},
- "ACVP-AES-ECB": &blockCipher{"AES", 16, true, false},
- "ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, true, true},
- "ACVP-AES-CTR": &blockCipher{"AES-CTR", 16, false, true},
+ "ACVP-AES-ECB": &blockCipher{"AES", 16, true, false, IterateAES},
+ "ACVP-AES-CBC": &blockCipher{"AES-CBC", 16, true, true, IterateAESCBC},
+ "ACVP-AES-CTR": &blockCipher{"AES-CTR", 16, false, true, nil},
"ACVP-AES-GCM": &aead{"AES-GCM", false},
"ACVP-AES-CCM": &aead{"AES-CCM", true},
"ACVP-AES-KW": &aead{"AES-KW", false},