mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-19 14:57:32 +09:00
Update NSS to 3.35-RTM
This commit is contained in:
parent
23de11e5cd
commit
608f9fca02
388 changed files with 39075 additions and 20752 deletions
4
security/nss/nss-tool/.clang-format
Normal file
4
security/nss/nss-tool/.clang-format
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
---
|
||||
Language: Cpp
|
||||
BasedOnStyle: Google
|
||||
...
|
||||
23
security/nss/nss-tool/common/argparse.cc
Normal file
23
security/nss/nss-tool/common/argparse.cc
Normal file
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
30
security/nss/nss-tool/common/argparse.h
Normal file
30
security/nss/nss-tool/common/argparse.h
Normal file
|
|
@ -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__
|
||||
20
security/nss/nss-tool/common/tool.h
Normal file
20
security/nss/nss-tool/common/tool.h
Normal file
|
|
@ -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__
|
||||
216
security/nss/nss-tool/common/util.cc
Normal file
216
security/nss/nss-tool/common/util.cc
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
/* 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;
|
||||
}
|
||||
|
||||
std::istream &GetStreamFromFileOrStdin(std::string &path, std::ifstream &ifs) {
|
||||
if (path.empty()) {
|
||||
return std::cin;
|
||||
}
|
||||
|
||||
ifs.open(path, std::ifstream::binary);
|
||||
if (!ifs.good()) {
|
||||
std::cerr << "IO Error when opening " << path << std::endl;
|
||||
std::cerr << "Input file does not exist or you don't have permissions."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
return ifs;
|
||||
}
|
||||
32
security/nss/nss-tool/common/util.h
Normal file
32
security/nss/nss-tool/common/util.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* 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);
|
||||
std::istream &GetStreamFromFileOrStdin(std::string &path, std::ifstream &ifs);
|
||||
|
||||
#endif // util_h__
|
||||
497
security/nss/nss-tool/db/dbtool.cc
Normal file
497
security/nss/nss-tool/db/dbtool.cc
Normal file
|
|
@ -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;
|
||||
}
|
||||
28
security/nss/nss-tool/db/dbtool.h
Normal file
28
security/nss/nss-tool/db/dbtool.h
Normal file
|
|
@ -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__
|
||||
161
security/nss/nss-tool/digest/digesttool.cc
Normal file
161
security/nss/nss-tool/digest/digesttool.cc
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/* 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 "digesttool.h"
|
||||
#include "argparse.h"
|
||||
#include "scoped_ptrs.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
|
||||
#include <hasht.h> // contains supported digest types
|
||||
#include <nss.h>
|
||||
#include <pk11pub.h>
|
||||
#include <prio.h>
|
||||
|
||||
static SECOidData* HashTypeToOID(HASH_HashType hashtype) {
|
||||
SECOidTag hashtag;
|
||||
|
||||
if (hashtype <= HASH_AlgNULL || hashtype >= HASH_AlgTOTAL) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
switch (hashtype) {
|
||||
case HASH_AlgMD5:
|
||||
hashtag = SEC_OID_MD5;
|
||||
break;
|
||||
case HASH_AlgSHA1:
|
||||
hashtag = SEC_OID_SHA1;
|
||||
break;
|
||||
case HASH_AlgSHA224:
|
||||
hashtag = SEC_OID_SHA224;
|
||||
break;
|
||||
case HASH_AlgSHA256:
|
||||
hashtag = SEC_OID_SHA256;
|
||||
break;
|
||||
case HASH_AlgSHA384:
|
||||
hashtag = SEC_OID_SHA384;
|
||||
break;
|
||||
case HASH_AlgSHA512:
|
||||
hashtag = SEC_OID_SHA512;
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return SECOID_FindOIDByTag(hashtag);
|
||||
}
|
||||
|
||||
static SECOidData* HashNameToOID(const std::string& hashName) {
|
||||
for (size_t htype = HASH_AlgNULL + 1; htype < HASH_AlgTOTAL; htype++) {
|
||||
SECOidData* hashOID = HashTypeToOID(static_cast<HASH_HashType>(htype));
|
||||
if (hashOID && std::string(hashOID->desc) == hashName) {
|
||||
return hashOID;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool Digest(const ArgParser& parser, SECOidData* hashOID);
|
||||
static bool ComputeDigest(std::istream& is, ScopedPK11Context& hashCtx);
|
||||
|
||||
bool DigestTool::Run(const std::vector<std::string>& arguments) {
|
||||
ArgParser parser(arguments);
|
||||
|
||||
if (parser.GetPositionalArgumentCount() != 1) {
|
||||
Usage();
|
||||
return false;
|
||||
}
|
||||
|
||||
// no need for a db for the digest tool
|
||||
SECStatus rv = NSS_NoDB_Init(".");
|
||||
if (rv != SECSuccess) {
|
||||
std::cerr << "NSS init failed!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string hashName = parser.GetPositionalArgument(0);
|
||||
std::transform(hashName.begin(), hashName.end(), hashName.begin(), ::toupper);
|
||||
SECOidData* hashOID = HashNameToOID(hashName);
|
||||
if (hashOID == nullptr) {
|
||||
std::cerr << "Error: Unknown digest type "
|
||||
<< parser.GetPositionalArgument(0) << "." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ret = Digest(parser, hashOID);
|
||||
|
||||
// shutdown nss
|
||||
if (NSS_Shutdown() != SECSuccess) {
|
||||
std::cerr << "NSS Shutdown failed!" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void DigestTool::Usage() {
|
||||
std::cerr << "Usage: nss digest md5|sha-1|sha-224|sha-256|sha-384|sha-512 "
|
||||
"[--infile <path>]"
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
static bool Digest(const ArgParser& parser, SECOidData* hashOID) {
|
||||
std::string inputFile;
|
||||
if (parser.Has("--infile")) {
|
||||
inputFile = parser.Get("--infile");
|
||||
}
|
||||
|
||||
ScopedPK11Context hashCtx(PK11_CreateDigestContext(hashOID->offset));
|
||||
if (hashCtx == nullptr) {
|
||||
std::cerr << "Creating digest context failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
PK11_DigestBegin(hashCtx.get());
|
||||
|
||||
std::ifstream fis;
|
||||
std::istream& is = GetStreamFromFileOrStdin(inputFile, fis);
|
||||
if (!is.good() || !ComputeDigest(is, hashCtx)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
unsigned char digest[HASH_LENGTH_MAX];
|
||||
unsigned int len;
|
||||
SECStatus rv = PK11_DigestFinal(hashCtx.get(), digest, &len, HASH_LENGTH_MAX);
|
||||
if (rv != SECSuccess || len == 0) {
|
||||
std::cerr << "Calculating final hash value failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
// human readable output
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
std::cout << std::setw(2) << std::setfill('0') << std::hex
|
||||
<< static_cast<int>(digest[i]);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool ComputeDigest(std::istream& is, ScopedPK11Context& hashCtx) {
|
||||
while (is) {
|
||||
unsigned char buf[4096];
|
||||
is.read(reinterpret_cast<char*>(buf), sizeof(buf));
|
||||
if (is.fail() && !is.eof()) {
|
||||
std::cerr << "Error reading from input stream." << std::endl;
|
||||
return false;
|
||||
}
|
||||
SECStatus rv = PK11_DigestOp(hashCtx.get(), buf, is.gcount());
|
||||
if (rv != SECSuccess) {
|
||||
std::cerr << "PK11_DigestOp failed." << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
20
security/nss/nss-tool/digest/digesttool.h
Normal file
20
security/nss/nss-tool/digest/digesttool.h
Normal file
|
|
@ -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 digest_tool_h__
|
||||
#define digest_tool_h__
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include "tool.h"
|
||||
|
||||
class DigestTool : public Tool {
|
||||
public:
|
||||
bool Run(const std::vector<std::string>& arguments) override;
|
||||
|
||||
private:
|
||||
void Usage() override;
|
||||
};
|
||||
|
||||
#endif // digest_tool_h__
|
||||
464
security/nss/nss-tool/enc/enctool.cc
Normal file
464
security/nss/nss-tool/enc/enctool.cc
Normal file
|
|
@ -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;
|
||||
}
|
||||
62
security/nss/nss-tool/enc/enctool.h
Normal file
62
security/nss/nss-tool/enc/enctool.h
Normal file
|
|
@ -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__
|
||||
70
security/nss/nss-tool/nss_tool.cc
Normal file
70
security/nss/nss-tool/nss_tool.cc
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* 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 "digest/digesttool.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;
|
||||
std::cerr << " nss digest <options>" << std::endl;
|
||||
}
|
||||
|
||||
static const std::string kDbCommand = "db";
|
||||
static const std::string kEncryptCommand = "encrypt";
|
||||
static const std::string kDecryptCommand = "decrypt";
|
||||
static const std::string kDigestCommand = "digest";
|
||||
|
||||
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 (argv[1] == kDigestCommand) {
|
||||
tool = std::unique_ptr<Tool>(new DigestTool());
|
||||
}
|
||||
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;
|
||||
}
|
||||
31
security/nss/nss-tool/nss_tool.gyp
Normal file
31
security/nss/nss-tool/nss_tool.gyp
Normal file
|
|
@ -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/.
|
||||
{
|
||||
'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',
|
||||
'digest/digesttool.cc'
|
||||
],
|
||||
'include_dirs': [
|
||||
'common',
|
||||
],
|
||||
'dependencies' : [
|
||||
'<(DEPTH)/cpputil/cpputil.gyp:cpputil',
|
||||
'<(DEPTH)/exports.gyp:dbm_exports',
|
||||
'<(DEPTH)/exports.gyp:nss_exports',
|
||||
],
|
||||
}
|
||||
],
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue