acvp: move hash iterations into modulewrapper.

In cases where the RPC from acvptool to modulewrapper is expensive,
these iterated tests take excessive amounts of time. By moving the
inner loop into the module wrapper the number of round-trips is reduced
by 1000×.

Change-Id: Ic047db071239492e416a08cab60d6a7e2905e8dc
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47364
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
grpc-202302
Adam Langley 4 years ago committed by CQ bot account: commit-bot@chromium.org
parent 2b2cb7d93c
commit 9fc6174162
  1. 8
      util/fipstools/acvp/ACVP.md
  2. 31
      util/fipstools/acvp/acvptool/subprocess/hash.go
  3. 30
      util/fipstools/acvp/modulewrapper/modulewrapper.cc

@ -90,9 +90,15 @@ The other commands are as follows. (Note that you only need to implement the com
| SHA2-384 | Value to hash | Digest |
| SHA2-512 | Value to hash | Digest |
| SHA2-512/256 | Value to hash | Digest |
| SHA-1/MCT | Initial seed¹ | Digest |
| SHA2-224/MCT | Initial seed¹ | Digest |
| SHA2-256/MCT | Initial seed¹ | Digest |
| SHA2-384/MCT | Initial seed¹ | Digest |
| SHA2-512/MCT | Initial seed¹ | Digest |
| SHA2-512/256/MCT | Initial seed¹ | Digest |
| TLSKDF/&lt;1.0\|1.2&gt;/&lt;HASH&gt; | Number output bytes, secret, label, seed1, seed2 | Output |
¹ The iterated block-cipher tests would result in excessive numbers of round trips if the module wrapper handled only basic operations. Thus some ACVP logic is pushed down for these tests so that the inner loop can be handled locally. Either read the [NIST documentation](https://usnistgov.github.io/ACVP/draft-celi-acvp-symmetric.html#name-monte-carlo-tests-for-block) to understand the iteration count and return values or, probably more fruitfully, see how these functions are handled in the `modulewrapper` directory.
¹ The iterated tests would result in excessive numbers of round trips if the module wrapper handled only basic operations. Thus some ACVP logic is pushed down for these tests so that the inner loop can be handled locally. Either read the NIST documentation ([block-ciphers](https://usnistgov.github.io/ACVP/draft-celi-acvp-symmetric.html#name-monte-carlo-tests-for-block) [hashes](https://pages.nist.gov/ACVP/draft-celi-acvp-sha.html#name-monte-carlo-tests-for-sha-1)) to understand the iteration count and return values or, probably more fruitfully, see how these functions are handled in the `modulewrapper` directory.
## Online operation

@ -62,15 +62,6 @@ type hashPrimitive struct {
size int
}
// hash uses the subprocess to hash msg and returns the digest.
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, m Transactable) (interface{}, error) {
var parsed hashTestVectorSet
if err := json.Unmarshal(vectorSet, &parsed); err != nil {
@ -98,9 +89,14 @@ func (h *hashPrimitive) Process(vectorSet []byte, m Transactable) (interface{},
// http://usnistgov.github.io/ACVP/artifacts/draft-celi-acvp-sha-00.html#rfc.section.3
switch group.Type {
case "AFT":
result, err := m.Transact(h.algo, 1, msg)
if err != nil {
panic(h.algo + " hash operation failed: " + err.Error())
}
response.Tests = append(response.Tests, hashTestResponse{
ID: test.ID,
DigestHex: hex.EncodeToString(h.hash(msg, m)),
DigestHex: hex.EncodeToString(result[0]),
})
case "MCT":
@ -110,20 +106,15 @@ func (h *hashPrimitive) Process(vectorSet []byte, m Transactable) (interface{},
testResponse := hashTestResponse{ID: test.ID}
buf := make([]byte, 3*h.size)
var digest []byte
digest := msg
for i := 0; i < 100; i++ {
copy(buf, msg)
copy(buf[h.size:], msg)
copy(buf[2*h.size:], msg)
for j := 0; j < 1000; j++ {
digest = h.hash(buf, m)
copy(buf, buf[h.size:])
copy(buf[2*h.size:], digest)
result, err := m.Transact(h.algo+"/MCT", 1, digest)
if err != nil {
panic(h.algo + " hash operation failed: " + err.Error())
}
digest = result[0]
testResponse.MCTResults = append(testResponse.MCTResults, hashMCTResult{hex.EncodeToString(digest)})
msg = digest
}
response.Tests = append(response.Tests, testResponse)

@ -860,6 +860,30 @@ static bool Hash(const Span<const uint8_t> args[], ReplyCallback write_reply) {
return write_reply({Span<const uint8_t>(digest)});
}
template <uint8_t *(*OneShotHash)(const uint8_t *, size_t, uint8_t *),
size_t DigestLength>
static bool HashMCT(const Span<const uint8_t> args[],
ReplyCallback write_reply) {
if (args[0].size() != DigestLength) {
return false;
}
uint8_t buf[DigestLength * 3];
memcpy(buf, args[0].data(), DigestLength);
memcpy(buf + DigestLength, args[0].data(), DigestLength);
memcpy(buf + 2 * DigestLength, args[0].data(), DigestLength);
for (size_t i = 0; i < 1000; i++) {
uint8_t digest[DigestLength];
OneShotHash(buf, sizeof(buf), digest);
memmove(buf, buf + DigestLength, DigestLength * 2);
memcpy(buf + DigestLength * 2, digest, DigestLength);
}
return write_reply(
{Span<const uint8_t>(buf + 2 * DigestLength, DigestLength)});
}
static uint32_t GetIterations(const Span<const uint8_t> iterations_bytes) {
uint32_t iterations;
if (iterations_bytes.size() != sizeof(iterations)) {
@ -1861,6 +1885,12 @@ static constexpr struct {
{"SHA2-384", 1, Hash<SHA384, SHA384_DIGEST_LENGTH>},
{"SHA2-512", 1, Hash<SHA512, SHA512_DIGEST_LENGTH>},
{"SHA2-512/256", 1, Hash<SHA512_256, SHA512_256_DIGEST_LENGTH>},
{"SHA-1/MCT", 1, HashMCT<SHA1, SHA_DIGEST_LENGTH>},
{"SHA2-224/MCT", 1, HashMCT<SHA224, SHA224_DIGEST_LENGTH>},
{"SHA2-256/MCT", 1, HashMCT<SHA256, SHA256_DIGEST_LENGTH>},
{"SHA2-384/MCT", 1, HashMCT<SHA384, SHA384_DIGEST_LENGTH>},
{"SHA2-512/MCT", 1, HashMCT<SHA512, SHA512_DIGEST_LENGTH>},
{"SHA2-512/256/MCT", 1, HashMCT<SHA512_256, SHA512_256_DIGEST_LENGTH>},
{"AES/encrypt", 3, AES<AES_set_encrypt_key, AES_encrypt>},
{"AES/decrypt", 3, AES<AES_set_decrypt_key, AES_decrypt>},
{"AES-CBC/encrypt", 4, AES_CBC<AES_set_encrypt_key, AES_ENCRYPT>},

Loading…
Cancel
Save