Add a test driver for Wycheproof's x25519_test.json.

FileTest and Wycheproof express more-or-less the same things, so I've
just written a script to mechanically convert them. Saves writing a JSON
parser.

I've also left a TODO with other files that are worth converting. Per
Thai, the webcrypto variants of the files are just a different format
and will later be consolidated, so I've ignored those. The
curve/hash-specific ECDSA files and the combined one are intended to be
the same, so I've ignored the combined one. (Just by test counts, there
are some discrepancies, but Thai says he'll fix that and we can update
when that happens.)

Change-Id: I5fcbd5cb0e1bea32964b09fb469cb43410f53c2d
Reviewed-on: https://boringssl-review.googlesource.com/27785
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/curve25519/x25519_test.cc b/crypto/curve25519/x25519_test.cc
index 3e0a27e..2b50891 100644
--- a/crypto/curve25519/x25519_test.cc
+++ b/crypto/curve25519/x25519_test.cc
@@ -21,6 +21,7 @@
 #include <openssl/curve25519.h>
 
 #include "../internal.h"
+#include "../test/file_test.h"
 #include "../test/test_util.h"
 
 
@@ -124,3 +125,25 @@
 
   EXPECT_EQ(Bytes(kExpected), Bytes(scalar));
 }
+
+TEST(X25519Test, Wycheproof) {
+  FileTestGTest("third_party/wycheproof/x25519_test.txt", [](FileTest *t) {
+      t->IgnoreInstruction("curve");
+      t->IgnoreAttribute("curve");
+
+      // Our implementation tolerates the Wycheproof "acceptable"
+      // inputs. Wycheproof's valid vs. acceptable criteria does not match our
+      // X25519 return value, so we test only the overall output.
+      t->IgnoreAttribute("result");
+
+      std::vector<uint8_t> priv, pub, shared;
+      ASSERT_TRUE(t->GetBytes(&priv, "private"));
+      ASSERT_TRUE(t->GetBytes(&pub, "public"));
+      ASSERT_TRUE(t->GetBytes(&shared, "shared"));
+      ASSERT_EQ(32u, priv.size());
+      ASSERT_EQ(32u, pub.size());
+      uint8_t secret[32];
+      X25519(secret, priv.data(), pub.data());
+      EXPECT_EQ(Bytes(secret), Bytes(shared));
+  });
+}
diff --git a/crypto/test/file_test.h b/crypto/test/file_test.h
index 002b350..a164e99 100644
--- a/crypto/test/file_test.h
+++ b/crypto/test/file_test.h
@@ -154,6 +154,9 @@
   // missing. It should only be used after a |HasAttribute| call.
   const std::string &GetAttributeOrDie(const std::string &key);
 
+  // IgnoreAttribute marks the attribute with key |key| as used.
+  void IgnoreAttribute(const std::string &key) { HasAttribute(key); }
+
   // GetBytes looks up the attribute with key |key| and decodes it as a byte
   // string. On success, it writes the result to |*out| and returns
   // true. Otherwise it returns false with an error to |stderr|. The value may
@@ -173,6 +176,9 @@
   // HasInstruction returns true if the current test has an instruction.
   bool HasInstruction(const std::string &key);
 
+  // IgnoreInstruction marks the instruction with key |key| as used.
+  void IgnoreInstruction(const std::string &key) { HasInstruction(key); }
+
   // GetInstruction looks up the instruction with key |key|. It sets
   // |*out_value| to the value (empty string if the instruction has no value)
   // and returns true if it exists and returns false with an error to |stderr|
diff --git a/sources.cmake b/sources.cmake
index ee0f9e6..d2fea88 100644
--- a/sources.cmake
+++ b/sources.cmake
@@ -59,4 +59,5 @@
   crypto/x509/some_names1.pem
   crypto/x509/some_names2.pem
   crypto/x509/some_names3.pem
+  third_party/wycheproof/x25519_test.txt
 )
