From d0ba5a10003afb0667fc7ae8dcb559400099a751 Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 10 Apr 2017 16:06:04 +0200 Subject: Bug 1355422 - NSS tool for encryption, r=ttaubert Summary: Command line tool to encrypt files with aes-gcm and chacha. Can also be used to measure performance of the cipher. Reviewers: ttaubert Differential Revision: https://nss-review.dev.mozaws.net/D245 --- nss-tool/common/tool.h | 20 +++ nss-tool/common/util.cc | 12 +- nss-tool/common/util.h | 7 +- nss-tool/db/dbtool.cc | 8 +- nss-tool/db/dbtool.h | 7 +- nss-tool/enc/enctool.cc | 464 ++++++++++++++++++++++++++++++++++++++++++++++++ nss-tool/enc/enctool.h | 62 +++++++ nss-tool/nss_tool.cc | 30 +++- nss-tool/nss_tool.gyp | 1 + 9 files changed, 593 insertions(+), 18 deletions(-) create mode 100644 nss-tool/common/tool.h create mode 100644 nss-tool/enc/enctool.cc create mode 100644 nss-tool/enc/enctool.h (limited to 'nss-tool') diff --git a/nss-tool/common/tool.h b/nss-tool/common/tool.h new file mode 100644 index 000000000..17ebcac29 --- /dev/null +++ b/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 +#include + +class Tool { + public: + virtual bool Run(const std::vector& arguments) = 0; + virtual ~Tool() {} + + private: + virtual void Usage() = 0; +}; + +#endif // tool_h__ diff --git a/nss-tool/common/util.cc b/nss-tool/common/util.cc index 5b7ed0b9d..e5af2cb8a 100644 --- a/nss-tool/common/util.cc +++ b/nss-tool/common/util.cc @@ -74,15 +74,15 @@ static char *GetModulePassword(PK11SlotInfo *slot, int retry, void *arg) { return nullptr; } -static std::vector ReadFromIstream(std::istream &is) { - std::vector certData; +static std::vector ReadFromIstream(std::istream &is) { + std::vector data; while (is) { char buf[1024]; is.read(buf, sizeof(buf)); - certData.insert(certData.end(), buf, buf + is.gcount()); + data.insert(data.end(), buf, buf + is.gcount()); } - return certData; + return data; } static std::string GetNewPasswordFromUser(void) { @@ -181,8 +181,8 @@ std::string StringToHex(const ScopedSECItem &input) { return ss.str(); } -std::vector ReadInputData(std::string &dataPath) { - std::vector data; +std::vector ReadInputData(std::string dataPath) { + std::vector data; if (dataPath.empty()) { std::cout << "No input file path given, using stdin." << std::endl; data = ReadFromIstream(std::cin); diff --git a/nss-tool/common/util.h b/nss-tool/common/util.h index bfe6dbf70..bc2e297a4 100644 --- a/nss-tool/common/util.h +++ b/nss-tool/common/util.h @@ -5,12 +5,17 @@ #ifndef util_h__ #define util_h__ +#include "nspr.h" #include "scoped_ptrs.h" #include #include #include +#ifndef PORT_Malloc +#define PORT_Malloc PR_Malloc +#endif + enum PwDataType { PW_NONE = 0, PW_FROMFILE = 1, PW_PLAINTEXT = 2 }; typedef struct { PwDataType source; @@ -21,6 +26,6 @@ bool InitSlotPassword(void); bool ChangeSlotPassword(void); bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot); std::string StringToHex(const ScopedSECItem &input); -std::vector ReadInputData(std::string &dataPath); +std::vector ReadInputData(std::string dataPath); #endif // util_h__ diff --git a/nss-tool/db/dbtool.cc b/nss-tool/db/dbtool.cc index e48cedec8..5bdeec56e 100644 --- a/nss-tool/db/dbtool.cc +++ b/nss-tool/db/dbtool.cc @@ -264,10 +264,10 @@ bool DBTool::ImportCertificate(const ArgParser &parser) { return false; } - std::vector certData = ReadInputData(derFilePath); + std::vector certData = ReadInputData(derFilePath); - ScopedCERTCertificate cert( - CERT_DecodeCertFromPackage(certData.data(), certData.size())); + ScopedCERTCertificate cert(CERT_DecodeCertFromPackage( + reinterpret_cast(certData.data()), certData.size())); if (cert.get() == nullptr) { std::cerr << "Error: Could not decode certificate!" << std::endl; return false; @@ -379,7 +379,7 @@ bool DBTool::ImportKey(const ArgParser &parser) { return false; } - std::vector privKeyData = ReadInputData(privKeyFilePath); + std::vector privKeyData = ReadInputData(privKeyFilePath); if (privKeyData.empty()) { return false; } diff --git a/nss-tool/db/dbtool.h b/nss-tool/db/dbtool.h index 927afa9f8..dd0ef0ace 100644 --- a/nss-tool/db/dbtool.h +++ b/nss-tool/db/dbtool.h @@ -8,13 +8,14 @@ #include #include #include "argparse.h" +#include "tool.h" -class DBTool { +class DBTool : public Tool { public: - bool Run(const std::vector& arguments); + bool Run(const std::vector& arguments) override; private: - void Usage(); + void Usage() override; bool PathHasDBFiles(std::string path); void ListCertificates(); bool ImportCertificate(const ArgParser& parser); diff --git a/nss-tool/enc/enctool.cc b/nss-tool/enc/enctool.cc new file mode 100644 index 000000000..0e17931a2 --- /dev/null +++ b/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 +#include +#include +#include +#include + +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& 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(b); + } + std::cerr << std::endl << std::dec; + } +} + +std::vector EncTool::GenerateRandomness(size_t num_bytes) { + std::vector 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& 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_cast(bytes.data())), + bytes.size()); + output.flush(); + output.close(); + return true; +} + +bool EncTool::GetKey(const std::vector& 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& aad, + const std::vector& iv_bytes, + const std::vector& key_bytes, + ScopedSECItem& aes_key, ScopedSECItem& params) { + if (iv_bytes.empty()) { + return false; + } + + // GCM params. + CK_GCM_PARAMS* gcm_params = + static_cast(PORT_Malloc(sizeof(struct CK_GCM_PARAMS))); + if (!gcm_params) { + return false; + } + + uint8_t* iv = static_cast(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(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(gcm_params); + + return GetKey(key_bytes, aes_key); +} + +bool EncTool::GenerateAesGcmKey(const std::vector& aad, + ScopedSECItem& aes_key, ScopedSECItem& params) { + size_t key_size = 16, iv_size = 12; + std::vector iv_bytes = GenerateRandomness(iv_size); + PrintBytes(iv_bytes, "IV"); + std::vector 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& aad, + ScopedSECItem& aes_key, ScopedSECItem& params) { + std::vector iv_bytes = ReadInputData(iv_file_); + PrintBytes(iv_bytes, "IV"); + std::vector 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& aad, + const std::vector& iv_bytes, + const std::vector& key_bytes, + ScopedSECItem& chacha_key, ScopedSECItem& params) { + if (iv_bytes.empty()) { + return false; + } + + // AEAD params. + CK_NSS_AEAD_PARAMS* aead_params = static_cast( + PORT_Malloc(sizeof(struct CK_NSS_AEAD_PARAMS))); + if (!aead_params) { + return false; + } + + uint8_t* iv = static_cast(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(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(aead_params); + + return GetKey(key_bytes, chacha_key); +} + +bool EncTool::GenerateChachaKey(const std::vector& aad, + ScopedSECItem& chacha_key, + ScopedSECItem& params) { + size_t key_size = 32, iv_size = 12; + std::vector iv_bytes = GenerateRandomness(iv_size); + PrintBytes(iv_bytes, "IV"); + std::vector 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& aad, + ScopedSECItem& chacha_key, ScopedSECItem& params) { + std::vector iv_bytes = ReadInputData(iv_file_); + PrintBytes(iv_bytes, "IV"); + std::vector 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[chunkSize + 16]; + const unsigned char* bufferStart = + reinterpret_cast(buffer); + + ScopedPK11SlotInfo slot(PK11_GetInternalSlot()); + if (!slot) { + PrintError("Unable to find security device", PR_GetError(), __LINE__); + return SECFailure; + } + + ScopedSECItem key, params; + if (!(this->*get_params)(std::vector(), key, params)) { + PrintError("Geting keys and params failed.", __LINE__); + return SECFailure; + } + + 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 SECFailure; + } + + 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 data = ReadInputData(""); + uint8_t out[data.size() + 16]; + SECStatus rv; + if (encrypt) { + rv = PK11_Encrypt(symKey.get(), cipher_mech_, params.get(), out, &outLen, + data.size() + 16, data.data(), data.size()); + } else { + rv = PK11_Decrypt(symKey.get(), cipher_mech_, params.get(), out, &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(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; + } + + // Read file from file_name. + std::ifstream input(file_name, std::ios::binary); + if (!input.good()) { + return false; + } + uint8_t out[chunkSize + 16]; + 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(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& 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(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 ] [--out ] + [--key ] [--iv ] + + --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-tool/enc/enctool.h b/nss-tool/enc/enctool.h new file mode 100644 index 000000000..5a6a5a164 --- /dev/null +++ b/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 +#include +#include "argparse.h" +#include "prerror.h" +#include "scoped_ptrs.h" +#include "tool.h" + +class EncTool : public Tool { + public: + bool Run(const std::vector& arguments) override; + void Usage() override; + + private: + typedef bool (EncTool::*key_func_t)(const std::vector& aad, + ScopedSECItem& chacha_key, + ScopedSECItem& params); + void PrintBytes(const std::vector& bytes, const std::string& txt); + bool WriteBytes(const std::vector& 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& key_bytes, ScopedSECItem& key_item); + bool GetAesGcmKey(const std::vector& aad, + const std::vector& iv_bytes, + const std::vector& key_bytes, + ScopedSECItem& aes_key, ScopedSECItem& params); + bool GetChachaKey(const std::vector& aad, + const std::vector& iv_bytes, + const std::vector& key_bytes, + ScopedSECItem& chacha_key, ScopedSECItem& params); + bool GenerateAesGcmKey(const std::vector& aad, + ScopedSECItem& aes_key, ScopedSECItem& params); + bool ReadAesGcmKey(const std::vector& aad, ScopedSECItem& aes_key, + ScopedSECItem& params); + std::vector GenerateRandomness(size_t num_bytes); + bool GenerateChachaKey(const std::vector& aad, + ScopedSECItem& chacha_key, ScopedSECItem& params); + bool ReadChachaKey(const std::vector& 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-tool/nss_tool.cc b/nss-tool/nss_tool.cc index 0b6b734bb..1d8fc6153 100644 --- a/nss-tool/nss_tool.cc +++ b/nss-tool/nss_tool.cc @@ -2,7 +2,10 @@ * 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 +#include #include +#include #include #include @@ -10,19 +13,40 @@ #include "argparse.h" #include "db/dbtool.h" +#include "enc/enctool.h" +#include "tool.h" static void Usage() { std::cerr << "Usage: nss [options]" << std::endl; std::cerr << " nss db [--path ] " << std::endl; + std::cerr << " nss encrypt " << std::endl; + std::cerr << " nss decrypt " << 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 arguments(argv + 2, argv + argc); - if (std::string(argv[1]) != "db") { + std::unique_ptr tool = nullptr; + if (argv[1] == kDbCommand) { + tool = std::unique_ptr(new DBTool()); + } + if (argv[1] == kEncryptCommand) { + tool = std::unique_ptr(new EncTool()); + arguments.push_back("--encrypt"); + } + if (argv[1] == kDecryptCommand) { + tool = std::unique_ptr(new EncTool()); + arguments.push_back("--decrypt"); + } + if (!tool) { Usage(); return 1; } @@ -30,9 +54,7 @@ int main(int argc, char **argv) { int exit_code = 0; PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); - std::vector arguments(argv + 2, argv + argc); - DBTool tool; - if (!tool.Run(arguments)) { + if (!tool->Run(arguments)) { exit_code = 1; } diff --git a/nss-tool/nss_tool.gyp b/nss-tool/nss_tool.gyp index 95b627070..ed408e9b6 100644 --- a/nss-tool/nss_tool.gyp +++ b/nss-tool/nss_tool.gyp @@ -15,6 +15,7 @@ 'common/argparse.cc', 'common/util.cc', 'db/dbtool.cc', + 'enc/enctool.cc', ], 'include_dirs': [ 'common', -- cgit v1.2.1