Handle "acceptable" Wycheproof inputs unambiguously.

This CL updates the JSON conversion to preserve the flags. A
WycheproofResult now captures both "result" and "flags". An "acceptable"
test case's validity is determined by its flags. By default, we consider
an "acceptable" case as invalid, but a test driver may mark some of them
as valid by listing the flags as a parameter.

Previously, some Wycheproof tests (I think it was x25519_tests.txt?) did
not contain enough information to resolve this unambiguously. This has
since been fixed.

This also makes the converted files smaller because we no longer expand the
flags into comments.

Change-Id: I2ca02d7f1b95f250409e8b23c4ad7bb595d77fdf
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/39188
Commit-Queue: Adam Langley <agl@google.com>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/test/wycheproof_util.cc b/crypto/test/wycheproof_util.cc
index 8f7dfeb..cd870dd 100644
--- a/crypto/test/wycheproof_util.cc
+++ b/crypto/test/wycheproof_util.cc
@@ -14,6 +14,10 @@
 
 #include "./wycheproof_util.h"
 
+#include <stdlib.h>
+
+#include <algorithm>
+
 #include <openssl/bn.h>
 #include <openssl/digest.h>
 #include <openssl/ec.h>
@@ -22,21 +26,55 @@
 #include "./file_test.h"
 
 
+bool WycheproofResult::IsValid(
+    const std::vector<std::string> &acceptable_flags) const {
+  switch (raw_result) {
+    case WycheproofRawResult::kValid:
+      return true;
+    case WycheproofRawResult::kInvalid:
+      return false;
+    case WycheproofRawResult::kAcceptable:
+      for (const auto &flag : flags) {
+        if (std::find(acceptable_flags.begin(), acceptable_flags.end(), flag) ==
+            acceptable_flags.end()) {
+          return false;
+        }
+      }
+      return true;
+  }
+
+  abort();
+}
+
 bool GetWycheproofResult(FileTest *t, WycheproofResult *out) {
   std::string result;
   if (!t->GetAttribute(&result, "result")) {
     return false;
   }
   if (result == "valid") {
-    *out = WycheproofResult::kValid;
+    out->raw_result = WycheproofRawResult::kValid;
   } else if (result == "invalid") {
-    *out = WycheproofResult::kInvalid;
+    out->raw_result = WycheproofRawResult::kInvalid;
   } else if (result == "acceptable") {
-    *out = WycheproofResult::kAcceptable;
+    out->raw_result = WycheproofRawResult::kAcceptable;
   } else {
     t->PrintLine("Bad result string '%s'", result.c_str());
     return false;
   }
+
+  out->flags.clear();
+  if (t->HasAttribute("flags")) {
+    std::string flags = t->GetAttributeOrDie("flags");
+    size_t idx = 0;
+    while (idx < flags.size()) {
+      size_t comma = flags.find(',', idx);
+      if (comma == std::string::npos) {
+        comma = flags.size();
+      }
+      out->flags.push_back(flags.substr(idx, comma - idx));
+      idx = comma + 1;
+    }
+  }
   return true;
 }
 
diff --git a/crypto/test/wycheproof_util.h b/crypto/test/wycheproof_util.h
index 4d8a14c..67e0ed3 100644
--- a/crypto/test/wycheproof_util.h
+++ b/crypto/test/wycheproof_util.h
@@ -17,18 +17,31 @@
 
 #include <openssl/base.h>
 
+#include <string>
+#include <vector>
+
 
 // This header contains convenience functions for Wycheproof tests.
 
 class FileTest;
 
-enum class WycheproofResult {
+enum class WycheproofRawResult {
   kValid,
   kInvalid,
   kAcceptable,
 };
 
-// GetWycheproofResult sets |*out| to the parsed "result" key of |t|.
+struct WycheproofResult {
+  WycheproofRawResult raw_result;
+  std::vector<std::string> flags;
+
+  // IsValid returns true if the Wycheproof test should be considered valid. A
+  // test result of "acceptable" is treated as valid if all flags are included
+  // in |acceptable_flags| and invalid otherwise.
+  bool IsValid(const std::vector<std::string> &acceptable_flags = {}) const;
+};
+
+// GetWycheproofResult sets |*out| to the parsed "result" and "flags" keys of |t|.
 bool GetWycheproofResult(FileTest *t, WycheproofResult *out);
 
 // GetWycheproofDigest returns a digest function using the Wycheproof name, or