diff --git a/third_party/wycheproof/convert_wycheproof.go b/third_party/wycheproof/convert_wycheproof.go
new file mode 100644
index 0000000..312a417
--- /dev/null
+++ b/third_party/wycheproof/convert_wycheproof.go
@@ -0,0 +1,255 @@
+/* Copyright (c) 2018, 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. */
+
+// convert_wycheproof.go converts Wycheproof test vectors into a format more
+// easily consumed by BoringSSL.
+package main
+
+import (
+	"encoding/json"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"os"
+	"sort"
+	"strings"
+)
+
+type wycheproofTest struct {
+	Algorithm        string            `json:"algorithm"`
+	GeneratorVersion string            `json:"generatorVersion"`
+	NumberOfTests    int               `json:"numberOfTests"`
+	Notes            map[string]string `json:"notes"`
+	Header           []string          `json:"header"`
+	// encoding/json does not support collecting unused keys, so we leave
+	// everything past this point as generic.
+	TestGroups []map[string]interface{} `json:"testGroups"`
+}
+
+func sortedKeys(m map[string]interface{}) []string {
+	keys := make([]string, 0, len(m))
+	for k, _ := range m {
+		keys = append(keys, k)
+	}
+	sort.Strings(keys)
+	return keys
+}
+
+func printAttribute(w io.Writer, key string, valueI interface{}, isInstruction bool) error {
+	switch value := valueI.(type) {
+	case float64:
+		if float64(int(value)) != value {
+			panic(key + "was not an integer.")
+		}
+		if isInstruction {
+			if _, err := fmt.Fprintf(w, "[%s = %d]\n", key, int(value)); err != nil {
+				return err
+			}
+		} else {
+			if _, err := fmt.Fprintf(w, "%s = %d\n", key, int(value)); err != nil {
+				return err
+			}
+		}
+	case string:
+		if strings.Contains(value, "\n") {
+			panic(key + " contained a newline.")
+		}
+		if isInstruction {
+			if _, err := fmt.Fprintf(w, "[%s = %s]\n", key, value); err != nil {
+				return err
+			}
+		} else {
+			if _, err := fmt.Fprintf(w, "%s = %s\n", key, value); err != nil {
+				return err
+			}
+		}
+	case map[string]interface{}:
+		for _, k := range sortedKeys(value) {
+			if err := printAttribute(w, key+"."+k, value[k], isInstruction); err != nil {
+				return err
+			}
+		}
+	default:
+		panic(fmt.Sprintf("Unknown type for %q: %T", key, valueI))
+	}
+	return nil
+}
+
+func printComment(w io.Writer, in string) error {
+	const width = 80 - 2
+	lines := strings.Split(in, "\n")
+	for _, line := range lines {
+		for {
+			if len(line) <= width {
+				if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil {
+					return err
+				}
+				break
+			}
+
+			// Find the last space we can break at.
+			n := strings.LastIndexByte(line[:width+1], ' ')
+			if n < 0 {
+				// The next word is too long. Wrap as soon as that word ends.
+				n = strings.IndexByte(line[width+1:], ' ')
+				if n < 0 {
+					// This was the last word.
+					if _, err := fmt.Fprintf(w, "# %s\n", line); err != nil {
+						return nil
+					}
+					break
+				}
+				n += width + 1
+			}
+			if _, err := fmt.Fprintf(w, "# %s\n", line[:n]); err != nil {
+				return err
+			}
+			line = line[n+1:] // Ignore the space.
+		}
+	}
+	return nil
+}
+
+func isSupportedCurve(curve string) bool {
+	switch curve {
+	case "brainpoolP224r1", "brainpoolP224t1", "brainpoolP256r1", "brainpoolP256t1", "brainpoolP320r1", "brainpoolP320t1", "brainpoolP384r1", "brainpoolP384t1", "brainpoolP512r1", "brainpoolP512t1", "secp256k1":
+		return false
+	case "edwards25519", "curve25519", "secp224r1", "secp256r1", "secp384r1", "secp521r1":
+		return true
+	default:
+		panic("Unknown curve: " + curve)
+	}
+}
+
+func convertWycheproof(jsonPath, txtPath string) error {
+	jsonData, err := ioutil.ReadFile(jsonPath)
+	if err != nil {
+		return err
+	}
+
+	var w wycheproofTest
+	if err := json.Unmarshal(jsonData, &w); err != nil {
+		return err
+	}
+
+	f, err := os.OpenFile(txtPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0666)
+	if err != nil {
+		return err
+	}
+	defer f.Close()
+	if _, err := fmt.Fprintf(f, `# Imported from Wycheproof's %s.
+# This file is generated by convert_wycheproof.go. Do not edit by hand.
+#
+# Algorithm: %s
+# Generator version: %s
+
+`, jsonPath, w.Algorithm, w.GeneratorVersion); err != nil {
+		return err
+	}
+
+	for _, group := range w.TestGroups {
+		// Skip tests with unsupported curves. We filter these out at
+		// conversion time to avoid unnecessarily inflating
+		// crypto_test_data.cc.
+		if curve, ok := group["curve"]; ok && !isSupportedCurve(curve.(string)) {
+			continue
+		}
+		if keyI, ok := group["key"]; ok {
+			if key, ok := keyI.(map[string]interface{}); ok {
+				if curve, ok := key["curve"]; ok && !isSupportedCurve(curve.(string)) {
+					continue
+				}
+			}
+		}
+
+		for _, k := range sortedKeys(group) {
+			// Wycheproof files always include both keyPem and
+			// keyDer. Skip keyPem as they contain newlines. We
+			// process keyDer more easily.
+			if k == "type" || k == "tests" || k == "keyPem" {
+				continue
+			}
+			if err := printAttribute(f, k, group[k], true); err != nil {
+				return err
+			}
+		}
+		fmt.Fprintf(f, "\n")
+		tests := group["tests"].([]interface{})
+		for _, testI := range tests {
+			test := testI.(map[string]interface{})
+			// Skip tests with unsupported curves.
+			if curve, ok := test["curve"]; ok && !isSupportedCurve(curve.(string)) {
+				continue
+			}
+			if comment, ok := test["comment"]; ok {
+				if err := printComment(f, comment.(string)); err != nil {
+					return err
+				}
+			}
+			for _, k := range sortedKeys(test) {
+				if k == "comment" || k == "flags" || k == "tcId" {
+					continue
+				}
+				if err := printAttribute(f, k, test[k], false); err != nil {
+					return err
+				}
+			}
+			if flags, ok := test["flags"]; ok {
+				for _, flag := range flags.([]interface{}) {
+					if note, ok := w.Notes[flag.(string)]; ok {
+						if err := printComment(f, note); err != nil {
+							return err
+						}
+					}
+				}
+			}
+			if _, err := fmt.Fprintf(f, "\n"); err != nil {
+				return err
+			}
+		}
+	}
+	return nil
+}
+
+func main() {
+	jsonPaths := []string{
+		"x25519_test.json",
+
+		// TODO(davidben): The following tests still need test drivers.
+		// "aes_cbc_pkcs5_test.json",
+		// "aes_gcm_siv_test.json",
+		// "aes_gcm_test.json",
+		// "chacha20_poly1305_test.json",
+		// "dsa_test.json",
+		// "ecdh_test.json",
+		// "ecdsa_secp224r1_sha224_test.json",
+		// "ecdsa_secp224r1_sha256_test.json",
+		// "ecdsa_secp256r1_sha256_test.json",
+		// "ecdsa_secp384r1_sha384_test.json",
+		// "ecdsa_secp384r1_sha512_test.json",
+		// "ecdsa_secp521r1_sha512_test.json",
+		// "eddsa_test.json",
+		// "rsa_signature_test.json",
+	}
+	for _, jsonPath := range jsonPaths {
+		if !strings.HasSuffix(jsonPath, ".json") {
+			panic(jsonPath)
+		}
+		txtPath := jsonPath[:len(jsonPath)-len(".json")] + ".txt"
+		if err := convertWycheproof(jsonPath, txtPath); err != nil {
+			fmt.Fprintf(os.Stderr, "Error converting %s: %s\n", jsonPath, err)
+			os.Exit(1)
+		}
+	}
+}
diff --git a/third_party/wycheproof/x25519_test.txt b/third_party/wycheproof/x25519_test.txt
new file mode 100644
index 0000000..d474128
--- /dev/null
+++ b/third_party/wycheproof/x25519_test.txt
@@ -0,0 +1,532 @@
+# Imported from Wycheproof's x25519_test.json.
+# This file is generated by convert_wycheproof.go. Do not edit by hand.
+#
+# Algorithm: X25519
+# Generator version: 0.4
+
+[curve = curve25519]
+
+# normal case
+curve = curve25519
+private = 4852834d9d6b77dadeabaaf2e11dca66d19fe74993a7bec36c6e16a0983feaba
+public = 9c647d9ae589b9f58fdc3ca4947efbc915c4b2e08e744a0edf469dac59c8f85a
+result = valid
+shared = 87b7f212b627f7a54ca5e0bcdaddd5389d9de6156cdbcf8ebe14ffbcfb436551
+
+# public key on twist
+curve = curve25519
+private = 588c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211998a8
+public = 63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733
+result = acceptable
+shared = b1a707519495ffffb298ff941716b06dfab87cf8d91123fe2be9a233dda22212
+# Public keys are either points on curve25519 or points on its twist.
+# Implementations may either reject such keys or compute X25519 using the twist.
+# If a point multiplication is performed then it is important that the result is
+# correct, since otherwise attacks with invalid keys are possible.
+
+# public key on twist
+curve = curve25519
+private = b05bfd32e55325d9fd648cb302848039000b390e44d521e58aab3b29a6960ba8
+public = 0f83c36fded9d32fadf4efa3ae93a90bb5cfa66893bc412c43fa7287dbb99779
+result = acceptable
+shared = 67dd4a6e165533534c0e3f172e4ab8576bca923a5f07b2c069b4c310ff2e935b
+# Public keys are either points on curve25519 or points on its twist.
+# Implementations may either reject such keys or compute X25519 using the twist.
+# If a point multiplication is performed then it is important that the result is
+# correct, since otherwise attacks with invalid keys are possible.
+
+# public key on twist
+curve = curve25519
+private = 70e34bcbe1f47fbc0fddfd7c1e1aa53d57bfe0f66d243067b424bb6210bed19c
+public = 0b8211a2b6049097f6871c6c052d3c5fc1ba17da9e32ae458403b05bb283092a
+result = acceptable
+shared = 4a0638cfaa9ef1933b47f8939296a6b25be541ef7f70e844c0bcc00b134de64a
+# Public keys are either points on curve25519 or points on its twist.
+# Implementations may either reject such keys or compute X25519 using the twist.
+# If a point multiplication is performed then it is important that the result is
+# correct, since otherwise attacks with invalid keys are possible.
+
+# public key on twist
+curve = curve25519
+private = 68c1f3a653a4cdb1d37bba94738f8b957a57beb24d646e994dc29a276aad458d
+public = 343ac20a3b9c6a27b1008176509ad30735856ec1c8d8fcae13912d08d152f46c
+result = acceptable
+shared = 399491fce8dfab73b4f9f611de8ea0b27b28f85994250b0f475d585d042ac207
+# Public keys are either points on curve25519 or points on its twist.
+# Implementations may either reject such keys or compute X25519 using the twist.
+# If a point multiplication is performed then it is important that the result is
+# correct, since otherwise attacks with invalid keys are possible.
+
+# public key on twist
+curve = curve25519
+private = d877b26d06dff9d9f7fd4c5b3769f8cdd5b30516a5ab806be324ff3eb69ea0b2
+public = fa695fc7be8d1be5bf704898f388c452bafdd3b8eae805f8681a8d15c2d4e142
+result = acceptable
+shared = 2c4fe11d490a53861776b13b4354abd4cf5a97699db6e6c68c1626d07662f758
+# Public keys are either points on curve25519 or points on its twist.
+# Implementations may either reject such keys or compute X25519 using the twist.
+# If a point multiplication is performed then it is important that the result is
+# correct, since otherwise attacks with invalid keys are possible.
+
+# public key = 0
+curve = curve25519
+private = 207494038f2bb811d47805bcdf04a2ac585ada7f2f23389bfd4658f9ddd4debc
+public = 0000000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key = 1
+curve = curve25519
+private = 202e8972b61c7e61930eb9450b5070eae1c670475685541f0476217e4818cfab
+public = 0100000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# edge case on twist
+curve = curve25519
+private = 38dde9f3e7b799045f9ac3793d4a9277dadeadc41bec0290f81f744f73775f84
+public = 0200000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+shared = 9a2cfe84ff9c4a9739625cae4a3b82a906877a441946f8d7b3d795fe8f5d1639
+
+# edge case on twist
+curve = curve25519
+private = 9857a914e3c29036fd9a442ba526b5cdcdf28216153e636c10677acab6bd6aa5
+public = 0300000000000000000000000000000000000000000000000000000000000000
+result = acceptable
+shared = 4da4e0aa072c232ee2f0fa4e519ae50b52c1edd08a534d4ef346c2e106d21d60
+
+# edge case on twist
+curve = curve25519
+private = 48e2130d723305ed05e6e5894d398a5e33367a8c6aac8fcdf0a88e4b42820db7
+public = ffffff030000f8ffff1f0000c0ffffff000000feffff070000f0ffff3f000000
+result = acceptable
+shared = 9ed10c53747f647f82f45125d3de15a1e6b824496ab40410ffcc3cfe95760f3b
+
+# edge case on twist
+curve = curve25519
+private = 28f41011691851b3a62b641553b30d0dfddcb8fffcf53700a7be2f6a872e9fb0
+public = 000000fcffff070000e0ffff3f000000ffffff010000f8ffff0f0000c0ffff7f
+result = acceptable
+shared = cf72b4aa6aa1c9f894f4165b86109aa468517648e1f0cc70e1ab08460176506b
+
+# edge case on twist
+curve = curve25519
+private = 18a93b6499b9f6b3225ca02fef410e0adec23532321d2d8ef1a6d602a8c65b83
+public = 00000000ffffffff00000000ffffffff00000000ffffffff00000000ffffff7f
+result = acceptable
+shared = 5d50b62836bb69579410386cf7bb811c14bf85b1c7b17e5924c7ffea91ef9e12
+
+# edge case on twist
+curve = curve25519
+private = c01d1305a1338a1fcac2ba7e2e032b427e0b04903165aca957d8d0553d8717b0
+public = eaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = 19230eb148d5d67c3c22ab1daeff80a57eae4265ce2872657b2c8099fc698e50
+
+# edge case for public key
+curve = curve25519
+private = 386f7f16c50731d64f82e6a170b142a4e34f31fd7768fcb8902925e7d1e21abe
+public = 0400000000000000000000000000000000000000000000000000000000000000
+result = valid
+shared = 0fcab5d842a078d7a71fc59b57bfb4ca0be6873b49dcdb9f44e14ae8fbdfa542
+
+# edge case for public key
+curve = curve25519
+private = e023a289bd5e90fa2804ddc019a05ef3e79d434bb6ea2f522ecb643a75296e95
+public = ffffffff00000000ffffffff00000000ffffffff00000000ffffffff00000000
+result = valid
+shared = 54ce8f2275c077e3b1306a3939c5e03eef6bbb88060544758d9fef59b0bc3e4f
+
+# edge case for public key
+curve = curve25519
+private = 68f010d62ee8d926053a361c3a75c6ea4ebdc8606ab285003a6f8f4076b01e83
+public = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03
+result = valid
+shared = f136775c5beb0af8110af10b20372332043cab752419678775a223df57c9d30d
+
+# edge case for public key
+curve = curve25519
+private = 58ebcb35b0f8845caf1ec630f96576b62c4b7b6c36b29deb2cb0084651755c96
+public = fffffffbfffffbffffdfffffdffffffffefffffefffff7fffff7ffffbfffff3f
+result = valid
+shared = bf9affd06b844085586460962ef2146ff3d4533d9444aab006eb88cc3054407d
+
+# edge case for public key
+curve = curve25519
+private = 188c4bc5b9c44b38bb658b9b2ae82d5b01015e093184b17cb7863503a783e1bb
+public = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f
+result = valid
+shared = d480de04f699cb3be0684a9cc2e31281ea0bc5a9dcc157d3d20158d46ca5246d
+
+# edge case for public key
+curve = curve25519
+private = e06c11bb2e13ce3dc7673f67f5482242909423a9ae95ee986a988d98faee23a2
+public = fffffffffeffff7ffffffffffeffff7ffffffffffeffff7ffffffffffeffff7f
+result = valid
+shared = 4c4401cce6b51e4cb18f2790246c9bf914db667750a1cb89069092af07292276
+
+# edge case for public key
+curve = curve25519
+private = c0658c46dde18129293877535b1162b6f9f5414a23cf4d2cbc140a4d99da2b8f
+public = ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = valid
+shared = 578ba8cc2dbdc575afcf9df2b3ee6189f5337d6854c79b4ce165ea12293b3a0f
+
+# public key with low order
+curve = curve25519
+private = 10255c9230a97a30a458ca284a629669293a31890cda9d147febc7d1e22d6bb1
+public = e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 78f1e8edf14481b389448dac8f59c70b038e7cf92ef2c7eff57a72466e115296
+public = 5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = a0a05a3e8f9f44204d5f8059a94ac7dfc39a49ac016dd743dbfa43c5d671fd88
+public = ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = d0dbb3ed1906663f15420af31f4eaf6509d9a9949723500605ad7c1c6e7450a9
+public = edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = c0b1d0eb22b244fe3291140072cdd9d989b5f0ecd96c100feb5bca241c1d9f8f
+public = eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 480bf45f594942a8bc0f3353c6e8b8853d77f351f1c2ca6c2d1abf8a00b4229c
+public = 0000000000000000000000000000000000000000000000000000000000000080
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 30f993fcf8514fc89bd8db14cd43ba0d4b2530e73c4276a05e1b145d420cedb4
+public = 0100000000000000000000000000000000000000000000000000000000000080
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = c04974b758380e2a5b5df6eb09bb2f6b3434f982722a8e676d3da251d1b3de83
+public = e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b880
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 502a31373db32446842fe5add3e024022ea54f274182afc3d9f1bb3d39534eb5
+public = 5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f11d7
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 90fa6417b0e37030fd6e43eff2abaef14c6793117a039cf621318ba90f4e98be
+public = ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 78ad3f26027f1c9fdd975a1613b947779bad2cf2b741ade01840885a30bb979c
+public = edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key with low order
+curve = curve25519
+private = 98e23de7b1e0926ed9c87e7b14baf55f497a1d7096f93977680e44dc1c7b7b8b
+public = eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000000
+
+# public key >= p
+curve = curve25519
+private = f01e48dafac9d7bcf589cbc382c878d18bda3550589ffb5d50b523bebe329dae
+public = efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = bd36a0790eb883098c988b21786773de0b3a4df162282cf110de18dd484ce74b
+
+# public key >= p
+curve = curve25519
+private = 288796bc5aff4b81a37501757bc0753a3c21964790d38699308debc17a6eaf8d
+public = f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = b4e0dd76da7b071728b61f856771aa356e57eda78a5b1655cc3820fb5f854c5c
+
+# public key >= p
+curve = curve25519
+private = 98df845f6651bf1138221f119041f72b6dbc3c4ace7143d99fd55ad867480da8
+public = f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = 6fdf6c37611dbd5304dc0f2eb7c9517eb3c50e12fd050ac6dec27071d4bfc034
+
+# public key >= p
+curve = curve25519
+private = f09498e46f02f878829e78b803d316a2ed695d0498a08abdf8276930e24edcb0
+public = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+result = acceptable
+shared = 4c8fc4b1c6ab88fb21f18f6d4c810240d4e94651ba44f7a2c863cec7dc56602d
+
+# public key >= p
+curve = curve25519
+private = 1813c10a5c7f21f96e17f288c0cc37607c04c5f5aea2db134f9e2ffc66bd9db8
+public = 0200000000000000000000000000000000000000000000000000000000000080
+result = acceptable
+shared = 1cd0b28267dc541c642d6d7dca44a8b38a63736eef5c4e6501ffbbb1780c033c
+
+# public key >= p
+curve = curve25519
+private = 7857fb808653645a0beb138a64f5f4d733a45ea84c3cda11a9c06f7e7139149e
+public = 0300000000000000000000000000000000000000000000000000000000000080
+result = acceptable
+shared = 8755be01c60a7e825cff3e0e78cb3aa4333861516aa59b1c51a8b2a543dfa822
+
+# public key >= p
+curve = curve25519
+private = e03aa842e2abc56e81e87b8b9f417b2a1e5913c723eed28d752f8d47a59f498f
+public = 0400000000000000000000000000000000000000000000000000000000000080
+result = acceptable
+shared = 54c9a1ed95e546d27822a360931dda60a1df049da6f904253c0612bbdc087476
+
+# public key >= p
+curve = curve25519
+private = f8f707b7999b18cb0d6b96124f2045972ca274bfc154ad0c87038c24c6d0d4b2
+public = daffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = cc1f40d743cdc2230e1043daba8b75e810f1fbab7f255269bd9ebb29e6bf494f
+
+# public key >= p
+curve = curve25519
+private = a034f684fa631e1a348118c1ce4c98231f2d9eec9ba5365b4a05d69a785b0796
+public = dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 54998ee43a5b007bf499f078e736524400a8b5c7e9b9b43771748c7cdf880412
+
+# public key >= p
+curve = curve25519
+private = 30b6c6a0f2ffa680768f992ba89e152d5bc9893d38c9119be4f767bfab6e0ca5
+public = dcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = ead9b38efdd723637934e55ab717a7ae09eb86a21dc36a3feeb88b759e391e09
+
+# public key >= p
+curve = curve25519
+private = 901b9dcf881e01e027575035d40b43bdc1c5242e030847495b0c7286469b6591
+public = eaffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 602ff40789b54b41805915fe2a6221f07a50ffc2c3fc94cf61f13d7904e88e0e
+
+# public key >= p
+curve = curve25519
+private = 8046677c28fd82c9a1bdb71a1a1a34faba1225e2507fe3f54d10bd5b0d865f8e
+public = ebffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = e00ae8b143471247ba24f12c885536c3cb981b58e1e56b2baf35c12ae1f79c26
+
+# public key >= p
+curve = curve25519
+private = 602f7e2f68a846b82cc269b1d48e939886ae54fd636c1fe074d710127d472491
+public = efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 98cb9b50dd3fc2b0d4f2d2bf7c5cfdd10c8fcd31fc40af1ad44f47c131376362
+
+# public key >= p
+curve = curve25519
+private = 60887b3dc72443026ebedbbbb70665f42b87add1440e7768fbd7e8e2ce5f639d
+public = f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 38d6304c4a7e6d9f7959334fb5245bd2c754525d4c91db950206926234c1f633
+
+# public key >= p
+curve = curve25519
+private = 78d31dfa854497d72d8def8a1b7fb006cec2d8c4924647c93814ae56faeda495
+public = f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 786cd54996f014a5a031ec14db812ed08355061fdb5de680a800ac521f318e23
+
+# public key >= p
+curve = curve25519
+private = c04c5baefa8302ddded6a4bb957761b4eb97aefa4fc3b8043085f96a5659b3a5
+public = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
+result = acceptable
+shared = 29ae8bc73e9b10a08b4f681c43c3e0ac1a171d31b38f1a48efba29ae639ea134
+
+# RFC 7748
+curve = curve25519
+private = a046e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449a44
+public = e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c
+result = valid
+shared = c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552
+
+# RFC 7748
+curve = curve25519
+private = 4866e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba4d
+public = e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a413
+result = valid
+shared = 95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 0ab4e76380d84dde4f6833c58f2a9fb8f83bb0169b172be4b6e0592887741a36
+result = acceptable
+shared = 0200000000000000000000000000000000000000000000000000000000000000
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 89e10d5701b4337d2d032181538b1064bd4084401ceca1fd12663a1959388000
+result = valid
+shared = 0900000000000000000000000000000000000000000000000000000000000000
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 2b55d3aa4a8f80c8c0b2ae5f933e85af49beac36c2fa7394bab76c8933f8f81d
+result = valid
+shared = 1000000000000000000000000000000000000000000000000000000000000000
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 63e5b1fe9601fe84385d8866b0421262f78fbfa5aff9585e626679b18547d959
+result = acceptable
+shared = feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = e428f3dac17809f827a522ce32355058d07369364aa78902ee10139b9f9dd653
+result = valid
+shared = fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = b3b50e3ed3a407b95de942ef74575b5ab8a10c09ee103544d60bdfed8138ab2b
+result = acceptable
+shared = f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 213fffe93d5ea8cd242e462844029922c43c77c9e3e42f562f485d24c501a20b
+result = valid
+shared = f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff3f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 91b232a178b3cd530932441e6139418f72172292f1da4c1834fc5ebfefb51e3f
+result = valid
+shared = ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 045c6e11c5d332556c7822fe94ebf89b56a3878dc27ca079103058849fabcb4f
+result = acceptable
+shared = e5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 1ca2190b71163539063c35773bda0c9c928e9136f0620aeb093f099197b7f74e
+result = acceptable
+shared = e3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = f76e9010ac33c5043b2d3b76a842171000c4916222e9e85897a0aec7f6350b3c
+result = valid
+shared = ddffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = bb72688d8f8aa7a39cd6060cd5c8093cdec6fe341937c3886a99346cd07faa55
+result = acceptable
+shared = dbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 88fddea193391c6a5933ef9b71901549447205aae9da928a6b91a352ba10f41f
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000000002
+
+# edge case for shared secret
+curve = curve25519
+private = a0a4f130b98a5be4b1cedb7cb85584a3520e142d474dc9ccb909a073a976bf63
+public = 303b392f153116cad9cc682a00ccc44c95ff0d3bbe568beb6c4e739bafdc2c68
+result = acceptable
+shared = 0000000000000000000000000000000000000000000000000000000000008000
+
+# checking for overflow
+curve = curve25519
+private = c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2
+public = fd300aeb40e1fa582518412b49b208a7842b1e1f056a040178ea4141534f652d
+result = valid
+shared = b734105dc257585d73b566ccb76f062795ccbec89128e52b02f3e59639f13c46
+
+# checking for overflow
+curve = curve25519
+private = c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2
+public = c8ef79b514d7682677bc7931e06ee5c27c9b392b4ae9484473f554e6678ecc2e
+result = valid
+shared = 647a46b6fc3f40d62141ee3cee706b4d7a9271593a7b143e8e2e2279883e4550
+
+# checking for overflow
+curve = curve25519
+private = c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2
+public = 64aeac2504144861532b7bbcb6c87d67dd4c1f07ebc2e06effb95aecc6170b2c
+result = valid
+shared = 4ff03d5fb43cd8657a3cf37c138cadcecce509e4eba089d0ef40b4e4fb946155
+
+# checking for overflow
+curve = curve25519
+private = c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2
+public = bf68e35e9bdb7eee1b50570221860f5dcdad8acbab031b14974cc49013c49831
+result = valid
+shared = 21cee52efdbc812e1d021a4af1e1d8bc4db3c400e4d2a2c56a3926db4d99c65b
+
+# checking for overflow
+curve = curve25519
+private = c81724704000b26d31703cc97e3a378d56fad8219361c88cca8bd7c5719b12b2
+public = 5347c491331a64b43ddc683034e677f53dc32b52a52a577c15a83bf298e99f19
+result = valid
+shared = 18cb89e4e20c0c2bd324305245266c9327690bbe79acb88f5b8fb3f74eca3e52
+
+# private key == -1 (mod order)
+curve = curve25519
+private = a023cdd083ef5bb82f10d62e59e15a6800000000000000000000000000000050
+public = 258e04523b8d253ee65719fc6906c657192d80717edc828fa0af21686e2faa75
+result = valid
+shared = 258e04523b8d253ee65719fc6906c657192d80717edc828fa0af21686e2faa75
+
+# private key == 1 (mod order) on twist
+curve = curve25519
+private = 58083dd261ad91eff952322ec824c682ffffffffffffffffffffffffffffff5f
+public = 2eae5ec3dd494e9f2d37d258f873a8e6e9d0dbd1e383ef64d98bb91b3e0be035
+result = acceptable
+shared = 2eae5ec3dd494e9f2d37d258f873a8e6e9d0dbd1e383ef64d98bb91b3e0be035
+