Introduce a mechanism for base64 options.

We may wish to pass data to the runner that contains NULs.

Change-Id: Id78dad0ad0b5b6d0537481c818e3febdf1740cc9
Reviewed-on: https://boringssl-review.googlesource.com/1603
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 1856a03..2ca0b21 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -3,6 +3,7 @@
 import (
 	"bytes"
 	"crypto/x509"
+	"encoding/base64"
 	"flag"
 	"fmt"
 	"io"
@@ -208,11 +209,14 @@
 				CertTypeECDSASign,
 			},
 		},
-		flags: []string{"-expect-certificate-types", string([]byte{
-			CertTypeDSSSign,
-			CertTypeRSASign,
-			CertTypeECDSASign,
-		})},
+		flags: []string{
+			"-expect-certificate-types",
+			base64.StdEncoding.EncodeToString([]byte{
+				CertTypeDSSSign,
+				CertTypeRSASign,
+				CertTypeECDSASign,
+			}),
+		},
 	},
 	{
 		name: "NoClientCertificate",
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index 9716227..e69d570 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -16,6 +16,9 @@
 
 #include <string.h>
 
+#include <memory>
+
+#include <openssl/base64.h>
 
 namespace {
 
@@ -54,14 +57,10 @@
 
 const size_t kNumBoolFlags = sizeof(kBoolFlags) / sizeof(kBoolFlags[0]);
 
-// TODO(davidben): Some of these should be in a new kBase64Flags to allow NUL
-// bytes.
 const StringFlag kStringFlags[] = {
   { "-key-file", &TestConfig::key_file },
   { "-cert-file", &TestConfig::cert_file },
   { "-expect-server-name", &TestConfig::expected_server_name },
-  // Conveniently, 00 is not a certificate type.
-  { "-expect-certificate-types", &TestConfig::expected_certificate_types },
   { "-advertise-npn", &TestConfig::advertise_npn },
   { "-expect-next-proto", &TestConfig::expected_next_proto },
   { "-select-next-proto", &TestConfig::select_next_proto },
@@ -69,6 +68,12 @@
 
 const size_t kNumStringFlags = sizeof(kStringFlags) / sizeof(kStringFlags[0]);
 
+const StringFlag kBase64Flags[] = {
+  { "-expect-certificate-types", &TestConfig::expected_certificate_types },
+};
+
+const size_t kNumBase64Flags = sizeof(kBase64Flags) / sizeof(kBase64Flags[0]);
+
 }  // namespace
 
 TestConfig::TestConfig()
@@ -117,6 +122,32 @@
       continue;
     }
 
+    for (j = 0; j < kNumBase64Flags; j++) {
+      if (strcmp(argv[i], kBase64Flags[j].flag) == 0) {
+        break;
+      }
+    }
+    if (j < kNumBase64Flags) {
+      i++;
+      if (i >= argc) {
+        fprintf(stderr, "Missing parameter\n");
+        return false;
+      }
+      size_t len;
+      if (!EVP_DecodedLength(&len, strlen(argv[i]))) {
+        fprintf(stderr, "Invalid base64: %s\n", argv[i]);
+      }
+      std::unique_ptr<uint8_t[]> decoded(new uint8_t[len]);
+      if (!EVP_DecodeBase64(decoded.get(), &len, len,
+                            reinterpret_cast<const uint8_t *>(argv[i]),
+                            strlen(argv[i]))) {
+        fprintf(stderr, "Invalid base64: %s\n", argv[i]);
+      }
+      out_config->*(kBase64Flags[j].member) = std::string(
+          reinterpret_cast<const char *>(decoded.get()), len);
+      continue;
+    }
+
     fprintf(stderr, "Unknown argument: %s\n", argv[i]);
     return false;
   }