summaryrefslogtreecommitdiff
path: root/nss/nss-tool
diff options
context:
space:
mode:
Diffstat (limited to 'nss/nss-tool')
-rw-r--r--nss/nss-tool/.clang-format4
-rw-r--r--nss/nss-tool/common/argparse.cc23
-rw-r--r--nss/nss-tool/common/argparse.h30
-rw-r--r--nss/nss-tool/common/tool.h20
-rw-r--r--nss/nss-tool/common/util.cc201
-rw-r--r--nss/nss-tool/common/util.h31
-rw-r--r--nss/nss-tool/db/dbtool.cc497
-rw-r--r--nss/nss-tool/db/dbtool.h28
-rw-r--r--nss/nss-tool/enc/enctool.cc464
-rw-r--r--nss/nss-tool/enc/enctool.h62
-rw-r--r--nss/nss-tool/nss_tool.cc64
-rw-r--r--nss/nss-tool/nss_tool.gyp30
12 files changed, 1454 insertions, 0 deletions
diff --git a/nss/nss-tool/.clang-format b/nss/nss-tool/.clang-format
new file mode 100644
index 0000000..06e3c51
--- /dev/null
+++ b/nss/nss-tool/.clang-format
@@ -0,0 +1,4 @@
+---
+Language: Cpp
+BasedOnStyle: Google
+...
diff --git a/nss/nss-tool/common/argparse.cc b/nss/nss-tool/common/argparse.cc
new file mode 100644
index 0000000..3b7c738
--- /dev/null
+++ b/nss/nss-tool/common/argparse.cc
@@ -0,0 +1,23 @@
+/* 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 "argparse.h"
+
+ArgParser::ArgParser(const std::vector<std::string>& arguments) {
+ for (size_t i = 0; i < arguments.size(); i++) {
+ std::string arg = arguments.at(i);
+ if (arg.find("--") == 0) {
+ // look for an option argument
+ if (i + 1 < arguments.size() && arguments.at(i + 1).find("--") != 0) {
+ programArgs_[arg] = arguments.at(i + 1);
+ i++;
+ } else {
+ programArgs_[arg] = "";
+ }
+ } else {
+ // positional argument (e.g. required argument)
+ positionalArgs_.push_back(arg);
+ }
+ }
+}
diff --git a/nss/nss-tool/common/argparse.h b/nss/nss-tool/common/argparse.h
new file mode 100644
index 0000000..8645d5a
--- /dev/null
+++ b/nss/nss-tool/common/argparse.h
@@ -0,0 +1,30 @@
+/* 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/. */
+
+#ifndef argparse_h__
+#define argparse_h__
+
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+class ArgParser {
+ public:
+ ArgParser(const std::vector<std::string>& arguments);
+
+ bool Has(std::string arg) const { return programArgs_.count(arg) > 0; }
+
+ std::string Get(std::string arg) const { return programArgs_.at(arg); }
+
+ size_t GetPositionalArgumentCount() const { return positionalArgs_.size(); }
+ std::string GetPositionalArgument(size_t pos) const {
+ return positionalArgs_.at(pos);
+ }
+
+ private:
+ std::unordered_map<std::string, std::string> programArgs_;
+ std::vector<std::string> positionalArgs_;
+};
+
+#endif // argparse_h__
diff --git a/nss/nss-tool/common/tool.h b/nss/nss-tool/common/tool.h
new file mode 100644
index 0000000..17ebcac
--- /dev/null
+++ b/nss/nss-tool/common/tool.h
@@ -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/. */
+
+#ifndef tool_h__
+#define tool_h__
+
+#include <string>
+#include <vector>
+
+class Tool {
+ public:
+ virtual bool Run(const std::vector<std::string>& arguments) = 0;
+ virtual ~Tool() {}
+
+ private:
+ virtual void Usage() = 0;
+};
+
+#endif // tool_h__
diff --git a/nss/nss-tool/common/util.cc b/nss/nss-tool/common/util.cc
new file mode 100644
index 0000000..e5af2cb
--- /dev/null
+++ b/nss/nss-tool/common/util.cc
@@ -0,0 +1,201 @@
+/* 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 "util.h"
+
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <prerror.h>
+
+#if defined(__unix__) || defined(__APPLE__)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(WIN32) || defined(_WIN64)
+#include <Windows.h>
+#endif
+
+static std::string GetPassword(const std::string &prompt) {
+ std::cout << prompt << std::endl;
+
+#if defined(__unix__) || defined(__APPLE__)
+ termios oldt;
+ tcgetattr(STDIN_FILENO, &oldt);
+ termios newt = oldt;
+ newt.c_lflag &= ~ECHO;
+ tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+#elif defined(WIN32) || defined(_WIN64)
+ HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+ DWORD mode = 0;
+ GetConsoleMode(hStdin, &mode);
+ SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
+#endif
+
+ std::string pw;
+ std::getline(std::cin, pw);
+
+#if defined(__unix__) || defined(__APPLE__)
+ tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+#elif defined(WIN32) || defined(_WIN64)
+ SetConsoleMode(hStdin, mode);
+#endif
+
+ return pw;
+}
+
+static char *GetModulePassword(PK11SlotInfo *slot, int retry, void *arg) {
+ if (arg == nullptr) {
+ return nullptr;
+ }
+
+ PwData *pwData = reinterpret_cast<PwData *>(arg);
+
+ if (retry > 0) {
+ std::cerr << "Incorrect password/PIN entered." << std::endl;
+ return nullptr;
+ }
+
+ switch (pwData->source) {
+ case PW_NONE:
+ case PW_FROMFILE:
+ std::cerr << "Password input method not supported." << std::endl;
+ return nullptr;
+ case PW_PLAINTEXT:
+ return PL_strdup(pwData->data);
+ default:
+ break;
+ }
+
+ std::cerr << "Password check failed: No password found." << std::endl;
+ return nullptr;
+}
+
+static std::vector<uint8_t> ReadFromIstream(std::istream &is) {
+ std::vector<uint8_t> data;
+ while (is) {
+ char buf[1024];
+ is.read(buf, sizeof(buf));
+ data.insert(data.end(), buf, buf + is.gcount());
+ }
+
+ return data;
+}
+
+static std::string GetNewPasswordFromUser(void) {
+ std::string pw;
+
+ while (true) {
+ pw = GetPassword("Enter new password: ");
+ if (pw == GetPassword("Re-enter password: ")) {
+ break;
+ }
+
+ std::cerr << "Passwords do not match. Try again." << std::endl;
+ }
+
+ return pw;
+}
+
+bool InitSlotPassword(void) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ std::cout << "Enter a password which will be used to encrypt your keys."
+ << std::endl
+ << std::endl;
+ std::string pw = GetNewPasswordFromUser();
+
+ SECStatus rv = PK11_InitPin(slot.get(), nullptr, pw.c_str());
+ if (rv != SECSuccess) {
+ std::cerr << "Init db password failed." << std::endl;
+ return false;
+ }
+
+ return true;
+}
+
+bool ChangeSlotPassword(void) {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ // get old password and authenticate to db
+ PK11_SetPasswordFunc(&GetModulePassword);
+ std::string oldPw = GetPassword("Enter your current password: ");
+ PwData pwData = {PW_PLAINTEXT, const_cast<char *>(oldPw.c_str())};
+ SECStatus rv = PK11_Authenticate(slot.get(), false /*loadCerts*/, &pwData);
+ if (rv != SECSuccess) {
+ std::cerr << "Password incorrect." << std::endl;
+ return false;
+ }
+
+ // get new password
+ std::string newPw = GetNewPasswordFromUser();
+
+ if (PK11_ChangePW(slot.get(), oldPw.c_str(), newPw.c_str()) != SECSuccess) {
+ std::cerr << "Failed to change password." << std::endl;
+ return false;
+ }
+
+ std::cout << "Password changed successfully." << std::endl;
+ return true;
+}
+
+bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot) {
+ if (!PK11_NeedLogin(slot.get())) {
+ return true;
+ }
+
+ PK11_SetPasswordFunc(&GetModulePassword);
+ std::string pw = GetPassword("Enter your password: ");
+ PwData pwData = {PW_PLAINTEXT, const_cast<char *>(pw.c_str())};
+ SECStatus rv = PK11_Authenticate(slot.get(), true /*loadCerts*/, &pwData);
+ if (rv != SECSuccess) {
+ std::cerr << "Could not authenticate to token "
+ << PK11_GetTokenName(slot.get()) << ". Failed with error "
+ << PR_ErrorToName(PR_GetError()) << std::endl;
+ return false;
+ }
+ std::cout << std::endl;
+
+ return true;
+}
+
+std::string StringToHex(const ScopedSECItem &input) {
+ std::stringstream ss;
+ ss << "0x";
+ for (size_t i = 0; i < input->len; i++) {
+ ss << std::hex << std::setfill('0') << std::setw(2)
+ << static_cast<int>(input->data[i]);
+ }
+
+ return ss.str();
+}
+
+std::vector<uint8_t> ReadInputData(std::string dataPath) {
+ std::vector<uint8_t> data;
+ if (dataPath.empty()) {
+ std::cout << "No input file path given, using stdin." << std::endl;
+ data = ReadFromIstream(std::cin);
+ } else {
+ std::ifstream is(dataPath, std::ifstream::binary);
+ if (is.good()) {
+ data = ReadFromIstream(is);
+ } else {
+ std::cerr << "IO Error when opening " << dataPath << std::endl;
+ std::cerr << "Input file does not exist or you don't have permissions."
+ << std::endl;
+ }
+ }
+
+ return data;
+}
diff --git a/nss/nss-tool/common/util.h b/nss/nss-tool/common/util.h
new file mode 100644
index 0000000..bc2e297
--- /dev/null
+++ b/nss/nss-tool/common/util.h
@@ -0,0 +1,31 @@
+/* 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/. */
+
+#ifndef util_h__
+#define util_h__
+
+#include "nspr.h"
+#include "scoped_ptrs.h"
+
+#include <secmodt.h>
+#include <string>
+#include <vector>
+
+#ifndef PORT_Malloc
+#define PORT_Malloc PR_Malloc
+#endif
+
+enum PwDataType { PW_NONE = 0, PW_FROMFILE = 1, PW_PLAINTEXT = 2 };
+typedef struct {
+ PwDataType source;
+ char *data;
+} PwData;
+
+bool InitSlotPassword(void);
+bool ChangeSlotPassword(void);
+bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot);
+std::string StringToHex(const ScopedSECItem &input);
+std::vector<uint8_t> ReadInputData(std::string dataPath);
+
+#endif // util_h__
diff --git a/nss/nss-tool/db/dbtool.cc b/nss/nss-tool/db/dbtool.cc
new file mode 100644
index 0000000..8c369cf
--- /dev/null
+++ b/nss/nss-tool/db/dbtool.cc
@@ -0,0 +1,497 @@
+/* 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 "dbtool.h"
+#include "argparse.h"
+#include "scoped_ptrs.h"
+#include "util.h"
+
+#include <iomanip>
+#include <iostream>
+#include <regex>
+#include <sstream>
+
+#include <cert.h>
+#include <certdb.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <prio.h>
+
+const std::vector<std::string> kCommandArgs(
+ {"--create", "--list-certs", "--import-cert", "--list-keys", "--import-key",
+ "--delete-cert", "--delete-key", "--change-password"});
+
+static bool HasSingleCommandArgument(const ArgParser &parser) {
+ auto pred = [&](const std::string &cmd) { return parser.Has(cmd); };
+ return std::count_if(kCommandArgs.begin(), kCommandArgs.end(), pred) == 1;
+}
+
+static bool HasArgumentRequiringWriteAccess(const ArgParser &parser) {
+ return parser.Has("--create") || parser.Has("--import-cert") ||
+ parser.Has("--import-key") || parser.Has("--delete-cert") ||
+ parser.Has("--delete-key") || parser.Has("--change-password");
+}
+
+static std::string PrintFlags(unsigned int flags) {
+ std::stringstream ss;
+ if ((flags & CERTDB_VALID_CA) && !(flags & CERTDB_TRUSTED_CA) &&
+ !(flags & CERTDB_TRUSTED_CLIENT_CA)) {
+ ss << "c";
+ }
+ if ((flags & CERTDB_TERMINAL_RECORD) && !(flags & CERTDB_TRUSTED)) {
+ ss << "p";
+ }
+ if (flags & CERTDB_TRUSTED_CA) {
+ ss << "C";
+ }
+ if (flags & CERTDB_TRUSTED_CLIENT_CA) {
+ ss << "T";
+ }
+ if (flags & CERTDB_TRUSTED) {
+ ss << "P";
+ }
+ if (flags & CERTDB_USER) {
+ ss << "u";
+ }
+ if (flags & CERTDB_SEND_WARN) {
+ ss << "w";
+ }
+ if (flags & CERTDB_INVISIBLE_CA) {
+ ss << "I";
+ }
+ if (flags & CERTDB_GOVT_APPROVED_CA) {
+ ss << "G";
+ }
+ return ss.str();
+}
+
+static const char *const keyTypeName[] = {"null", "rsa", "dsa", "fortezza",
+ "dh", "kea", "ec"};
+
+void DBTool::Usage() {
+ std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
+ std::cerr << " --create" << std::endl;
+ std::cerr << " --change-password" << std::endl;
+ std::cerr << " --list-certs" << std::endl;
+ std::cerr << " --import-cert [<path>] --name <name> [--trusts <trusts>]"
+ << std::endl;
+ std::cerr << " --list-keys" << std::endl;
+ std::cerr << " --import-key [<path> [-- name <name>]]" << std::endl;
+ std::cerr << " --delete-cert <name>" << std::endl;
+ std::cerr << " --delete-key <name>" << std::endl;
+}
+
+bool DBTool::Run(const std::vector<std::string> &arguments) {
+ ArgParser parser(arguments);
+
+ if (!HasSingleCommandArgument(parser)) {
+ Usage();
+ return false;
+ }
+
+ PRAccessHow how = PR_ACCESS_READ_OK;
+ bool readOnly = true;
+ if (HasArgumentRequiringWriteAccess(parser)) {
+ how = PR_ACCESS_WRITE_OK;
+ readOnly = false;
+ }
+
+ std::string initDir(".");
+ if (parser.Has("--path")) {
+ initDir = parser.Get("--path");
+ }
+ if (PR_Access(initDir.c_str(), how) != PR_SUCCESS) {
+ std::cerr << "Directory '" << initDir
+ << "' does not exist or you don't have permissions!" << std::endl;
+ return false;
+ }
+
+ std::cout << "Using database directory: " << initDir << std::endl
+ << std::endl;
+
+ bool dbFilesExist = PathHasDBFiles(initDir);
+ if (parser.Has("--create") && dbFilesExist) {
+ std::cerr << "Trying to create database files in a directory where they "
+ "already exists. Delete the db files before creating new ones."
+ << std::endl;
+ return false;
+ }
+ if (!parser.Has("--create") && !dbFilesExist) {
+ std::cerr << "No db files found." << std::endl;
+ std::cerr << "Create them using 'nss db --create [--path /foo/bar]' before "
+ "continuing."
+ << std::endl;
+ return false;
+ }
+
+ // init NSS
+ const char *certPrefix = ""; // certutil -P option --- can leave this empty
+ SECStatus rv = NSS_Initialize(initDir.c_str(), certPrefix, certPrefix,
+ "secmod.db", readOnly ? NSS_INIT_READONLY : 0);
+ if (rv != SECSuccess) {
+ std::cerr << "NSS init failed!" << std::endl;
+ return false;
+ }
+
+ bool ret = true;
+ if (parser.Has("--list-certs")) {
+ ListCertificates();
+ } else if (parser.Has("--import-cert")) {
+ ret = ImportCertificate(parser);
+ } else if (parser.Has("--create")) {
+ ret = InitSlotPassword();
+ if (ret) {
+ std::cout << "DB files created successfully." << std::endl;
+ }
+ } else if (parser.Has("--list-keys")) {
+ ret = ListKeys();
+ } else if (parser.Has("--import-key")) {
+ ret = ImportKey(parser);
+ } else if (parser.Has("--delete-cert")) {
+ ret = DeleteCert(parser);
+ } else if (parser.Has("--delete-key")) {
+ ret = DeleteKey(parser);
+ } else if (parser.Has("--change-password")) {
+ ret = ChangeSlotPassword();
+ }
+
+ // shutdown nss
+ if (NSS_Shutdown() != SECSuccess) {
+ std::cerr << "NSS Shutdown failed!" << std::endl;
+ return false;
+ }
+
+ return ret;
+}
+
+bool DBTool::PathHasDBFiles(std::string path) {
+ std::regex certDBPattern("cert.*\\.db");
+ std::regex keyDBPattern("key.*\\.db");
+
+ PRDir *dir = PR_OpenDir(path.c_str());
+ if (!dir) {
+ std::cerr << "Directory " << path << " could not be accessed!" << std::endl;
+ return false;
+ }
+
+ PRDirEntry *ent;
+ bool dbFileExists = false;
+ while ((ent = PR_ReadDir(dir, PR_SKIP_BOTH))) {
+ if (std::regex_match(ent->name, certDBPattern) ||
+ std::regex_match(ent->name, keyDBPattern) ||
+ "secmod.db" == std::string(ent->name)) {
+ dbFileExists = true;
+ break;
+ }
+ }
+
+ (void)PR_CloseDir(dir);
+ return dbFileExists;
+}
+
+void DBTool::ListCertificates() {
+ ScopedCERTCertList list(PK11_ListCerts(PK11CertListAll, nullptr));
+ CERTCertListNode *node;
+
+ std::cout << std::setw(60) << std::left << "Certificate Nickname"
+ << " "
+ << "Trust Attributes" << std::endl;
+ std::cout << std::setw(60) << std::left << ""
+ << " "
+ << "SSL,S/MIME,JAR/XPI" << std::endl
+ << std::endl;
+
+ for (node = CERT_LIST_HEAD(list); !CERT_LIST_END(node, list);
+ node = CERT_LIST_NEXT(node)) {
+ CERTCertificate *cert = node->cert;
+
+ std::string name("(unknown)");
+ char *appData = static_cast<char *>(node->appData);
+ if (appData && strlen(appData) > 0) {
+ name = appData;
+ } else if (cert->nickname && strlen(cert->nickname) > 0) {
+ name = cert->nickname;
+ } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
+ name = cert->emailAddr;
+ }
+
+ CERTCertTrust trust;
+ std::string trusts;
+ if (CERT_GetCertTrust(cert, &trust) == SECSuccess) {
+ std::stringstream ss;
+ ss << PrintFlags(trust.sslFlags);
+ ss << ",";
+ ss << PrintFlags(trust.emailFlags);
+ ss << ",";
+ ss << PrintFlags(trust.objectSigningFlags);
+ trusts = ss.str();
+ } else {
+ trusts = ",,";
+ }
+ std::cout << std::setw(60) << std::left << name << " " << trusts
+ << std::endl;
+ }
+}
+
+bool DBTool::ImportCertificate(const ArgParser &parser) {
+ if (!parser.Has("--name")) {
+ std::cerr << "A name (--name) is required to import a certificate."
+ << std::endl;
+ Usage();
+ return false;
+ }
+
+ std::string derFilePath = parser.Get("--import-cert");
+ std::string certName = parser.Get("--name");
+ std::string trustString("TCu,Cu,Tu");
+ if (parser.Has("--trusts")) {
+ trustString = parser.Get("--trusts");
+ }
+
+ CERTCertTrust trust;
+ SECStatus rv = CERT_DecodeTrustString(&trust, trustString.c_str());
+ if (rv != SECSuccess) {
+ std::cerr << "Cannot decode trust string!" << std::endl;
+ return false;
+ }
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ std::vector<uint8_t> certData = ReadInputData(derFilePath);
+
+ ScopedCERTCertificate cert(CERT_DecodeCertFromPackage(
+ reinterpret_cast<char *>(certData.data()), certData.size()));
+ if (cert.get() == nullptr) {
+ std::cerr << "Error: Could not decode certificate!" << std::endl;
+ return false;
+ }
+
+ rv = PK11_ImportCert(slot.get(), cert.get(), CK_INVALID_HANDLE,
+ certName.c_str(), PR_FALSE);
+ if (rv != SECSuccess) {
+ // TODO handle authentication -> PK11_Authenticate (see certutil.c line
+ // 134)
+ std::cerr << "Error: Could not add certificate to database!" << std::endl;
+ return false;
+ }
+
+ rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert.get(), &trust);
+ if (rv != SECSuccess) {
+ std::cerr << "Cannot change cert's trust" << std::endl;
+ return false;
+ }
+
+ std::cout << "Certificate import was successful!" << std::endl;
+ // TODO show information about imported certificate
+ return true;
+}
+
+bool DBTool::ListKeys() {
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ if (!DBLoginIfNeeded(slot)) {
+ return false;
+ }
+
+ ScopedSECKEYPrivateKeyList list(PK11_ListPrivateKeysInSlot(slot.get()));
+ if (list.get() == nullptr) {
+ std::cerr << "Listing private keys failed with error "
+ << PR_ErrorToName(PR_GetError()) << std::endl;
+ return false;
+ }
+
+ SECKEYPrivateKeyListNode *node;
+ int count = 0;
+ for (node = PRIVKEY_LIST_HEAD(list.get());
+ !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
+ char *keyNameRaw = PK11_GetPrivateKeyNickname(node->key);
+ std::string keyName(keyNameRaw ? keyNameRaw : "");
+
+ if (keyName.empty()) {
+ ScopedCERTCertificate cert(PK11_GetCertFromPrivateKey(node->key));
+ if (cert.get()) {
+ if (cert->nickname && strlen(cert->nickname) > 0) {
+ keyName = cert->nickname;
+ } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
+ keyName = cert->emailAddr;
+ }
+ }
+ if (keyName.empty()) {
+ keyName = "(none)"; // default value
+ }
+ }
+
+ SECKEYPrivateKey *key = node->key;
+ ScopedSECItem keyIDItem(PK11_GetLowLevelKeyIDForPrivateKey(key));
+ if (keyIDItem.get() == nullptr) {
+ std::cerr << "Error: PK11_GetLowLevelKeyIDForPrivateKey failed!"
+ << std::endl;
+ continue;
+ }
+
+ std::string keyID = StringToHex(keyIDItem);
+
+ if (count++ == 0) {
+ // print header
+ std::cout << std::left << std::setw(20) << "<key#, key name>"
+ << std::setw(20) << "key type"
+ << "key id" << std::endl;
+ }
+
+ std::stringstream leftElem;
+ leftElem << "<" << count << ", " << keyName << ">";
+ std::cout << std::left << std::setw(20) << leftElem.str() << std::setw(20)
+ << keyTypeName[key->keyType] << keyID << std::endl;
+ }
+
+ if (count == 0) {
+ std::cout << "No keys found." << std::endl;
+ }
+
+ return true;
+}
+
+bool DBTool::ImportKey(const ArgParser &parser) {
+ std::string privKeyFilePath = parser.Get("--import-key");
+ std::string name;
+ if (parser.Has("--name")) {
+ name = parser.Get("--name");
+ }
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ if (!DBLoginIfNeeded(slot)) {
+ return false;
+ }
+
+ std::vector<uint8_t> privKeyData = ReadInputData(privKeyFilePath);
+ if (privKeyData.empty()) {
+ return false;
+ }
+ SECItem pkcs8PrivKeyItem = {
+ siBuffer, reinterpret_cast<unsigned char *>(privKeyData.data()),
+ static_cast<unsigned int>(privKeyData.size())};
+
+ SECItem nickname = {siBuffer, nullptr, 0};
+ if (!name.empty()) {
+ nickname.data = const_cast<unsigned char *>(
+ reinterpret_cast<const unsigned char *>(name.c_str()));
+ nickname.len = static_cast<unsigned int>(name.size());
+ }
+
+ SECStatus rv = PK11_ImportDERPrivateKeyInfo(
+ slot.get(), &pkcs8PrivKeyItem,
+ nickname.data == nullptr ? nullptr : &nickname, nullptr /*publicValue*/,
+ true /*isPerm*/, false /*isPrivate*/, KU_ALL, nullptr);
+ if (rv != SECSuccess) {
+ std::cerr << "Importing a private key in DER format failed with error "
+ << PR_ErrorToName(PR_GetError()) << std::endl;
+ return false;
+ }
+
+ std::cout << "Key import succeeded." << std::endl;
+ return true;
+}
+
+bool DBTool::DeleteCert(const ArgParser &parser) {
+ std::string certName = parser.Get("--delete-cert");
+ if (certName.empty()) {
+ std::cerr << "A name is required to delete a certificate." << std::endl;
+ Usage();
+ return false;
+ }
+
+ ScopedCERTCertificate cert(CERT_FindCertByNicknameOrEmailAddr(
+ CERT_GetDefaultCertDB(), certName.c_str()));
+ if (!cert) {
+ std::cerr << "Could not find certificate with name " << certName << "."
+ << std::endl;
+ return false;
+ }
+
+ SECStatus rv = SEC_DeletePermCertificate(cert.get());
+ if (rv != SECSuccess) {
+ std::cerr << "Unable to delete certificate with name " << certName << "."
+ << std::endl;
+ return false;
+ }
+
+ std::cout << "Certificate with name " << certName << " deleted successfully."
+ << std::endl;
+ return true;
+}
+
+bool DBTool::DeleteKey(const ArgParser &parser) {
+ std::string keyName = parser.Get("--delete-key");
+ if (keyName.empty()) {
+ std::cerr << "A name is required to delete a key." << std::endl;
+ Usage();
+ return false;
+ }
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+ if (slot.get() == nullptr) {
+ std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+ return false;
+ }
+
+ if (!DBLoginIfNeeded(slot)) {
+ return false;
+ }
+
+ ScopedSECKEYPrivateKeyList list(PK11_ListPrivKeysInSlot(
+ slot.get(), const_cast<char *>(keyName.c_str()), nullptr));
+ if (list.get() == nullptr) {
+ std::cerr << "Fetching private keys with nickname " << keyName
+ << " failed with error " << PR_ErrorToName(PR_GetError())
+ << std::endl;
+ return false;
+ }
+
+ unsigned int foundKeys = 0, deletedKeys = 0;
+ SECKEYPrivateKeyListNode *node;
+ for (node = PRIVKEY_LIST_HEAD(list.get());
+ !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
+ SECKEYPrivateKey *privKey = node->key;
+ foundKeys++;
+ // see PK11_DeleteTokenPrivateKey for example usage
+ // calling PK11_DeleteTokenPrivateKey directly does not work because it also
+ // destroys the SECKEYPrivateKey (by calling SECKEY_DestroyPrivateKey) -
+ // then SECKEY_DestroyPrivateKeyList does not
+ // work because it also calls SECKEY_DestroyPrivateKey
+ SECStatus rv =
+ PK11_DestroyTokenObject(privKey->pkcs11Slot, privKey->pkcs11ID);
+ if (rv == SECSuccess) {
+ deletedKeys++;
+ }
+ }
+
+ if (foundKeys > deletedKeys) {
+ std::cerr << "Some keys could not be deleted." << std::endl;
+ }
+
+ if (deletedKeys > 0) {
+ std::cout << "Found " << foundKeys << " keys." << std::endl;
+ std::cout << "Successfully deleted " << deletedKeys
+ << " key(s) with nickname " << keyName << "." << std::endl;
+ } else {
+ std::cout << "No key with nickname " << keyName << " found to delete."
+ << std::endl;
+ }
+
+ return true;
+}
diff --git a/nss/nss-tool/db/dbtool.h b/nss/nss-tool/db/dbtool.h
new file mode 100644
index 0000000..dd0ef0a
--- /dev/null
+++ b/nss/nss-tool/db/dbtool.h
@@ -0,0 +1,28 @@
+/* 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/. */
+
+#ifndef dbtool_h__
+#define dbtool_h__
+
+#include <string>
+#include <vector>
+#include "argparse.h"
+#include "tool.h"
+
+class DBTool : public Tool {
+ public:
+ bool Run(const std::vector<std::string>& arguments) override;
+
+ private:
+ void Usage() override;
+ bool PathHasDBFiles(std::string path);
+ void ListCertificates();
+ bool ImportCertificate(const ArgParser& parser);
+ bool ListKeys();
+ bool ImportKey(const ArgParser& parser);
+ bool DeleteCert(const ArgParser& parser);
+ bool DeleteKey(const ArgParser& parser);
+};
+
+#endif // dbtool_h__
diff --git a/nss/nss-tool/enc/enctool.cc b/nss/nss-tool/enc/enctool.cc
new file mode 100644
index 0000000..b3c0d1d
--- /dev/null
+++ b/nss/nss-tool/enc/enctool.cc
@@ -0,0 +1,464 @@
+/* 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 "enctool.h"
+#include "argparse.h"
+#include "util.h"
+
+#include "nss.h"
+
+#include <assert.h>
+#include <chrono>
+#include <fstream>
+#include <iomanip>
+#include <iostream>
+
+void EncTool::PrintError(const std::string& m, size_t line_number) {
+ std::cerr << m << " - enctool.cc:" << line_number << std::endl;
+}
+
+void EncTool::PrintError(const std::string& m, PRErrorCode err,
+ size_t line_number) {
+ std::cerr << m << " (error " << err << ")"
+ << " - enctool.cc:" << line_number << std::endl;
+}
+
+void EncTool::PrintBytes(const std::vector<uint8_t>& bytes,
+ const std::string& txt) {
+ if (debug_) {
+ std::cerr << txt << ": ";
+ for (uint8_t b : bytes) {
+ std::cerr << std::setfill('0') << std::setw(2) << std::hex
+ << static_cast<int>(b);
+ }
+ std::cerr << std::endl << std::dec;
+ }
+}
+
+std::vector<uint8_t> EncTool::GenerateRandomness(size_t num_bytes) {
+ std::vector<uint8_t> bytes(num_bytes);
+ if (PK11_GenerateRandom(bytes.data(), num_bytes) != SECSuccess) {
+ PrintError("No randomness available. Abort!", __LINE__);
+ exit(1);
+ }
+ return bytes;
+}
+
+bool EncTool::WriteBytes(const std::vector<uint8_t>& bytes,
+ std::string out_file) {
+ std::fstream output(out_file, std::ios::out | std::ios::binary);
+ if (!output.good()) {
+ return false;
+ }
+ output.write(reinterpret_cast<const char*>(
+ const_cast<const unsigned char*>(bytes.data())),
+ bytes.size());
+ output.flush();
+ output.close();
+ return true;
+}
+
+bool EncTool::GetKey(const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& key_item) {
+ if (key_bytes.empty()) {
+ return false;
+ }
+
+ // Build key.
+ key_item =
+ ScopedSECItem(SECITEM_AllocItem(nullptr, nullptr, key_bytes.size()));
+ if (!key_item) {
+ return false;
+ }
+ key_item->type = siBuffer;
+ memcpy(key_item->data, key_bytes.data(), key_bytes.size());
+ key_item->len = key_bytes.size();
+
+ return true;
+}
+
+bool EncTool::GetAesGcmKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& aes_key, ScopedSECItem& params) {
+ if (iv_bytes.empty()) {
+ return false;
+ }
+
+ // GCM params.
+ CK_GCM_PARAMS* gcm_params =
+ static_cast<CK_GCM_PARAMS*>(PORT_Malloc(sizeof(struct CK_GCM_PARAMS)));
+ if (!gcm_params) {
+ return false;
+ }
+
+ uint8_t* iv = static_cast<uint8_t*>(PORT_Malloc(iv_bytes.size()));
+ if (!iv) {
+ return false;
+ }
+ memcpy(iv, iv_bytes.data(), iv_bytes.size());
+ gcm_params->pIv = iv;
+ gcm_params->ulIvLen = iv_bytes.size();
+ gcm_params->ulTagBits = 128;
+ if (aad.empty()) {
+ gcm_params->pAAD = nullptr;
+ gcm_params->ulAADLen = 0;
+ } else {
+ uint8_t* ad = static_cast<uint8_t*>(PORT_Malloc(aad.size()));
+ if (!ad) {
+ return false;
+ }
+ memcpy(ad, aad.data(), aad.size());
+ gcm_params->pAAD = ad;
+ gcm_params->ulAADLen = aad.size();
+ }
+
+ params =
+ ScopedSECItem(SECITEM_AllocItem(nullptr, nullptr, sizeof(*gcm_params)));
+ if (!params) {
+ return false;
+ }
+ params->len = sizeof(*gcm_params);
+ params->type = siBuffer;
+ params->data = reinterpret_cast<unsigned char*>(gcm_params);
+
+ return GetKey(key_bytes, aes_key);
+}
+
+bool EncTool::GenerateAesGcmKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& aes_key, ScopedSECItem& params) {
+ size_t key_size = 16, iv_size = 12;
+ std::vector<uint8_t> iv_bytes = GenerateRandomness(iv_size);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = GenerateRandomness(key_size);
+ PrintBytes(key_bytes, "key");
+ // Maybe write out the key and parameters.
+ if (write_key_ && !WriteBytes(key_bytes, key_file_)) {
+ return false;
+ }
+ if (write_iv_ && !WriteBytes(iv_bytes, iv_file_)) {
+ return false;
+ }
+ return GetAesGcmKey(aad, iv_bytes, key_bytes, aes_key, params);
+}
+
+bool EncTool::ReadAesGcmKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& aes_key, ScopedSECItem& params) {
+ std::vector<uint8_t> iv_bytes = ReadInputData(iv_file_);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = ReadInputData(key_file_);
+ PrintBytes(key_bytes, "key");
+ return GetAesGcmKey(aad, iv_bytes, key_bytes, aes_key, params);
+}
+
+bool EncTool::GetChachaKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& chacha_key, ScopedSECItem& params) {
+ if (iv_bytes.empty()) {
+ return false;
+ }
+
+ // AEAD params.
+ CK_NSS_AEAD_PARAMS* aead_params = static_cast<CK_NSS_AEAD_PARAMS*>(
+ PORT_Malloc(sizeof(struct CK_NSS_AEAD_PARAMS)));
+ if (!aead_params) {
+ return false;
+ }
+
+ uint8_t* iv = static_cast<uint8_t*>(PORT_Malloc(iv_bytes.size()));
+ if (!iv) {
+ return false;
+ }
+ memcpy(iv, iv_bytes.data(), iv_bytes.size());
+ aead_params->pNonce = iv;
+ aead_params->ulNonceLen = iv_bytes.size();
+ aead_params->ulTagLen = 16;
+ if (aad.empty()) {
+ aead_params->pAAD = nullptr;
+ aead_params->ulAADLen = 0;
+ } else {
+ uint8_t* ad = static_cast<uint8_t*>(PORT_Malloc(aad.size()));
+ if (!ad) {
+ return false;
+ }
+ memcpy(ad, aad.data(), aad.size());
+ aead_params->pAAD = ad;
+ aead_params->ulAADLen = aad.size();
+ }
+
+ params =
+ ScopedSECItem(SECITEM_AllocItem(nullptr, nullptr, sizeof(*aead_params)));
+ if (!params) {
+ return false;
+ }
+ params->len = sizeof(*aead_params);
+ params->type = siBuffer;
+ params->data = reinterpret_cast<unsigned char*>(aead_params);
+
+ return GetKey(key_bytes, chacha_key);
+}
+
+bool EncTool::GenerateChachaKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key,
+ ScopedSECItem& params) {
+ size_t key_size = 32, iv_size = 12;
+ std::vector<uint8_t> iv_bytes = GenerateRandomness(iv_size);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = GenerateRandomness(key_size);
+ PrintBytes(key_bytes, "key");
+ // Maybe write out the key and parameters.
+ if (write_key_ && !WriteBytes(key_bytes, key_file_)) {
+ return false;
+ }
+ if (write_iv_ && !WriteBytes(iv_bytes, iv_file_)) {
+ return false;
+ }
+ return GetChachaKey(aad, iv_bytes, key_bytes, chacha_key, params);
+}
+
+bool EncTool::ReadChachaKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key, ScopedSECItem& params) {
+ std::vector<uint8_t> iv_bytes = ReadInputData(iv_file_);
+ PrintBytes(iv_bytes, "IV");
+ std::vector<uint8_t> key_bytes = ReadInputData(key_file_);
+ PrintBytes(key_bytes, "key");
+ return GetChachaKey(aad, iv_bytes, key_bytes, chacha_key, params);
+}
+
+bool EncTool::DoCipher(std::string file_name, std::string out_file,
+ bool encrypt, key_func_t get_params) {
+ SECStatus rv;
+ unsigned int outLen = 0, chunkSize = 1024;
+ char buffer[1040];
+ const unsigned char* bufferStart =
+ reinterpret_cast<const unsigned char*>(buffer);
+
+ ScopedPK11SlotInfo slot(PK11_GetInternalSlot());
+ if (!slot) {
+ PrintError("Unable to find security device", PR_GetError(), __LINE__);
+ return false;
+ }
+
+ ScopedSECItem key, params;
+ if (!(this->*get_params)(std::vector<uint8_t>(), key, params)) {
+ PrintError("Geting keys and params failed.", __LINE__);
+ return false;
+ }
+
+ ScopedPK11SymKey symKey(
+ PK11_ImportSymKey(slot.get(), cipher_mech_, PK11_OriginUnwrap,
+ CKA_DECRYPT | CKA_ENCRYPT, key.get(), nullptr));
+ if (!symKey) {
+ PrintError("Failure to import key into NSS", PR_GetError(), __LINE__);
+ return false;
+ }
+
+ std::streambuf* buf;
+ std::ofstream output_file(out_file, std::ios::out | std::ios::binary);
+ if (!out_file.empty()) {
+ if (!output_file.good()) {
+ return false;
+ }
+ buf = output_file.rdbuf();
+ } else {
+ buf = std::cout.rdbuf();
+ }
+ std::ostream output(buf);
+
+ // Read from stdin.
+ if (file_name.empty()) {
+ std::vector<uint8_t> data = ReadInputData("");
+ std::vector<uint8_t> out(data.size() + 16);
+ SECStatus rv;
+ if (encrypt) {
+ rv = PK11_Encrypt(symKey.get(), cipher_mech_, params.get(), out.data(),
+ &outLen, data.size() + 16, data.data(), data.size());
+ } else {
+ rv = PK11_Decrypt(symKey.get(), cipher_mech_, params.get(), out.data(),
+ &outLen, data.size() + 16, data.data(), data.size());
+ }
+ if (rv != SECSuccess) {
+ PrintError(encrypt ? "Error encrypting" : "Error decrypting",
+ PR_GetError(), __LINE__);
+ return false;
+ };
+ output.write(reinterpret_cast<char*>(out.data()), outLen);
+ output.flush();
+ if (output_file.good()) {
+ output_file.close();
+ } else {
+ output << std::endl;
+ }
+
+ std::cerr << "Done " << (encrypt ? "encrypting" : "decrypting")
+ << std::endl;
+ return true;
+ }
+
+ // Read file from file_name.
+ std::ifstream input(file_name, std::ios::binary);
+ if (!input.good()) {
+ return false;
+ }
+ uint8_t out[1040];
+ while (input) {
+ if (encrypt) {
+ input.read(buffer, chunkSize);
+ rv = PK11_Encrypt(symKey.get(), cipher_mech_, params.get(), out, &outLen,
+ chunkSize + 16, bufferStart, input.gcount());
+ } else {
+ // We have to read the tag when decrypting.
+ input.read(buffer, chunkSize + 16);
+ rv = PK11_Decrypt(symKey.get(), cipher_mech_, params.get(), out, &outLen,
+ chunkSize + 16, bufferStart, input.gcount());
+ }
+ if (rv != SECSuccess) {
+ PrintError(encrypt ? "Error encrypting" : "Error decrypting",
+ PR_GetError(), __LINE__);
+ return false;
+ };
+ output.write(reinterpret_cast<const char*>(out), outLen);
+ output.flush();
+ }
+ if (output_file.good()) {
+ output_file.close();
+ } else {
+ output << std::endl;
+ }
+ std::cerr << "Done " << (encrypt ? "encrypting" : "decrypting") << std::endl;
+
+ return true;
+}
+
+size_t EncTool::PrintFileSize(std::string file_name) {
+ std::ifstream input(file_name, std::ifstream::ate | std::ifstream::binary);
+ auto size = input.tellg();
+ std::cerr << "Size of file to encrypt: " << size / 1024 / 1024 << " MB"
+ << std::endl;
+ return size;
+}
+
+bool EncTool::IsValidCommand(ArgParser arguments) {
+ // Either encrypt or decrypt is fine.
+ bool valid = arguments.Has("--encrypt") != arguments.Has("--decrypt");
+ // An input file is required for decryption only.
+ valid &= arguments.Has("--in") || arguments.Has("--encrypt");
+ // An output file is required for encryption only.
+ valid &= arguments.Has("--out") || arguments.Has("--decrypt");
+ // Files holding the IV and key are required for decryption.
+ valid &= arguments.Has("--iv") || arguments.Has("--encrypt");
+ valid &= arguments.Has("--key") || arguments.Has("--encrypt");
+ // Cipher is always required.
+ valid &= arguments.Has("--cipher");
+ return valid;
+}
+
+bool EncTool::Run(const std::vector<std::string>& arguments) {
+ ArgParser parser(arguments);
+
+ if (!IsValidCommand(parser)) {
+ Usage();
+ return false;
+ }
+
+ if (NSS_NoDB_Init(nullptr) != SECSuccess) {
+ PrintError("NSS initialization failed", PR_GetError(), __LINE__);
+ return false;
+ }
+
+ if (parser.Has("--debug")) {
+ debug_ = 1;
+ }
+ if (parser.Has("--iv")) {
+ iv_file_ = parser.Get("--iv");
+ } else {
+ write_iv_ = false;
+ }
+ if (parser.Has("--key")) {
+ key_file_ = parser.Get("--key");
+ } else {
+ write_key_ = false;
+ }
+
+ key_func_t get_params;
+ bool encrypt = parser.Has("--encrypt");
+ if (parser.Get("--cipher") == kAESCommand) {
+ cipher_mech_ = CKM_AES_GCM;
+ if (encrypt) {
+ get_params = &EncTool::GenerateAesGcmKey;
+ } else {
+ get_params = &EncTool::ReadAesGcmKey;
+ }
+ } else if (parser.Get("--cipher") == kChaChaCommand) {
+ cipher_mech_ = CKM_NSS_CHACHA20_POLY1305;
+ if (encrypt) {
+ get_params = &EncTool::GenerateChachaKey;
+ } else {
+ get_params = &EncTool::ReadChachaKey;
+ }
+ } else {
+ Usage();
+ return false;
+ }
+ // Don't write out key and iv when decrypting.
+ if (!encrypt) {
+ write_key_ = false;
+ write_iv_ = false;
+ }
+
+ std::string input_file = parser.Has("--in") ? parser.Get("--in") : "";
+ std::string output_file = parser.Has("--out") ? parser.Get("--out") : "";
+ size_t file_size = 0;
+ if (!input_file.empty()) {
+ file_size = PrintFileSize(input_file);
+ }
+ auto begin = std::chrono::high_resolution_clock::now();
+ if (!DoCipher(input_file, output_file, encrypt, get_params)) {
+ (void)NSS_Shutdown();
+ return false;
+ }
+ auto end = std::chrono::high_resolution_clock::now();
+ auto ns =
+ std::chrono::duration_cast<std::chrono::nanoseconds>(end - begin).count();
+ auto seconds = ns / 1000000000;
+ std::cerr << ns << " ns (~" << seconds << " s) and " << std::endl;
+ std::cerr << "That's approximately " << (double)file_size / ns << " b/ns"
+ << std::endl;
+
+ if (NSS_Shutdown() != SECSuccess) {
+ return false;
+ }
+
+ return true;
+}
+
+void EncTool::Usage() {
+ std::string const txt = R"~(
+Usage: nss encrypt|decrypt --cipher aes|chacha [--in <file>] [--out <file>]
+ [--key <file>] [--iv <file>]
+
+ --cipher Set the cipher to use.
+ --cipher aes: Use AES-GCM to encrypt/decrypt.
+ --cipher chacha: Use ChaCha20/Poly1305 to encrypt/decrypt.
+ --in The file to encrypt/decrypt. If no file is given, we read
+ from stdin (only when encrypting).
+ --out The file to write the ciphertext/plaintext to. If no file
+ is given we write the plaintext to stdout (only when
+ decrypting).
+ --key The file to write the used key to/to read the key
+ from. Optional parameter. When not given, don't write out
+ the key.
+ --iv The file to write the used IV to/to read the IV
+ from. Optional parameter. When not given, don't write out
+ the IV.
+
+ Examples:
+ nss encrypt --cipher aes --iv iv --key key --out ciphertext
+ nss decrypt --cipher chacha --iv iv --key key --in ciphertex
+
+ Note: This tool overrides files without asking.
+)~";
+ std::cerr << txt << std::endl;
+}
diff --git a/nss/nss-tool/enc/enctool.h b/nss/nss-tool/enc/enctool.h
new file mode 100644
index 0000000..5a6a5a1
--- /dev/null
+++ b/nss/nss-tool/enc/enctool.h
@@ -0,0 +1,62 @@
+/* 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/. */
+
+#ifndef enctool_h__
+#define enctool_h__
+
+#include <string>
+#include <vector>
+#include "argparse.h"
+#include "prerror.h"
+#include "scoped_ptrs.h"
+#include "tool.h"
+
+class EncTool : public Tool {
+ public:
+ bool Run(const std::vector<std::string>& arguments) override;
+ void Usage() override;
+
+ private:
+ typedef bool (EncTool::*key_func_t)(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key,
+ ScopedSECItem& params);
+ void PrintBytes(const std::vector<uint8_t>& bytes, const std::string& txt);
+ bool WriteBytes(const std::vector<uint8_t>& bytes, std::string out_file);
+ void PrintError(const std::string& m, PRErrorCode err, size_t line_number);
+ void PrintError(const std::string& m, size_t line_number);
+ bool GetKey(const std::vector<uint8_t>& key_bytes, ScopedSECItem& key_item);
+ bool GetAesGcmKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& aes_key, ScopedSECItem& params);
+ bool GetChachaKey(const std::vector<uint8_t>& aad,
+ const std::vector<uint8_t>& iv_bytes,
+ const std::vector<uint8_t>& key_bytes,
+ ScopedSECItem& chacha_key, ScopedSECItem& params);
+ bool GenerateAesGcmKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& aes_key, ScopedSECItem& params);
+ bool ReadAesGcmKey(const std::vector<uint8_t>& aad, ScopedSECItem& aes_key,
+ ScopedSECItem& params);
+ std::vector<uint8_t> GenerateRandomness(size_t num_bytes);
+ bool GenerateChachaKey(const std::vector<uint8_t>& aad,
+ ScopedSECItem& chacha_key, ScopedSECItem& params);
+ bool ReadChachaKey(const std::vector<uint8_t>& aad, ScopedSECItem& chacha_key,
+ ScopedSECItem& params);
+ bool DoCipher(std::string fileName, std::string outFile, bool encrypt,
+ key_func_t get_params);
+ size_t PrintFileSize(std::string fileName);
+ bool IsValidCommand(ArgParser arguments);
+
+ bool debug_ = false;
+ bool write_key_ = true;
+ bool write_iv_ = true;
+ std::string key_file_ = "/tmp/key";
+ std::string iv_file_ = "/tmp/iv";
+ CK_MECHANISM_TYPE cipher_mech_;
+
+ const std::string kAESCommand = "aes";
+ const std::string kChaChaCommand = "chacha";
+};
+
+#endif // enctool_h__
diff --git a/nss/nss-tool/nss_tool.cc b/nss/nss-tool/nss_tool.cc
new file mode 100644
index 0000000..1d8fc61
--- /dev/null
+++ b/nss/nss-tool/nss_tool.cc
@@ -0,0 +1,64 @@
+/* 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 <algorithm>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <prinit.h>
+
+#include "argparse.h"
+#include "db/dbtool.h"
+#include "enc/enctool.h"
+#include "tool.h"
+
+static void Usage() {
+ std::cerr << "Usage: nss <command> <subcommand> [options]" << std::endl;
+ std::cerr << " nss db [--path <directory>] <commands>" << std::endl;
+ std::cerr << " nss encrypt <options>" << std::endl;
+ std::cerr << " nss decrypt <options>" << std::endl;
+}
+
+static const std::string kDbCommand = "db";
+static const std::string kEncryptCommand = "encrypt";
+static const std::string kDecryptCommand = "decrypt";
+
+int main(int argc, char **argv) {
+ if (argc < 2) {
+ Usage();
+ return 1;
+ }
+ std::vector<std::string> arguments(argv + 2, argv + argc);
+
+ std::unique_ptr<Tool> tool = nullptr;
+ if (argv[1] == kDbCommand) {
+ tool = std::unique_ptr<Tool>(new DBTool());
+ }
+ if (argv[1] == kEncryptCommand) {
+ tool = std::unique_ptr<Tool>(new EncTool());
+ arguments.push_back("--encrypt");
+ }
+ if (argv[1] == kDecryptCommand) {
+ tool = std::unique_ptr<Tool>(new EncTool());
+ arguments.push_back("--decrypt");
+ }
+ if (!tool) {
+ Usage();
+ return 1;
+ }
+
+ int exit_code = 0;
+ PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+ if (!tool->Run(arguments)) {
+ exit_code = 1;
+ }
+
+ PR_Cleanup();
+
+ return exit_code;
+}
diff --git a/nss/nss-tool/nss_tool.gyp b/nss/nss-tool/nss_tool.gyp
new file mode 100644
index 0000000..ed408e9
--- /dev/null
+++ b/nss/nss-tool/nss_tool.gyp
@@ -0,0 +1,30 @@
+# 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/.
+{
+ 'includes' : [
+ '../coreconf/config.gypi',
+ '../cmd/platlibs.gypi',
+ ],
+ 'targets' : [
+ {
+ 'target_name' : 'nss',
+ 'type' : 'executable',
+ 'sources' : [
+ 'nss_tool.cc',
+ 'common/argparse.cc',
+ 'common/util.cc',
+ 'db/dbtool.cc',
+ 'enc/enctool.cc',
+ ],
+ 'include_dirs': [
+ 'common',
+ ],
+ 'dependencies' : [
+ '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
+ '<(DEPTH)/exports.gyp:dbm_exports',
+ '<(DEPTH)/exports.gyp:nss_exports',
+ ],
+ }
+ ],
+}