summaryrefslogtreecommitdiff
path: root/external_tests
diff options
context:
space:
mode:
authorEKR <ekr@rtfm.com>2016-08-06 14:24:47 -0700
committerEKR <ekr@rtfm.com>2016-08-06 14:24:47 -0700
commitdc74881bfdcad32466ebb2bda5cc696e5c102532 (patch)
tree594b8a1733020a529b54b0da0cc7f9c764f115c3 /external_tests
parent81d9b695925616090a40a3f62749e1f75007da9d (diff)
downloadnss-hg-dc74881bfdcad32466ebb2bda5cc696e5c102532.tar.gz
Bug 1295054 - Add support for Google's BoGo test harness. r=mt
This patch lets you run the test harness by hand. Next step is to enable it in CI.
Diffstat (limited to 'external_tests')
-rw-r--r--external_tests/manifest.mn1
-rw-r--r--external_tests/nss_bogo_shim/Makefile52
-rw-r--r--external_tests/nss_bogo_shim/config.cc58
-rw-r--r--external_tests/nss_bogo_shim/config.h88
-rw-r--r--external_tests/nss_bogo_shim/config.json38
-rw-r--r--external_tests/nss_bogo_shim/manifest.mn20
-rw-r--r--external_tests/nss_bogo_shim/nss_bogo_shim.cc314
-rw-r--r--external_tests/nss_bogo_shim/nsskeys.cc84
-rw-r--r--external_tests/nss_bogo_shim/nsskeys.h20
-rw-r--r--external_tests/ssl_gtest/libssl_internals.c1
10 files changed, 676 insertions, 0 deletions
diff --git a/external_tests/manifest.mn b/external_tests/manifest.mn
index 7075bac55..149e24b26 100644
--- a/external_tests/manifest.mn
+++ b/external_tests/manifest.mn
@@ -12,4 +12,5 @@ DIRS = \
util_gtest \
pk11_gtest \
ssl_gtest \
+ nss_bogo_shim \
$(NULL)
diff --git a/external_tests/nss_bogo_shim/Makefile b/external_tests/nss_bogo_shim/Makefile
new file mode 100644
index 000000000..fd6426d89
--- /dev/null
+++ b/external_tests/nss_bogo_shim/Makefile
@@ -0,0 +1,52 @@
+#! gmake
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#######################################################################
+# (1) Include initial platform-independent assignments (MANDATORY). #
+#######################################################################
+
+include manifest.mn
+
+#######################################################################
+# (2) Include "global" configuration information. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/config.mk
+
+#######################################################################
+# (3) Include "component" configuration information. (OPTIONAL) #
+#######################################################################
+
+CXXFLAGS += -std=c++0x
+
+#######################################################################
+# (4) Include "local" platform-dependent assignments (OPTIONAL). #
+#######################################################################
+
+include ../common/gtest.mk
+
+CFLAGS += -I$(CORE_DEPTH)/lib/ssl
+
+ifdef NSS_SSL_ENABLE_ZLIB
+include $(CORE_DEPTH)/coreconf/zlib.mk
+endif
+
+#######################################################################
+# (5) Execute "global" rules. (OPTIONAL) #
+#######################################################################
+
+include $(CORE_DEPTH)/coreconf/rules.mk
+
+#######################################################################
+# (6) Execute "component" rules. (OPTIONAL) #
+#######################################################################
+
+
+#######################################################################
+# (7) Execute "local" rules. (OPTIONAL). #
+#######################################################################
+
+
diff --git a/external_tests/nss_bogo_shim/config.cc b/external_tests/nss_bogo_shim/config.cc
new file mode 100644
index 000000000..2e6f7f775
--- /dev/null
+++ b/external_tests/nss_bogo_shim/config.cc
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "config.h"
+
+#include <cstdlib>
+#include <queue>
+#include <string>
+
+bool ConfigEntryBase::ParseInternal(std::queue<const char *> *args,
+ std::string *out) {
+ if (args->empty()) return false;
+ *out = args->front();
+ args->pop();
+ return true;
+}
+
+bool ConfigEntryBase::ParseInternal(std::queue<const char *> *args, int *out) {
+ if (args->empty()) return false;
+
+ char *endptr;
+ *out = strtol(args->front(), &endptr, 10);
+ args->pop();
+
+ return !*endptr;
+}
+
+bool ConfigEntryBase::ParseInternal(std::queue<const char *> *args, bool *out) {
+ *out = true;
+ return true;
+}
+
+std::string Config::XformFlag(const std::string &arg) {
+ if (arg.empty()) return "";
+
+ if (arg[0] != '-') return "";
+
+ return arg.substr(1);
+}
+
+Config::Status Config::ParseArgs(int argc, char **argv) {
+ std::queue<const char *> args;
+ for (int i = 1; i < argc; ++i) {
+ args.push(argv[i]);
+ }
+ while (!args.empty()) {
+ auto e = entries_.find(XformFlag(args.front()));
+ args.pop();
+ if (e == entries_.end()) {
+ return kUnknownFlag;
+ }
+ if (!e->second->Parse(&args)) return kMalformedArgument;
+ }
+
+ return kOK;
+}
diff --git a/external_tests/nss_bogo_shim/config.h b/external_tests/nss_bogo_shim/config.h
new file mode 100644
index 000000000..5c9a76b22
--- /dev/null
+++ b/external_tests/nss_bogo_shim/config.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Generic command line flags system for NSS BoGo shim. This class
+// could actually in principle handle other programs. The flags are
+// defined in the consumer code.
+
+#ifndef config_h_
+#define config_h_
+
+#include <cassert>
+
+#include <iostream>
+#include <map>
+#include <queue>
+#include <string>
+
+// Abstract base class for a given config flag.
+class ConfigEntryBase {
+ public:
+ ConfigEntryBase(const std::string& name, const std::string& type)
+ : name_(name), type_(type) {}
+
+ const std::string& type() const { return type_; }
+ virtual bool Parse(std::queue<const char*>* args) = 0;
+
+ protected:
+ bool ParseInternal(std::queue<const char*>* args, std::string* out);
+ bool ParseInternal(std::queue<const char*>* args, int* out);
+ bool ParseInternal(std::queue<const char*>* args, bool* out);
+
+ const std::string name_;
+ const std::string type_;
+};
+
+// Template specializations for the concrete flag types.
+template <typename T>
+class ConfigEntry : public ConfigEntryBase {
+ public:
+ ConfigEntry(const std::string& name, T init)
+ : ConfigEntryBase(name, typeid(T).name()), value_(init) {}
+ T get() const { return value_; }
+
+ bool Parse(std::queue<const char*>* args) {
+ return ParseInternal(args, &value_);
+ }
+
+ private:
+ T value_;
+};
+
+// The overall configuration (I.e., the total set of flags).
+class Config {
+ public:
+ enum Status { kOK, kUnknownFlag, kMalformedArgument, kMissingValue };
+
+ Config() : entries_() {}
+
+ template <typename T>
+ void AddEntry(const std::string& name, T init) {
+ entries_[name] = new ConfigEntry<T>(name, init);
+ }
+
+ Status ParseArgs(int argc, char** argv);
+
+ template <typename T>
+ T get(const std::string& key) const {
+ auto e = entry(key);
+ assert(e->type() == typeid(T).name());
+ return static_cast<const ConfigEntry<T>*>(e)->get();
+ }
+
+ private:
+ static std::string XformFlag(const std::string& arg);
+
+ std::map<std::string, ConfigEntryBase*> entries_;
+
+ const ConfigEntryBase* entry(const std::string& key) const {
+ auto e = entries_.find(key);
+ if (e == entries_.end()) return nullptr;
+ return e->second;
+ }
+};
+
+#endif // config_h_
diff --git a/external_tests/nss_bogo_shim/config.json b/external_tests/nss_bogo_shim/config.json
new file mode 100644
index 000000000..59a639ec4
--- /dev/null
+++ b/external_tests/nss_bogo_shim/config.json
@@ -0,0 +1,38 @@
+{
+ "DisabledTests": {
+ "*TLS13*":"TLS 1.3: Draft version mismatch",
+ "*HelloRetryRequest*":"TLS 1.3: Draft version mismatch",
+ "SecondClientHelloWrongCurve":"TLS 1.3: Draft version mismatch",
+ "KeyUpdate":"TLS 1.3: Draft version mismatch",
+ "*KeyShare*":"TLS 1.3: Draft version mismatch",
+ "FragmentedClientVersion":"TLS 1.3: Draft version mismatch",
+ "*VersionTolerance":"TLS 1.3: Draft version mismatch",
+ "Downgrade-TLS12-*":"TLS 1.3: Draft version mismatch",
+ "ClientAuth-SHA1-Fallback":"TLS 1.3: Draft version mismatch",
+ "NoSupportedCurves":"TLS 1.3: Draft version mismatch",
+ "SendEmptyRecords-Pass":"TLS 1.3: Draft version mismatch",
+ "*SSL3*":"NSS disables SSLv3",
+ "*SSLv3*":"NSS disables SSLv3",
+ "*AES256*":"Inconsistent support for AES256",
+ "*AES128-SHA256*":"No support for Suite B ciphers",
+ "*CHACHA20-POLY1305-OLD*":"Old ChaCha/Poly",
+ "DuplicateExtension*":"NSS sends unexpected_extension alert",
+ "WeakDH":"NSS supports 768-bit DH",
+ "SillyDH":"NSS supports 4097-bit DH",
+ "SendWarningAlerts":"This appears to be Boring-specific",
+ "V2ClientHello-WarningAlertPrefix":"Bug 1292893",
+ "TLS12-AES128-GCM-client":"Bug 1292895",
+ "*TLS12-AES128-GCM-LargeRecord*":"Bug 1292895",
+ "Renegotiate-Client-Forbidden-1":"Bug 1292898",
+ "Renegotiate-Server-Forbidden":"NSS doesn't disable renegotiation by default",
+ "Renegotiate-Client-NoIgnore":"NSS doesn't disable renegotiation by default",
+ "StrayHelloRequest*":"NSS doesn't disable renegotiation by default"
+ },
+ "ErrorMap" : {
+ ":HANDSHAKE_FAILURE_ON_CLIENT_HELLO:":"SSL_ERROR_NO_CYPHER_OVERLAP",
+ ":UNKNOWN_CIPHER_RETURNED:":"SSL_ERROR_NO_CYPHER_OVERLAP",
+ ":OLD_SESSION_CIPHER_NOT_RETURNED:":"SSL_ERROR_NO_CYPHER_OVERLAP",
+ ":NO_SHARED_CIPHER:":"SSL_ERROR_NO_CYPHER_OVERLAP"
+ }
+}
+
diff --git a/external_tests/nss_bogo_shim/manifest.mn b/external_tests/nss_bogo_shim/manifest.mn
new file mode 100644
index 000000000..2d60ddea3
--- /dev/null
+++ b/external_tests/nss_bogo_shim/manifest.mn
@@ -0,0 +1,20 @@
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+CORE_DEPTH = ../..
+DEPTH = ../..
+MODULE = nss
+
+CPPSRCS = \
+ config.cc \
+ nsskeys.cc \
+ nss_bogo_shim.cc \
+ $(NULL)
+
+REQUIRES = nspr nss libdbm
+
+PROGRAM = nss_bogo_shim
+#EXTRA_LIBS = $(DIST)/lib/$(LIB_PREFIX)softokn.$(LIB_SUFFIX)
+
+USE_STATIC_LIBS = 1
diff --git a/external_tests/nss_bogo_shim/nss_bogo_shim.cc b/external_tests/nss_bogo_shim/nss_bogo_shim.cc
new file mode 100644
index 000000000..2f52335d5
--- /dev/null
+++ b/external_tests/nss_bogo_shim/nss_bogo_shim.cc
@@ -0,0 +1,314 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "config.h"
+
+#include <cstdlib>
+#include <iostream>
+#include <memory>
+#include "nspr.h"
+#include "nss.h"
+#include "prio.h"
+#include "prnetdb.h"
+#include "ssl.h"
+#include "sslerr.h"
+#include "sslproto.h"
+
+#include "nsskeys.h"
+
+std::string FormatError(PRErrorCode code) {
+ return std::string(":") + PORT_ErrorToName(code) + ":" + ":" +
+ PORT_ErrorToString(code);
+}
+
+class TestAgent {
+ public:
+ TestAgent(const Config& cfg)
+ : cfg_(cfg),
+ pr_fd_(nullptr),
+ ssl_fd_(nullptr),
+ cert_(nullptr),
+ key_(nullptr) {}
+
+ ~TestAgent() {
+ if (pr_fd_) {
+ PR_Close(pr_fd_);
+ }
+
+ if (ssl_fd_) {
+ PR_Close(ssl_fd_);
+ }
+
+ if (key_) {
+ SECKEY_DestroyPrivateKey(key_);
+ }
+
+ if (cert_) {
+ CERT_DestroyCertificate(cert_);
+ }
+ }
+
+ static std::unique_ptr<TestAgent> Create(const Config& cfg) {
+ std::unique_ptr<TestAgent> agent(new TestAgent(cfg));
+
+ if (!agent->Init()) return nullptr;
+
+ return agent;
+ }
+
+ bool Init() {
+ if (!ConnectTcp()) {
+ return false;
+ }
+
+ if (!SetupKeys()) {
+ std::cerr << "Couldn't set up keys/certs\n";
+ return false;
+ }
+
+ if (!SetupOptions()) {
+ std::cerr << "Couldn't configure socket\n";
+ return false;
+ }
+
+ SECStatus rv = SSL_ResetHandshake(ssl_fd_, cfg_.get<bool>("server"));
+ if (rv != SECSuccess) return false;
+
+ return true;
+ }
+
+ bool ConnectTcp() {
+ PRStatus prv;
+ PRNetAddr addr;
+
+ prv = PR_StringToNetAddr("127.0.0.1", &addr);
+ if (prv != PR_SUCCESS) {
+ return false;
+ }
+ addr.inet.port = PR_htons(cfg_.get<int>("port"));
+
+ pr_fd_ = PR_OpenTCPSocket(addr.raw.family);
+ if (!pr_fd_) return false;
+
+ prv = PR_Connect(pr_fd_, &addr, PR_INTERVAL_NO_TIMEOUT);
+ if (prv != PR_SUCCESS) {
+ return false;
+ }
+
+ ssl_fd_ = SSL_ImportFD(NULL, pr_fd_);
+ if (!ssl_fd_) return false;
+ pr_fd_ = nullptr;
+
+ return true;
+ }
+
+ bool SetupKeys() {
+ SECStatus rv;
+
+ if (cfg_.get<std::string>("key-file") != "") {
+ key_ = ReadPrivateKey(cfg_.get<std::string>("key-file"));
+ if (!key_) exit(89); // Temporary to handle our inability to handle ECDSA
+ }
+ if (cfg_.get<std::string>("cert-file") != "") {
+ cert_ = ReadCertificate(cfg_.get<std::string>("cert-file"));
+ if (!cert_) return false;
+ }
+ if (cfg_.get<bool>("server")) {
+ // Server
+ rv = SSL_ConfigServerCert(ssl_fd_, cert_, key_, nullptr, 0);
+ if (rv != SECSuccess) {
+ std::cerr << "Couldn't configure server cert\n";
+ return false;
+ }
+ rv = SSL_ConfigServerSessionIDCache(1024, 0, 0, ".");
+ if (rv != SECSuccess) {
+ std::cerr << "Couldn't configure session cache\n";
+ return false;
+ }
+ } else {
+ // Client.
+
+ // Needed because server certs are not entirely valid.
+ rv = SSL_AuthCertificateHook(ssl_fd_, AuthCertificateHook, this);
+ if (rv != SECSuccess) return false;
+
+ if (key_ && cert_) {
+ rv = SSL_GetClientAuthDataHook(ssl_fd_, GetClientAuthDataHook, this);
+ if (rv != SECSuccess) return false;
+ }
+ }
+
+ return true;
+ }
+
+ bool SetupOptions() {
+ SECStatus rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_SESSION_TICKETS, PR_TRUE);
+ if (rv != SECSuccess) return false;
+
+ SSLVersionRange vrange = {SSL_LIBRARY_VERSION_TLS_1_0,
+ SSL_LIBRARY_VERSION_TLS_1_3};
+ rv = SSL_VersionRangeSet(ssl_fd_, &vrange);
+ if (rv != SECSuccess) return false;
+
+ rv = SSL_OptionSet(ssl_fd_, SSL_NO_CACHE, false);
+ if (rv != SECSuccess) return false;
+
+ if (!cfg_.get<bool>("server")) {
+ // Needed to make resumption work.
+ rv = SSL_SetURL(ssl_fd_, "server");
+ if (rv != SECSuccess) return false;
+ }
+
+ rv = SSL_OptionSet(ssl_fd_, SSL_ENABLE_EXTENDED_MASTER_SECRET, PR_TRUE);
+ if (rv != SECSuccess) return false;
+
+ if (!EnableNonExportCiphers()) return false;
+
+ return true;
+ }
+
+ bool EnableNonExportCiphers() {
+ for (size_t i = 0; i < SSL_NumImplementedCiphers; ++i) {
+ SSLCipherSuiteInfo csinfo;
+
+ SECStatus rv = SSL_GetCipherSuiteInfo(SSL_ImplementedCiphers[i], &csinfo,
+ sizeof(csinfo));
+ if (rv != SECSuccess) return false;
+ if (!csinfo.isExportable) {
+ rv = SSL_CipherPrefSet(ssl_fd_, SSL_ImplementedCiphers[i], PR_TRUE);
+ if (rv != SECSuccess) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ // Dummy auth certificate hook.
+ static SECStatus AuthCertificateHook(void* arg, PRFileDesc* fd,
+ PRBool checksig, PRBool isServer) {
+ return SECSuccess;
+ }
+
+ static SECStatus GetClientAuthDataHook(void* self, PRFileDesc* fd,
+ CERTDistNames* caNames,
+ CERTCertificate** cert,
+ SECKEYPrivateKey** privKey) {
+ TestAgent* a = static_cast<TestAgent*>(self);
+ *cert = CERT_DupCertificate(a->cert_);
+ *privKey = SECKEY_CopyPrivateKey(a->key_);
+ return SECSuccess;
+ }
+
+ SECStatus Handshake() { return SSL_ForceHandshake(ssl_fd_); }
+
+ // Implement a trivial echo client/server. Read bytes from the other side,
+ // flip all the bits, and send them back.
+ SECStatus ReadWrite() {
+ for (;;) {
+ uint8_t block[512];
+ int32_t rv = PR_Read(ssl_fd_, block, sizeof(block));
+ if (rv < 0) {
+ std::cerr << "Failure reading\n";
+ return SECFailure;
+ }
+ if (rv == 0) return SECSuccess;
+
+ int32_t len = rv;
+ for (int32_t i = 0; i < len; ++i) {
+ block[i] ^= 0xff;
+ }
+
+ rv = PR_Write(ssl_fd_, block, len);
+ if (rv != len) {
+ std::cerr << "Write failure\n";
+ return SECFailure;
+ }
+ }
+ return SECSuccess;
+ }
+
+ SECStatus DoExchange() {
+ SECStatus rv = Handshake();
+ if (rv != SECSuccess) {
+ PRErrorCode err = PR_GetError();
+ std::cerr << "Handshake failed with error=" << err << FormatError(err)
+ << std::endl;
+ return SECFailure;
+ }
+
+ rv = ReadWrite();
+ if (rv != SECSuccess) {
+ PRErrorCode err = PR_GetError();
+ std::cerr << "ReadWrite failed with error=" << FormatError(err)
+ << std::endl;
+ return SECFailure;
+ }
+
+ return SECSuccess;
+ }
+
+ private:
+ const Config& cfg_;
+ PRFileDesc* pr_fd_;
+ PRFileDesc* ssl_fd_;
+ CERTCertificate* cert_;
+ SECKEYPrivateKey* key_;
+};
+
+std::unique_ptr<const Config> ReadConfig(int argc, char** argv) {
+ std::unique_ptr<Config> cfg(new Config());
+
+ cfg->AddEntry<int>("port", 0);
+ cfg->AddEntry<bool>("server", false);
+ cfg->AddEntry<bool>("resume", false);
+ cfg->AddEntry<std::string>("key-file", "");
+ cfg->AddEntry<std::string>("cert-file", "");
+
+ auto rv = cfg->ParseArgs(argc, argv);
+ switch (rv) {
+ case Config::kOK:
+ break;
+ case Config::kUnknownFlag:
+ exit(89);
+ break;
+ default:
+ exit(1);
+ }
+
+ // Needed to change to std::unique_ptr<const Config>
+ return std::move(cfg);
+}
+
+void RunCycle(std::unique_ptr<const Config>& cfg) {
+ std::unique_ptr<TestAgent> agent(TestAgent::Create(*cfg));
+ if (!agent) {
+ exit(1);
+ }
+
+ SECStatus rv = agent->DoExchange();
+ if (rv) {
+ exit(1);
+ }
+}
+
+int main(int argc, char** argv) {
+ std::unique_ptr<const Config> cfg = ReadConfig(argc, argv);
+
+ SECStatus rv = NSS_NoDB_Init(nullptr);
+ if (rv != SECSuccess) return 1;
+ rv = NSS_SetDomesticPolicy();
+ if (rv != SECSuccess) return 1;
+
+ // Run a single test cycle.
+ RunCycle(cfg);
+
+ if (cfg->get<bool>("resume")) {
+ std::cout << "Resuming" << std::endl;
+ RunCycle(cfg);
+ }
+
+ exit(0);
+}
diff --git a/external_tests/nss_bogo_shim/nsskeys.cc b/external_tests/nss_bogo_shim/nsskeys.cc
new file mode 100644
index 000000000..1b5e15bee
--- /dev/null
+++ b/external_tests/nss_bogo_shim/nsskeys.cc
@@ -0,0 +1,84 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsskeys.h"
+
+#include <cstring>
+
+#include <fstream>
+#include <iostream>
+#include <string>
+
+#include "cert.h"
+#include "keyhi.h"
+#include "nspr.h"
+#include "nss.h"
+#include "nssb64.h"
+#include "pk11pub.h"
+
+const std::string kPEMBegin = "-----BEGIN ";
+const std::string kPEMEnd = "-----END ";
+
+// Read a PEM file, base64 decode it, and return the result.
+static bool ReadPEMFile(const std::string& filename, SECItem* item) {
+ std::ifstream in(filename);
+ if (in.bad()) return false;
+
+ char buf[1024];
+ in.getline(buf, sizeof(buf));
+ if (in.bad()) return false;
+
+ if (strncmp(buf, kPEMBegin.c_str(), kPEMBegin.size())) return false;
+
+ std::string value = "";
+ for (;;) {
+ in.getline(buf, sizeof(buf));
+ if (in.bad()) return false;
+
+ if (!strncmp(buf, kPEMEnd.c_str(), kPEMEnd.size())) break;
+
+ value += buf;
+ }
+
+ // Now we have a base64-encoded block.
+ if (!NSSBase64_DecodeBuffer(nullptr, item, value.c_str(), value.size()))
+ return false;
+
+ return true;
+}
+
+SECKEYPrivateKey* ReadPrivateKey(const std::string& file) {
+ SECItem item = {siBuffer, nullptr, 0};
+
+ if (!ReadPEMFile(file, &item)) return nullptr;
+ SECKEYPrivateKey* privkey = NULL;
+ PK11SlotInfo* slot = PK11_GetInternalSlot();
+ SECStatus rv = PK11_ImportDERPrivateKeyInfoAndReturnKey(
+ slot, &item, nullptr, nullptr, PR_FALSE, PR_FALSE,
+ KU_KEY_ENCIPHERMENT | KU_DATA_ENCIPHERMENT | KU_DIGITAL_SIGNATURE,
+ &privkey, nullptr);
+ PK11_FreeSlot(slot);
+ SECITEM_FreeItem(&item, PR_FALSE);
+ if (rv != SECSuccess) {
+ // This is probably due to this being an ECDSA key (Bug 1295121).
+ std::cerr << "Couldn't import key " << PORT_ErrorToString(PORT_GetError())
+ << "\n";
+ return nullptr;
+ }
+
+ return privkey;
+}
+
+CERTCertificate* ReadCertificate(const std::string& file) {
+ SECItem item = {siBuffer, nullptr, 0};
+
+ if (!ReadPEMFile(file, &item)) return nullptr;
+
+ CERTCertificate* cert = CERT_NewTempCertificate(
+ CERT_GetDefaultCertDB(), &item, NULL, PR_FALSE, PR_TRUE);
+ SECITEM_FreeItem(&item, PR_FALSE);
+ return cert;
+}
diff --git a/external_tests/nss_bogo_shim/nsskeys.h b/external_tests/nss_bogo_shim/nsskeys.h
new file mode 100644
index 000000000..45e56c353
--- /dev/null
+++ b/external_tests/nss_bogo_shim/nsskeys.h
@@ -0,0 +1,20 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Utilities to pull in OpenSSL-formatted keys.
+
+#ifndef nsskeys_h_
+#define nsskeys_h_
+
+#include "cert.h"
+#include "keyhi.h"
+
+#include <string>
+
+SECKEYPrivateKey* ReadPrivateKey(const std::string& file);
+CERTCertificate* ReadCertificate(const std::string& file);
+
+#endif
diff --git a/external_tests/ssl_gtest/libssl_internals.c b/external_tests/ssl_gtest/libssl_internals.c
index 725208cce..c9c84cb77 100644
--- a/external_tests/ssl_gtest/libssl_internals.c
+++ b/external_tests/ssl_gtest/libssl_internals.c
@@ -244,3 +244,4 @@ SSLInt_HasCertWithAuthType(PRFileDesc *fd, SSLAuthType authType)
return (PRBool)(!!ssl_FindServerCertByAuthType(ss, authType));
}
+