diff --git a/CMakeLists.txt b/CMakeLists.txt index aafdad099..a5c4fe930 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,7 +32,8 @@ option(USENPM "Use npm to recompile ethereum.js if it was changed" OFF) option(PROFILING "Build in support for profiling" OFF) set(BUNDLE "none" CACHE STRING "Predefined bundle of software to build (none, full, user, tests, minimal).") -option(MINER "Build the miner component" ON) +option(MINER "Build the CLI miner component" ON) +option(ETHKEY "Build the CLI key manager component" ON) option(SOLIDITY "Build the Solidity language components" ON) option(SERPENT "Build the Serpent language components" ON) option(TOOLS "Build the tools components" ON) @@ -251,6 +252,17 @@ elseif (BUNDLE STREQUAL "user") set(NCURSES ${DECENT_PLATFORM}) set(TOOLS ON) set(TESTS OFF) +elseif (BUNDLE STREQUAL "wallet") + set(SERPENT OFF) + set(SOLIDITY OFF) + set(USENPM OFF) + set(GUI OFF) + set(NCURSES OFF) + set(TOOLS OFF) + set(TESTS OFF) + set(ETHKEY ON) + set(MINER OFF) + set(ETHASHCL ON) elseif (BUNDLE STREQUAL "miner") set(SERPENT OFF) set(SOLIDITY OFF) @@ -259,6 +271,7 @@ elseif (BUNDLE STREQUAL "miner") set(NCURSES OFF) set(TOOLS OFF) set(TESTS OFF) + set(ETHKEY OFF) set(MINER ON) set(ETHASHCL ON) endif () @@ -296,6 +309,7 @@ message("-- JSONRPC JSON-RPC support ${JSONRPC} message("-- USENPM Javascript source building ${USENPM}") message("------------------------------------------------------------- components") message("-- MINER Build miner ${MINER}") +message("-- ETHKEY Build wallet tools ${ETHKEY}") message("-- TOOLS Build basic tools ${TOOLS}") message("-- SOLIDITY Build Solidity language components ${SOLIDITY}") message("-- SERPENT Build Serpent language components ${SERPENT}") @@ -364,6 +378,7 @@ if (JSCONSOLE) endif () add_subdirectory(secp256k1) +add_subdirectory(libscrypt) add_subdirectory(libdevcrypto) if (GENERAL) @@ -386,10 +401,14 @@ if (GENERAL) add_subdirectory(libwebthree) endif () -if (MINER) +if (MINER OR TOOLS) add_subdirectory(ethminer) endif () +if (ETHKEY OR TOOLS) + add_subdirectory(ethkey) +endif () + if (TESTS) add_subdirectory(libtestutils) add_subdirectory(test) diff --git a/alethzero/CMakeLists.txt b/alethzero/CMakeLists.txt index 41d9ea10f..595668cd1 100644 --- a/alethzero/CMakeLists.txt +++ b/alethzero/CMakeLists.txt @@ -23,6 +23,7 @@ qt5_wrap_ui(ui_Connect.h Connect.ui) qt5_wrap_ui(ui_Debugger.h Debugger.ui) qt5_wrap_ui(ui_Transact.h Transact.ui) qt5_wrap_ui(ui_ExportState.h ExportState.ui) +qt5_wrap_ui(ui_GetPassword.h GetPassword.ui) file(GLOB HEADERS "*.h") @@ -35,7 +36,7 @@ endif () # eth_add_executable is defined in cmake/EthExecutableHelper.cmake eth_add_executable(${EXECUTABLE} ICON alethzero - UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui + UI_RESOURCES alethzero.icns Main.ui Connect.ui Debugger.ui Transact.ui ExportState.ui GetPassword.ui WIN_RESOURCES alethzero.rc ) diff --git a/alethzero/GetPassword.ui b/alethzero/GetPassword.ui new file mode 100644 index 000000000..753bca565 --- /dev/null +++ b/alethzero/GetPassword.ui @@ -0,0 +1,123 @@ + + + GetPassword + + + + 0 + 0 + 400 + 187 + + + + Enter Password + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + Qt::RichText + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + QLineEdit::Password + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + GetPassword + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + GetPassword + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff --git a/alethzero/Main.ui b/alethzero/Main.ui index b5e22409d..ecdc07ab6 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -157,7 +157,10 @@ + + + @@ -1656,7 +1659,7 @@ font-size: 14pt &Enable Local Addresses - + true @@ -1745,6 +1748,21 @@ font-size: 14pt Co&nfirm Transactions + + + Import &Secret Key File... + + + + + &Re-Encrypt Key + + + + + Re-Encrypt All Keys... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 07f48c0bc..6a11736a4 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -75,6 +75,7 @@ #include "WebPage.h" #include "ExportState.h" #include "ui_Main.h" +#include "ui_GetPassword.h" using namespace std; using namespace dev; using namespace dev::p2p; @@ -467,7 +468,9 @@ void Main::load(QString _s) void Main::on_newTransaction_triggered() { m_transact.setEnvironment(m_keyManager.accounts(), ethereum(), &m_natSpecDB); - m_transact.exec(); + m_transact.setWindowFlags(Qt::Dialog); + m_transact.setWindowModality(Qt::WindowModal); + m_transact.show(); } void Main::on_loadJS_triggered() @@ -698,12 +701,17 @@ Secret Main::retrieveSecret(Address const& _a) const auto info = m_keyManager.accountDetails()[_a]; while (true) { - if (Secret s = m_keyManager.secret(_a, [&](){ - return QInputDialog::getText(const_cast(this), "Import Account Key", QString("Enter the password for the account %2 (%1). The hint is:\n%3").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first)).arg(QString::fromStdString(info.second)), QLineEdit::Password).toStdString(); - })) + Secret s = m_keyManager.secret(_a, [&](){ + QDialog d; + Ui_GetPassword gp; + gp.setupUi(&d); + d.setWindowTitle("Unlock Account"); + gp.label->setText(QString("Enter the password for the account %2 (%1).").arg(QString::fromStdString(_a.abridged())).arg(QString::fromStdString(info.first))); + gp.entry->setPlaceholderText("Hint: " + QString::fromStdString(info.second)); + return d.exec() == QDialog::Accepted ? gp.entry->text().toStdString() : string(); + }); + if (s || QMessageBox::warning(nullptr, "Unlock Account", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) return s; - else if (QMessageBox::warning(const_cast(this), "Incorrect Password", "The password you gave is incorrect for this key.", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) - return Secret(); } } @@ -771,17 +779,32 @@ void Main::readSettings(bool _skipGeometry) on_urlEdit_returnPressed(); } -std::string Main::getPassword(std::string const& _title, std::string const& _for) +std::string Main::getPassword(std::string const& _title, std::string const& _for, std::string* _hint, bool* _ok) { QString password; while (true) { - password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString()); + bool ok; + password = QInputDialog::getText(nullptr, QString::fromStdString(_title), QString::fromStdString(_for), QLineEdit::Password, QString(), &ok); + if (!ok) + { + if (_ok) + *_ok = false; + return string(); + } + if (password.isEmpty()) + break; QString confirm = QInputDialog::getText(nullptr, QString::fromStdString(_title), "Confirm this password by typing it again", QLineEdit::Password, QString()); if (password == confirm) break; QMessageBox::warning(nullptr, QString::fromStdString(_title), "You entered two different passwords - please enter the same password twice.", QMessageBox::Ok); } + + if (!password.isEmpty() && _hint && !m_keyManager.haveHint(password.toStdString())) + *_hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString(); + + if (_ok) + *_ok = true; return password.toStdString(); } @@ -797,8 +820,11 @@ void Main::on_importKey_triggered() QString s = QInputDialog::getText(this, "Import Account Key", "Enter this account's name"); if (QMessageBox::question(this, "Additional Security?", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!"); - std::string hint = QInputDialog::getText(this, "Import Account Key", "Enter a hint to help you remember this password.").toStdString(); + bool ok; + std::string hint; + std::string password = getPassword("Import Account Key", "Enter the password you would like to use for this key. Don't forget it!", &hint, &ok); + if (!ok) + return; m_keyManager.import(k.secret(), s.toStdString(), password, hint); } else @@ -813,6 +839,33 @@ void Main::on_importKey_triggered() } void Main::on_importKeyFile_triggered() +{ + QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); + h128 uuid = m_keyManager.store().importKey(s.toStdString()); + if (!uuid) + { + QMessageBox::warning(this, "Key File Invalid", "Could not find secret key definition. This is probably not an Web3 key file."); + return; + } + + QString info = QInputDialog::getText(this, "Import Key File", "Enter a description of this key to help you recognise it in the future."); + + QString pass; + for (Secret s; !s;) + { + s = Secret(m_keyManager.store().secret(uuid, [&](){ + pass = QInputDialog::getText(this, "Import Key File", "Enter the password for the key to complete the import.", QLineEdit::Password); + return pass.toStdString(); + }, false)); + if (!s && QMessageBox::question(this, "Import Key File", "The password you provided is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + + QString hint = QInputDialog::getText(this, "Import Key File", "Enter a hint for this password to help you remember it."); + m_keyManager.importExisting(uuid, info.toStdString(), pass.toStdString(), hint.toStdString()); +} + +void Main::on_claimPresale_triggered() { QString s = QFileDialog::getOpenFileName(this, "Claim Account Contents", QDir::homePath(), "JSON Files (*.json);;All Files (*)"); try @@ -1976,8 +2029,11 @@ void Main::on_newAccount_triggered() QString s = QInputDialog::getText(this, "Create Account", "Enter this account's name"); if (QMessageBox::question(this, "Create Account", "Would you like to use additional security for this key? This lets you protect it with a different password to other keys, but also means you must re-enter the key's password every time you wish to use the account.", QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes) { - std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!"); - std::string hint = QInputDialog::getText(this, "Create Account", "Enter a hint to help you remember this password.").toStdString(); + bool ok = false; + std::string hint; + std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!", &hint, &ok); + if (!ok) + return; m_keyManager.import(p.secret(), s.toStdString(), password, hint); } else @@ -1989,15 +2045,14 @@ void Main::on_killAccount_triggered() { if (ui->ourAccounts->currentRow() >= 0) { - auto hba = ui->accounts->currentItem()->data(Qt::UserRole).toByteArray(); + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); Address h((byte const*)hba.data(), Address::ConstructFromPointer); auto k = m_keyManager.accountDetails()[h]; - if ( - ethereum()->balanceAt(h) != 0 && - QMessageBox::critical(this, QString::fromStdString("Kill Account " + k.first + "?!"), - QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it. It, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" - "Are you sure you want to continue?"), - QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) + QString s = QInputDialog::getText(this, QString::fromStdString("Kill Account " + k.first + "?!"), + QString::fromStdString("Account " + k.first + " (" + render(h) + ") has " + formatBalance(ethereum()->balanceAt(h)) + " in it.\r\nIt, and any contract that this account can access, will be lost forever if you continue. Do NOT continue unless you know what you are doing.\n" + "Are you sure you want to continue? \r\n If so, type 'YES' to confirm."), + QLineEdit::Normal, "NO"); + if (s != "YES") return; m_keyManager.kill(h); if (m_keyManager.accounts().empty()) @@ -2009,6 +2064,57 @@ void Main::on_killAccount_triggered() } } +void Main::on_reencryptKey_triggered() +{ + if (ui->ourAccounts->currentRow() >= 0) + { + auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); + Address a((byte const*)hba.data(), Address::ConstructFromPointer); + QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"}; + bool ok = true; + KDF kdf = (KDF)kdfs.indexOf(QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your key:", kdfs, kdfs.size() - 1, false, &ok)); + if (!ok) + return; + std::string hint; + std::string password = getPassword("Create Account", "Enter the password you would like to use for this key. Don't forget it!\nEnter nothing to use your Master password.", &hint, &ok); + if (!ok) + return; + try { + auto pw = [&](){ + auto p = QInputDialog::getText(this, "Re-Encrypt Key", "Enter the original password for this key.\nHint: " + QString::fromStdString(m_keyManager.hint(a)), QLineEdit::Password, QString()).toStdString(); + if (p.empty()) + throw UnknownPassword(); + return p; + }; + while (!(password.empty() ? m_keyManager.recode(a, SemanticPassword::Master, pw, kdf) : m_keyManager.recode(a, password, hint, pw, kdf))) + if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + catch (UnknownPassword&) {} + } +} + +void Main::on_reencryptAll_triggered() +{ + QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"}; + bool ok = false; + QString kdf = QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your keys:", kdfs, kdfs.size() - 1, false, &ok); + if (!ok) + return; + try { + for (Address const& a: m_keyManager.accounts()) + while (!m_keyManager.recode(a, SemanticPassword::Existing, [&](){ + auto p = QInputDialog::getText(nullptr, "Re-Encrypt Key", QString("Enter the original password for key %1.\nHint: %2").arg(QString::fromStdString(pretty(a))).arg(QString::fromStdString(m_keyManager.hint(a))), QLineEdit::Password, QString()).toStdString(); + if (p.empty()) + throw UnknownPassword(); + return p; + }, (KDF)kdfs.indexOf(kdf))) + if (QMessageBox::question(this, "Re-Encrypt Key", "Password given is incorrect. Would you like to try again?", QMessageBox::Retry, QMessageBox::Cancel) == QMessageBox::Cancel) + return; + } + catch (UnknownPassword&) {} +} + void Main::on_go_triggered() { if (!ui->net->isChecked()) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 9f2082dd0..29cd0dbf3 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -33,9 +33,9 @@ #include #include #include +#include #include #include -#include #include #include #include "Context.h" @@ -137,7 +137,10 @@ private slots: void on_newAccount_triggered(); void on_killAccount_triggered(); void on_importKey_triggered(); + void on_reencryptKey_triggered(); + void on_reencryptAll_triggered(); void on_importKeyFile_triggered(); + void on_claimPresale_triggered(); void on_exportKey_triggered(); // Account pane @@ -246,7 +249,7 @@ private: void refreshBalances(); void setBeneficiary(dev::Address const& _b); - std::string getPassword(std::string const& _title, std::string const& _for); + std::string getPassword(std::string const& _title, std::string const& _for, std::string* _hint = nullptr, bool* _ok = nullptr); std::unique_ptr ui; diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index 1336c3f05..b485091d9 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -39,7 +39,8 @@ #include #include #include -#include +#include + #if ETH_SERPENT #include #include @@ -76,6 +77,7 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e m_ethereum = _eth; m_natSpecDB = _natSpecDB; + auto old = ui->from->currentIndex(); ui->from->clear(); for (auto const& i: m_accounts) { @@ -84,6 +86,10 @@ void Transact::setEnvironment(AddressHash const& _accounts, dev::eth::Client* _e QString s = QString("%4 %2: %1").arg(formatBalance(b).c_str()).arg(QString::fromStdString(m_context->render(i))).arg(QString::fromStdString(d.first)); ui->from->addItem(s); } + if (old > -1 && old < ui->from->count()) + ui->from->setCurrentIndex(old); + else if (ui->from->count()) + ui->from->setCurrentIndex(0); } bool Transact::isCreation() const @@ -301,6 +307,9 @@ void Transact::rejigData() // Determine how much balance we have to play with... //findSecret(value() + ethereum()->gasLimitRemaining() * gasPrice()); auto s = fromAccount(); + if (!s) + return; + auto b = ethereum()->balanceAt(s, PendingBlock); m_allGood = true; @@ -344,7 +353,7 @@ void Transact::rejigData() if (b < value() + baseGas * gasPrice()) { // Not enough - bail. - bail("
ERROR No single account contains enough for paying even the basic amount of gas required.
"); + bail("
ERROR Account doesn't contain enough for paying even the basic amount of gas required.
"); return; } else @@ -417,6 +426,8 @@ Secret Transact::findSecret(u256 _totalReq) const Address Transact::fromAccount() { + if (ui->from->currentIndex() < 0 || ui->from->currentIndex() >= (int)m_accounts.size()) + return Address(); auto it = m_accounts.begin(); std::advance(it, ui->from->currentIndex()); return *it; @@ -425,14 +436,19 @@ Address Transact::fromAccount() void Transact::on_send_clicked() { // Secret s = findSecret(value() + fee()); - Secret s = m_context->retrieveSecret(fromAccount()); - auto b = ethereum()->balanceAt(KeyPair(s).address(), PendingBlock); - if (!s || b < value() + fee()) + auto a = fromAccount(); + auto b = ethereum()->balanceAt(a, PendingBlock); + + if (!a || b < value() + fee()) { - QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); + QMessageBox::critical(nullptr, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount.", QMessageBox::Ok); return; } + Secret s = m_context->retrieveSecret(a); + if (!s) + return; + if (isCreation()) { // If execution is a contract creation, add Natspec to @@ -467,7 +483,7 @@ void Transact::on_debug_clicked() auto b = ethereum()->balanceAt(from, PendingBlock); if (!from || b < value() + fee()) { - QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: no single account contains at least the required amount."); + QMessageBox::critical(this, "Transaction Failed", "Couldn't make transaction: account doesn't contain at least the required amount."); return; } diff --git a/alethzero/Transact.h b/alethzero/Transact.h index cd62c0e20..c14fcc7e1 100644 --- a/alethzero/Transact.h +++ b/alethzero/Transact.h @@ -44,12 +44,13 @@ public: void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); private slots: + void on_from_currentIndexChanged(int) { rejigData(); rejigData(); } void on_destination_currentTextChanged(QString); - void on_value_valueChanged(int) { updateFee(); } - void on_gas_valueChanged(int) { updateFee(); } - void on_valueUnits_currentIndexChanged(int) { updateFee(); } - void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); } - void on_gasPrice_valueChanged(int) { updateFee(); } + void on_value_valueChanged(int) { updateFee(); rejigData(); } + void on_gas_valueChanged(int) { updateFee(); rejigData(); } + void on_valueUnits_currentIndexChanged(int) { updateFee(); rejigData(); } + void on_gasPriceUnits_currentIndexChanged(int) { updateFee(); rejigData(); } + void on_gasPrice_valueChanged(int) { updateFee(); rejigData(); } void on_data_textChanged() { rejigData(); } void on_optimize_clicked() { rejigData(); } void on_send_clicked(); diff --git a/eth/main.cpp b/eth/main.cpp index 33d049843..a57928e72 100644 --- a/eth/main.cpp +++ b/eth/main.cpp @@ -37,7 +37,8 @@ #include #include #include -#include +#include + #include #if ETH_JSCONSOLE || !ETH_TRUE #include diff --git a/ethkey/CMakeLists.txt b/ethkey/CMakeLists.txt new file mode 100644 index 000000000..5575acbd0 --- /dev/null +++ b/ethkey/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_policy(SET CMP0015 NEW) +set(CMAKE_AUTOMOC OFF) + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) +include_directories(${Boost_INCLUDE_DIRS}) +include_directories(${JSON_RPC_CPP_INCLUDE_DIRS}) + +if (JSCONSOLE) + include_directories(${V8_INCLUDE_DIRS}) +endif() + +set(EXECUTABLE ethkey) + +file(GLOB HEADERS "*.h") + +add_executable(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +add_dependencies(${EXECUTABLE} BuildInfo.h) + +target_link_libraries(${EXECUTABLE} devcrypto) +target_link_libraries(${EXECUTABLE} ethcore) + +if (DEFINED WIN32 AND NOT DEFINED CMAKE_COMPILER_IS_MINGW) + eth_copy_dlls("${EXECUTABLE}" MHD_DLLS) +endif() + +if (APPLE) + install(TARGETS ${EXECUTABLE} DESTINATION bin) +else() + eth_install_executable(${EXECUTABLE}) +endif() diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h new file mode 100644 index 000000000..df2ba5a5a --- /dev/null +++ b/ethkey/KeyAux.h @@ -0,0 +1,413 @@ +#pragma once + +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file KeyAux.cpp + * @author Gav Wood + * @date 2014 + * CLI module for key management. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +using namespace std; +using namespace dev; +using namespace dev::eth; +using namespace boost::algorithm; + +#undef RETURN + +class BadArgument: public Exception {}; + +string getAccountPassword(KeyManager& keyManager, Address const& a) +{ + return getPassword("Enter password for address " + keyManager.accountDetails()[a].first + " (" + a.abridged() + "; hint:" + keyManager.accountDetails()[a].second + "): "); +} + +string createPassword(std::string const& _prompt) +{ + string ret; + while (true) + { + ret = getPassword(_prompt); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (ret == confirm) + break; + cout << "Passwords were different. Try again." << endl; + } + return ret; +// cout << "Enter a hint to help you remember this password: " << flush; +// cin >> hint; +// return make_pair(ret, hint); +} + +pair createPassword(KeyManager& _keyManager, std::string const& _prompt, std::string const& _pass = std::string(), std::string const& _hint = std::string()) +{ + string pass = _pass; + if (pass.empty()) + while (true) + { + pass = getPassword(_prompt); + string confirm = getPassword("Please confirm the password by entering it again: "); + if (pass == confirm) + break; + cout << "Passwords were different. Try again." << endl; + } + string hint = _hint; + if (hint.empty() && !pass.empty() && !_keyManager.haveHint(pass)) + { + cout << "Enter a hint to help you remember this password: " << flush; + getline(cin, hint); + } + return make_pair(pass, hint); +} + +class KeyCLI +{ +public: + enum class OperationMode + { + None, + ListBare, + NewBare, + ImportBare, + ExportBare, + RecodeBare, + KillBare, + CreateWallet, + List, + New, + Import, + Export, + Recode, + Kill + }; + + KeyCLI(OperationMode _mode = OperationMode::None): m_mode(_mode) {} + + bool interpretOption(int& i, int argc, char** argv) + { + string arg = argv[i]; + if (arg == "--wallet-path" && i + 1 < argc) + m_walletPath = argv[++i]; + else if (arg == "--secrets-path" && i + 1 < argc) + m_secretsPath = argv[++i]; + else if ((arg == "-m" || arg == "--master") && i + 1 < argc) + m_masterPassword = argv[++i]; + else if (arg == "--unlock" && i + 1 < argc) + m_unlocks.push_back(argv[++i]); + else if (arg == "--lock" && i + 1 < argc) + m_lock = argv[++i]; + else if (arg == "--kdf" && i + 1 < argc) + m_kdf = argv[++i]; + else if (arg == "--kdf-param" && i + 2 < argc) + { + auto n = argv[++i]; + auto v = argv[++i]; + m_kdfParams[n] = v; + } + else if (arg == "--new-bare") + m_mode = OperationMode::NewBare; + else if (arg == "--import-bare") + m_mode = OperationMode::ImportBare; + else if (arg == "--list-bare") + m_mode = OperationMode::ListBare; + else if (arg == "--export-bare") + m_mode = OperationMode::ExportBare; + else if (arg == "--recode-bare") + m_mode = OperationMode::RecodeBare; + else if (arg == "--kill-bare") + m_mode = OperationMode::KillBare; + else if (arg == "--create-wallet") + m_mode = OperationMode::CreateWallet; + else if (arg == "--list") + m_mode = OperationMode::List; + else if ((arg == "-n" || arg == "--new") && i + 1 < argc) + { + m_mode = OperationMode::New; + m_name = argv[++i]; + } + else if ((arg == "-i" || arg == "--import") && i + 2 < argc) + { + m_mode = OperationMode::Import; + m_inputs = strings(1, argv[++i]); + m_name = argv[++i]; + } + else if (arg == "--export") + m_mode = OperationMode::Export; + else if (arg == "--recode") + m_mode = OperationMode::Recode; + else if (arg == "--no-icap") + m_icap = false; + else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::KillBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) + m_inputs.push_back(arg); + else + return false; + return true; + } + + KeyPair makeKey() const + { + KeyPair k(Secret::random()); + while (m_icap && k.address()[0]) + k = KeyPair(sha3(k.secret())); + return k; + } + + void execute() + { + if (m_mode == OperationMode::CreateWallet) + { + KeyManager wallet(m_walletPath, m_secretsPath); + if (m_masterPassword.empty()) + m_masterPassword = createPassword("Please enter a MASTER password to protect your key store (make it strong!): "); + if (m_masterPassword.empty()) + cerr << "Aborted (empty password not allowed)." << endl; + else + wallet.create(m_masterPassword); + } + else if (m_mode < OperationMode::CreateWallet) + { + SecretStore store(m_secretsPath); + switch (m_mode) + { + case OperationMode::ListBare: + for (h128 const& u: std::set() + store.keys()) + cout << toUUID(u) << endl; + break; + case OperationMode::NewBare: + { + if (m_lock.empty()) + m_lock = createPassword("Enter a password with which to secure this account: "); + auto k = makeKey(); + h128 u = store.importSecret(k.secret().asBytes(), m_lock); + cout << "Created key " << toUUID(u) << endl; + cout << " Address: " << k.address().hex() << endl; + cout << " ICAP: " << ICAP(k.address()).encoded() << endl; + break; + } + case OperationMode::ImportBare: + for (string const& i: m_inputs) + { + h128 u; + bytes b; + b = fromHex(i); + if (b.size() != 32) + { + std::string s = contentsString(i); + b = fromHex(s); + if (b.size() != 32) + u = store.importKey(i); + } + if (!u && b.size() == 32) + u = store.importSecret(b, lockPassword(toAddress(Secret(b)).abridged())); + else + { + cerr << "Cannot import " << i << " not a file or secret." << endl; + continue; + } + cout << "Successfully imported " << i << " as " << toUUID(u); + } + break; + case OperationMode::ExportBare: break; + case OperationMode::RecodeBare: + for (auto const& i: m_inputs) + { + h128 u = fromUUID(i); + if (u) + if (store.recode(u, lockPassword(toUUID(u)), [&](){ return getPassword("Enter password for key " + toUUID(u) + ": "); }, kdf())) + cerr << "Re-encoded " << toUUID(u) << endl; + else + cerr << "Couldn't re-encode " << toUUID(u) << "; key corrupt or incorrect password supplied." << endl; + else + cerr << "Couldn't re-encode " << toUUID(u) << "; not found." << endl; + } + case OperationMode::KillBare: + for (auto const& i: m_inputs) + { + h128 u = fromUUID(i); + if (u) + store.kill(u); + else + cerr << "Couldn't kill " << toUUID(u) << "; not found." << endl; + } + break; + default: break; + } + } + else + { + KeyManager wallet(m_walletPath, m_secretsPath); + if (wallet.exists()) + while (true) + { + if (wallet.load(m_masterPassword)) + break; + if (!m_masterPassword.empty()) + { + cout << "Password invalid. Try again." << endl; + m_masterPassword.clear(); + } + m_masterPassword = getPassword("Please enter your MASTER password: "); + } + else + { + cerr << "Couldn't open wallet. Does it exist?" << endl; + exit(-1); + } + switch (m_mode) + { + case OperationMode::New: + { + tie(m_lock, m_lockHint) = createPassword(wallet, "Enter a password with which to secure this account (or nothing to use the master password): ", m_lock, m_lockHint); + auto k = makeKey(); + bool usesMaster = m_lock.empty(); + h128 u = usesMaster ? wallet.import(k.secret(), m_name) : wallet.import(k.secret(), m_name, m_lock, m_lockHint); + cout << "Created key " << toUUID(u) << endl; + cout << " Name: " << m_name << endl; + if (usesMaster) + cout << " Uses master password." << endl; + else + cout << " Password hint: " << m_lockHint << endl; + cout << " Address: " << k.address().hex() << endl; + cout << " ICAP: " << ICAP(k.address()).encoded() << endl; + break; + } + case OperationMode::List: + { + vector bare; + vector nonIcap; + for (auto const& u: wallet.store().keys()) + if (Address a = wallet.address(u)) + if (a[0]) + nonIcap.push_back(u); + else + { + std::pair info = wallet.accountDetails()[a]; + cout << toUUID(u) << " " << a.abridged(); + cout << " " << ICAP(a).encoded(); + cout << " " << info.first << endl; + } + else + bare.push_back(u); + for (auto const& u: nonIcap) + if (Address a = wallet.address(u)) + { + std::pair info = wallet.accountDetails()[a]; + cout << toUUID(u) << " " << a.abridged(); + cout << " (Not ICAP) "; + cout << " " << info.first << endl; + } + for (auto const& u: bare) + cout << toUUID(u) << " (Bare)" << endl; + } + default: break; + } + } + } + + std::string lockPassword(std::string const& _accountName) + { + return m_lock.empty() ? createPassword("Enter a password with which to secure account " + _accountName + ": ") : m_lock; + } + + static void streamHelp(ostream& _out) + { + _out + << "Secret-store (\"bare\") operation modes:" << endl + << " --list-bare List all secret available in secret-store." << endl + << " --new-bare Generate and output a key without interacting with wallet and dump the JSON." << endl + << " --import-bare [ | , ... ] Import keys from given sources." << endl + << " --recode-bare [ | , ... ] Decrypt and re-encrypt given keys." << endl +// << " --export-bare [ , ... ] Export given keys." << endl + << " --kill-bare [ , ... ] Delete given keys." << endl + << "Secret-store configuration:" << endl + << " --secrets-path Specify Web3 secret-store path (default: " << SecretStore::defaultPath() << ")" << endl + << endl + << "Wallet operating modes:" << endl + << " -l,--list List all keys available in wallet." << endl + << " -n,--new Create a new key with given name and add it in the wallet." << endl + << " -i,--import [||] Import keys from given source and place in wallet." << endl + << " -e,--export [
| , ... ] Export given keys." << endl + << " -r,--recode [
|| , ... ] Decrypt and re-encrypt given keys." << endl + << "Wallet configuration:" << endl + << " --create-wallet Create an Ethereum master wallet." << endl + << " --wallet-path Specify Ethereum wallet path (default: " << KeyManager::defaultPath() << ")" << endl + << " -m, --master Specify wallet (master) password." << endl + << endl + << "Encryption configuration:" << endl + << " --kdf Specify KDF to use when encrypting (default: sc rypt)" << endl + << " --kdf-param Specify a parameter for the KDF." << endl +// << " --cipher Specify cipher to use when encrypting (default: aes-128-ctr)" << endl +// << " --cipher-param Specify a parameter for the cipher." << endl + << " --lock Specify password for when encrypting a (the) key." << endl + << " --hint Specify hint for the --lock password." << endl + << endl + << "Decryption configuration:" << endl + << " --unlock Specify password for a (the) key." << endl + << "Key generation configuration:" << endl + << " --no-icap Don't bother to make a direct-ICAP capable key." << endl + ; + } + + static bool isTrue(std::string const& _m) + { + return _m == "on" || _m == "yes" || _m == "true" || _m == "1"; + } + + static bool isFalse(std::string const& _m) + { + return _m == "off" || _m == "no" || _m == "false" || _m == "0"; + } + +private: + KDF kdf() const { return m_kdf == "pbkdf2" ? KDF::PBKDF2_SHA256 : KDF::Scrypt; } + + /// Operating mode. + OperationMode m_mode; + + /// Wallet stuff + string m_secretsPath = SecretStore::defaultPath(); + string m_walletPath = KeyManager::defaultPath(); + + /// Wallet password stuff + string m_masterPassword; + strings m_unlocks; + string m_lock; + string m_lockHint; + bool m_icap = true; + + /// Creating + string m_name; + + /// Importing + strings m_inputs; + + string m_kdf = "scrypt"; + map m_kdfParams; +// string m_cipher; +// map m_cipherParams; +}; diff --git a/ethkey/main.cpp b/ethkey/main.cpp new file mode 100644 index 000000000..53781a38a --- /dev/null +++ b/ethkey/main.cpp @@ -0,0 +1,84 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file main.cpp + * @author Gav Wood + * @date 2014 + * Ethereum client. + */ + +#include +#include +#include +#include +#include +#include +#include +#include "BuildInfo.h" +#include "KeyAux.h" +using namespace std; +using namespace dev; +using namespace dev::eth; + +void help() +{ + cout + << "Usage ethkey [OPTIONS]" << endl + << "Options:" << endl << endl; + KeyCLI::streamHelp(cout); + cout + << "General Options:" << endl + << " -v,--verbosity <0 - 9> Set the log verbosity from 0 to 9 (default: 8)." << endl + << " -V,--version Show the version and exit." << endl + << " -h,--help Show this help message and exit." << endl + ; + exit(0); +} + +void version() +{ + cout << "ethkey version " << dev::Version << endl; + cout << "Build: " << DEV_QUOTED(ETH_BUILD_PLATFORM) << "/" << DEV_QUOTED(ETH_BUILD_TYPE) << endl; + exit(0); +} + +int main(int argc, char** argv) +{ + KeyCLI m(KeyCLI::OperationMode::ListBare); + g_logVerbosity = 0; + + for (int i = 1; i < argc; ++i) + { + string arg = argv[i]; + if (m.interpretOption(i, argc, argv)) {} + else if ((arg == "-v" || arg == "--verbosity") && i + 1 < argc) + g_logVerbosity = atoi(argv[++i]); + else if (arg == "-h" || arg == "--help") + help(); + else if (arg == "-V" || arg == "--version") + version(); + else + { + cerr << "Invalid argument: " << arg << endl; + exit(-1); + } + } + + m.execute(); + + return 0; +} + diff --git a/ethminer/MinerAux.h b/ethminer/MinerAux.h index ef0621b7f..47fd2e2ae 100644 --- a/ethminer/MinerAux.h +++ b/ethminer/MinerAux.h @@ -16,10 +16,10 @@ You should have received a copy of the GNU General Public License along with cpp-ethereum. If not, see . */ -/** @file main.cpp +/** @file MinerAux.cpp * @author Gav Wood * @date 2014 - * Ethereum client. + * CLI module for mining. */ #include diff --git a/exp/main.cpp b/exp/main.cpp index 47efe576a..f0574fa7c 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -36,7 +36,6 @@ #include #include #include -/* #include #include #include @@ -50,24 +49,37 @@ #include #include #include -#include +#include + #include #include #include #include #include #include -#include */ +#include using namespace std; using namespace dev; -/*using namespace dev::eth; +using namespace dev::eth; using namespace dev::p2p; using namespace dev::shh; namespace js = json_spirit; namespace fs = boost::filesystem; -*/ + #if 1 +int main() +{ + cdebug << pbkdf2("password", asBytes("salt"), 1, 32); + cdebug << pbkdf2("password", asBytes("salt"), 1, 16); + cdebug << pbkdf2("password", asBytes("salt"), 2, 16); + cdebug << pbkdf2("testpassword", fromHex("de5742f1f1045c402296422cee5a8a9ecf0ac5bf594deca1170d22aef33a79cf"), 262144, 16); + return 0; +} + + +#elif 0 + int main() { cdebug << "EXP"; diff --git a/libdevcore/Common.h b/libdevcore/Common.h index ac4d89103..ae98861c1 100644 --- a/libdevcore/Common.h +++ b/libdevcore/Common.h @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #pragma warning(push) @@ -63,6 +64,8 @@ namespace dev extern char const* Version; +static const std::string EmptyString; + // Binary data types. using bytes = std::vector; using bytesRef = vector_ref; diff --git a/libdevcore/CommonData.h b/libdevcore/CommonData.h index 702f1f808..e1d8d7bdb 100644 --- a/libdevcore/CommonData.h +++ b/libdevcore/CommonData.h @@ -323,4 +323,13 @@ std::vector keysOf(std::map const& _m) return ret; } +template +std::vector keysOf(std::unordered_map const& _m) +{ + std::vector ret; + for (auto const& i: _m) + ret.push_back(i.first); + return ret; +} + } diff --git a/libdevcrypto/CMakeLists.txt b/libdevcrypto/CMakeLists.txt index 295dd257a..7df1149b0 100644 --- a/libdevcrypto/CMakeLists.txt +++ b/libdevcrypto/CMakeLists.txt @@ -22,6 +22,7 @@ add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) target_link_libraries(${EXECUTABLE} ${Boost_FILESYSTEM_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${LEVELDB_LIBRARIES}) target_link_libraries(${EXECUTABLE} ${CRYPTOPP_LIBRARIES}) +target_link_libraries(${EXECUTABLE} scrypt) target_link_libraries(${EXECUTABLE} devcore) install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) diff --git a/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 87e258573..814f8309e 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -119,10 +120,11 @@ std::pair dev::encryptSymNoAuth(h128 const& _k, bytesConstRef _plai return make_pair(encryptSymNoAuth(_k, iv, _plain), iv); } -bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) +bytes dev::encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain) { - const int c_aesKeyLen = 16; - SecByteBlock key(_k.data(), c_aesKeyLen); + if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32) + return bytes(); + SecByteBlock key(_k.data(), _k.size()); try { CTR_Mode::Encryption e; @@ -138,10 +140,11 @@ bytes dev::encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plai } } -bytes dev::decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) +bytes dev::decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher) { - const size_t c_aesKeyLen = 16; - SecByteBlock key(_k.data(), c_aesKeyLen); + if (_k.size() != 16 && _k.size() != 24 && _k.size() != 32) + return bytes(); + SecByteBlock key(_k.data(), _k.size()); try { CTR_Mode::Decryption d; @@ -180,6 +183,13 @@ bytes dev::pbkdf2(string const& _pass, bytes const& _salt, unsigned _iterations, return ret; } +bytes dev::scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen) +{ + bytes ret(_dkLen); + libscrypt_scrypt((uint8_t const*)_pass.data(), _pass.size(), _salt.data(), _salt.size(), _n, _r, _p, ret.data(), ret.size()); + return ret; +} + KeyPair KeyPair::create() { static boost::thread_specific_ptr s_eng; diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index 6464c7ede..10bcdd067 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -98,18 +98,26 @@ bool decryptSym(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); /// Encrypt payload using ECIES standard with AES128-CTR. void encryptECIES(Public const& _k, bytesConstRef _plain, bytes& o_cipher); - + /// Decrypt payload using ECIES standard with AES128-CTR. bool decryptECIES(Secret const& _k, bytesConstRef _cipher, bytes& o_plaintext); - + /// Encrypts payload with random IV/ctr using AES128-CTR. std::pair encryptSymNoAuth(h128 const& _k, bytesConstRef _plain); /// Encrypts payload with specified IV/ctr using AES128-CTR. -bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain); +bytes encryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _plain); /// Decrypts payload with specified IV/ctr using AES128-CTR. -bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher); +bytes decryptAES128CTR(bytesConstRef _k, h128 const& _iv, bytesConstRef _cipher); + +/// Encrypts payload with specified IV/ctr using AES128-CTR. +inline bytes encryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); } +inline bytes encryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _plain) { return encryptAES128CTR(_k.ref(), _iv, _plain); } + +/// Decrypts payload with specified IV/ctr using AES128-CTR. +inline bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); } +inline bytes decryptSymNoAuth(h256 const& _k, h128 const& _iv, bytesConstRef _cipher) { return decryptAES128CTR(_k.ref(), _iv, _cipher); } /// Recovers Public key from signed message hash. Public recover(Signature const& _sig, h256 const& _hash); @@ -123,6 +131,9 @@ bool verify(Public const& _k, Signature const& _s, h256 const& _hash); /// Derive key via PBKDF2. bytes pbkdf2(std::string const& _pass, bytes const& _salt, unsigned _iterations, unsigned _dkLen = 32); +/// Derive key via Scrypt. +bytes scrypt(std::string const& _pass, bytes const& _salt, uint64_t _n, uint32_t _r, uint32_t _p, unsigned _dkLen); + /// Simple class that represents a "key pair". /// All of the data of the class can be regenerated from the secret key (m_secret) alone. /// Actually stores a tuplet of secret, public and address (the right 160-bits of the public). diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 0416aa9f4..11ff98bf6 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -22,6 +22,7 @@ #include "SecretStore.h" #include #include +#include #include #include #include @@ -33,7 +34,57 @@ using namespace dev; namespace js = json_spirit; namespace fs = boost::filesystem; -SecretStore::SecretStore() +static const int c_keyFileVersion = 3; + +static js::mValue upgraded(std::string const& _s) +{ + js::mValue v; + js::read_string(_s, v); + if (v.type() != js::obj_type) + return js::mValue(); + js::mObject ret = v.get_obj(); + unsigned version = ret.count("Version") ? stoi(ret["Version"].get_str()) : ret.count("version") ? ret["version"].get_int() : 0; + if (version == 1) + { + // upgrade to version 2 + js::mObject old; + swap(old, ret); + + ret["id"] = old["Id"]; + js::mObject c; + c["ciphertext"] = old["Crypto"].get_obj()["CipherText"]; + c["cipher"] = "aes-128-cbc"; + { + js::mObject cp; + cp["iv"] = old["Crypto"].get_obj()["IV"]; + c["cipherparams"] = cp; + } + c["kdf"] = old["Crypto"].get_obj()["KeyHeader"].get_obj()["Kdf"]; + { + js::mObject kp; + kp["salt"] = old["Crypto"].get_obj()["Salt"]; + for (auto const& i: old["Crypto"].get_obj()["KeyHeader"].get_obj()["KdfParams"].get_obj()) + if (i.first != "SaltLen") + kp[boost::to_lower_copy(i.first)] = i.second; + c["kdfparams"] = kp; + } + c["sillymac"] = old["Crypto"].get_obj()["MAC"]; + c["sillymacjson"] = _s; + ret["crypto"] = c; + version = 2; + } + if (version == 2) + { + ret["crypto"].get_obj()["cipher"] = "aes-128-ctr"; + ret["crypto"].get_obj()["compat"] = "2"; + version = 3; + } + if (version == c_keyFileVersion) + return ret; + return js::mValue(); +} + +SecretStore::SecretStore(std::string const& _path): m_path(_path) { load(); } @@ -42,10 +93,11 @@ SecretStore::~SecretStore() { } -bytes SecretStore::secret(h128 const& _uuid, function const& _pass) const +bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const { + (void)_pass; auto rit = m_cached.find(_uuid); - if (rit != m_cached.end()) + if (_useCache && rit != m_cached.end()) return rit->second; auto it = m_keys.find(_uuid); if (it == m_keys.end()) @@ -93,7 +145,7 @@ void SecretStore::save(std::string const& _keysPath) js::read_string(k.second.first, crypto); v["crypto"] = crypto; v["id"] = uuid; - v["version"] = 2; + v["version"] = c_keyFileVersion; writeFile(filename, js::write_string(js::mValue(v), true)); if (!k.second.second.empty() && k.second.second != filename) boost::filesystem::remove(k.second.second); @@ -105,48 +157,88 @@ void SecretStore::load(std::string const& _keysPath) { fs::path p(_keysPath); boost::filesystem::create_directories(p); - js::mValue v; for (fs::directory_iterator it(p); it != fs::directory_iterator(); ++it) if (is_regular_file(it->path())) - { - cdebug << "Reading" << it->path(); - js::read_string(contentsString(it->path().string()), v); - if (v.type() == js::obj_type) - { - js::mObject o = v.get_obj(); - int version = o.count("Version") ? stoi(o["Version"].get_str()) : o.count("version") ? o["version"].get_int() : 0; - if (version == 2) - m_keys[fromUUID(o["id"].get_str())] = make_pair(js::write_string(o["crypto"], false), it->path().string()); - else - cwarn << "Cannot read key version" << version; - } -// else -// cwarn << "Invalid JSON in key file" << it->path().string(); - } + readKey(it->path().string(), true); +} + +h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) +{ + cdebug << "Reading" << _file; + return readKeyContent(contentsString(_file), _deleteFile ? _file : string()); +} + +h128 SecretStore::readKeyContent(std::string const& _content, std::string const& _file) +{ + js::mValue u = upgraded(_content); + if (u.type() == js::obj_type) + { + js::mObject& o = u.get_obj(); + auto uuid = fromUUID(o["id"].get_str()); + m_keys[uuid] = make_pair(js::write_string(o["crypto"], false), _file); + return uuid; + } + else + cwarn << "Invalid JSON in key file" << _file; + return h128(); } -std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) +bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function const& _pass, KDF _kdf) +{ +// cdebug << "recode:" << toUUID(_uuid); + bytes s = secret(_uuid, _pass, true); + if (s.empty()) + return false; + m_keys[_uuid].first = encrypt(s, _newPass, _kdf); + save(); + return true; +} + +std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf) { js::mObject ret; // KDF info - unsigned dklen = 16; - unsigned iterations = 262144; + unsigned dklen = 32; bytes salt = h256::random().asBytes(); - ret["kdf"] = "pbkdf2"; + bytes derivedKey; + if (_kdf == KDF::Scrypt) { - js::mObject params; - params["prf"] = "hmac-sha256"; - params["c"] = (int)iterations; - params["salt"] = toHex(salt); - params["dklen"] = (int)dklen; - ret["kdfparams"] = params; + unsigned iterations = 262144; + unsigned p = 1; + unsigned r = 8; + ret["kdf"] = "scrypt"; + { + js::mObject params; + params["n"] = (int64_t)iterations; + params["r"] = (int)r; + params["p"] = (int)p; + params["dklen"] = (int)dklen; + params["salt"] = toHex(salt); + ret["kdfparams"] = params; + } + derivedKey = scrypt(_pass, salt, iterations, r, p, dklen); + } + else + { + unsigned iterations = 262144; + ret["kdf"] = "pbkdf2"; + { + js::mObject params; + params["prf"] = "hmac-sha256"; + params["c"] = (int)iterations; + params["salt"] = toHex(salt); + params["dklen"] = (int)dklen; + ret["kdfparams"] = params; + } + derivedKey = pbkdf2(_pass, salt, iterations, dklen); } - bytes derivedKey = pbkdf2(_pass, salt, iterations, dklen); +// cdebug << "derivedKey" << toHex(derivedKey); // cipher info - ret["cipher"] = "aes-128-cbc"; - h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); + ret["cipher"] = "aes-128-ctr"; + h128 key(derivedKey, h128::AlignLeft); +// cdebug << "cipherKey" << key.hex(); h128 iv = h128::random(); { js::mObject params; @@ -159,7 +251,9 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) ret["ciphertext"] = toHex(cipherText); // and mac. - h256 mac = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); +// cdebug << "macBody" << toHex(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); +// cdebug << "mac" << mac.hex(); ret["mac"] = toHex(mac.ref()); return js::write_string((js::mValue)ret, true); @@ -188,30 +282,65 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) bytes salt = fromHex(params["salt"].get_str()); derivedKey = pbkdf2(_pass, salt, iterations, params["dklen"].get_int()); } + else if (o["kdf"].get_str() == "scrypt") + { + auto p = o["kdfparams"].get_obj(); + derivedKey = scrypt(_pass, fromHex(p["salt"].get_str()), p["n"].get_int(), p["r"].get_int(), p["p"].get_int(), p["dklen"].get_int()); + } else { cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; return bytes(); } + if (derivedKey.size() < 32 && !(o.count("compat") && o["compat"].get_str() == "2")) + { + cwarn << "Derived key's length too short (<32 bytes)"; + return bytes(); + } + bytes cipherText = fromHex(o["ciphertext"].get_str()); // check MAC - h256 mac(o["mac"].get_str()); - h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); - if (mac != macExp) + if (o.count("mac")) { - cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); - return bytes(); + h256 mac(o["mac"].get_str()); + h256 macExp; + if (o.count("compat") && o["compat"].get_str() == "2") + macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + else + macExp = sha3(bytesConstRef(&derivedKey).cropped(16, 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } + } + else if (o.count("sillymac")) + { + h256 mac(o["sillymac"].get_str()); + h256 macExp = sha3(asBytes(o["sillymacjson"].get_str()) + bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + if (mac != macExp) + { + cwarn << "Invalid key - MAC mismatch; expected" << toString(macExp) << ", got" << toString(mac); + return bytes(); + } } + else + cwarn << "No MAC. Proceeding anyway."; // decrypt - if (o["cipher"].get_str() == "aes-128-cbc") + if (o["cipher"].get_str() == "aes-128-ctr") { auto params = o["cipherparams"].get_obj(); - h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); h128 iv(params["iv"].get_str()); - return decryptSymNoAuth(key, iv, &cipherText); + if (o.count("compat") && o["compat"].get_str() == "2") + { + h128 key(sha3(h128(derivedKey, h128::AlignRight)), h128::AlignRight); + return decryptSymNoAuth(key, iv, &cipherText); + } + else + return decryptSymNoAuth(h128(derivedKey, h128::AlignLeft), iv, &cipherText); } else { diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 18c290c1f..4474212b1 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -30,27 +30,48 @@ namespace dev { +enum class KDF { + PBKDF2_SHA256, + Scrypt, +}; + class SecretStore { public: - SecretStore(); + SecretStore(std::string const& _path = defaultPath()); ~SecretStore(); - bytes secret(h128 const& _uuid, std::function const& _pass) const; + bytes secret(h128 const& _uuid, std::function const& _pass, bool _useCache = true) const; + h128 importKey(std::string const& _file) { auto ret = readKey(_file, false); if (ret) save(); return ret; } + h128 importKeyContent(std::string const& _content) { auto ret = readKeyContent(_content, std::string()); if (ret) save(); return ret; } h128 importSecret(bytes const& _s, std::string const& _pass); + bool recode(h128 const& _uuid, std::string const& _newPass, std::function const& _pass, KDF _kdf = KDF::Scrypt); void kill(h128 const& _uuid); + std::vector keys() const { return keysOf(m_keys); } + // Clear any cached keys. void clearCache() const; + // Doesn't save(). + h128 readKey(std::string const& _file, bool _deleteFile); + h128 readKeyContent(std::string const& _content, std::string const& _file = std::string()); + + void save(std::string const& _keysPath); + void save() { save(m_path); } + + static std::string defaultPath() { return getDataDir("web3") + "/keys"; } + private: - void save(std::string const& _keysPath = getDataDir("web3") + "/keys"); - void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); - static std::string encrypt(bytes const& _v, std::string const& _pass); + void load(std::string const& _keysPath); + void load() { load(m_path); } + static std::string encrypt(bytes const& _v, std::string const& _pass, KDF _kdf = KDF::Scrypt); static bytes decrypt(std::string const& _v, std::string const& _pass); mutable std::unordered_map m_cached; std::unordered_map> m_keys; + + std::string m_path; }; } diff --git a/libethash/internal.c b/libethash/internal.c index e881e0c7b..2a12163f7 100644 --- a/libethash/internal.c +++ b/libethash/internal.c @@ -368,7 +368,7 @@ static bool ethash_mmap(struct ethash_full* ret, FILE* f) if ((fd = ethash_fileno(ret->file)) == -1) { return false; } - mmapped_data= mmap( + mmapped_data = mmap( NULL, (size_t)ret->file_size + ETHASH_DAG_MAGIC_NUM_SIZE, PROT_READ | PROT_WRITE, diff --git a/libethcore/Ethash.cpp b/libethcore/Ethash.cpp index 24a6ab41e..60585a162 100644 --- a/libethcore/Ethash.cpp +++ b/libethcore/Ethash.cpp @@ -142,8 +142,12 @@ void Ethash::CPUMiner::workLoop() WorkPackage w = work(); EthashAux::FullType dag; - while (!shouldStop() && !(dag = EthashAux::full(w.seedHash, true))) - this_thread::sleep_for(chrono::milliseconds(500)); + while (!shouldStop() && !dag) + { + while (!shouldStop() && EthashAux::computeFull(w.seedHash, true) != 100) + this_thread::sleep_for(chrono::milliseconds(500)); + dag = EthashAux::full(w.seedHash, false); + } h256 boundary = w.boundary; unsigned hashCount = 1; diff --git a/libethcore/EthashAux.cpp b/libethcore/EthashAux.cpp index a94c1237d..06da22f98 100644 --- a/libethcore/EthashAux.cpp +++ b/libethcore/EthashAux.cpp @@ -133,7 +133,9 @@ bytesConstRef EthashAux::LightAllocation::data() const EthashAux::FullAllocation::FullAllocation(ethash_light_t _light, ethash_callback_t _cb) { +// cdebug << "About to call ethash_full_new..."; full = ethash_full_new(_light, _cb); +// cdebug << "Called OK."; if (!full) BOOST_THROW_EXCEPTION(ExternalFunctionFailure("ethash_full_new()")); } @@ -170,9 +172,9 @@ EthashAux::FullType EthashAux::full(h256 const& _seedHash, bool _createIfMissing if (_createIfMissing || computeFull(_seedHash, false) == 100) { s_dagCallback = _f; - cnote << "Loading from libethash..."; +// cnote << "Loading from libethash..."; ret = make_shared(l->light, dagCallbackShim); - cnote << "Done loading."; +// cnote << "Done loading."; DEV_GUARDED(get()->x_fulls) get()->m_fulls[_seedHash] = get()->m_lastUsedFull = ret; diff --git a/libethcore/ICAP.h b/libethcore/ICAP.h index b4229e1f5..a2456bd40 100644 --- a/libethcore/ICAP.h +++ b/libethcore/ICAP.h @@ -38,8 +38,6 @@ namespace eth struct InvalidICAP: virtual public dev::Exception {}; -static const std::string EmptyString; - /** * @brief Encapsulation of an ICAP address. * Can be encoded, decoded, looked-up and inspected. diff --git a/libethereum/KeyManager.cpp b/libethcore/KeyManager.cpp similarity index 71% rename from libethereum/KeyManager.cpp rename to libethcore/KeyManager.cpp index 8c3fd28c3..182201301 100644 --- a/libethereum/KeyManager.cpp +++ b/libethcore/KeyManager.cpp @@ -31,8 +31,8 @@ using namespace dev; using namespace eth; namespace fs = boost::filesystem; -KeyManager::KeyManager(std::string const& _keysFile): - m_keysFile(_keysFile) +KeyManager::KeyManager(std::string const& _keysFile, std::string const& _secretsPath): + m_keysFile(_keysFile), m_store(_secretsPath) {} KeyManager::~KeyManager() @@ -49,6 +49,32 @@ void KeyManager::create(std::string const& _pass) write(_pass, m_keysFile); } +bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf) +{ + noteHint(_newPass, _hint); + h128 u = uuid(_address); + if (!store().recode(u, _newPass, [&](){ return getPassword(u, _pass); }, _kdf)) + return false; + + m_keyInfo[u].passHash = hashPassword(_newPass); + write(); + return true; +} + +bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass, KDF _kdf) +{ + h128 u = uuid(_address); + std::string p; + if (_newPass == SemanticPassword::Existing) + p = getPassword(u, _pass); + else if (_newPass == SemanticPassword::Master) + p = defaultPassword(); + else + return false; + + return recode(_address, p, string(), _pass, _kdf); +} + bool KeyManager::load(std::string const& _pass) { try { @@ -70,8 +96,12 @@ bool KeyManager::load(std::string const& _pass) m_passwordInfo[(h256)i[0]] = (std::string)i[1]; m_password = (string)s[3]; } + cdebug << hashPassword(m_password) << toHex(m_password); m_cachedPasswords[hashPassword(m_password)] = m_password; - m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); + cdebug << hashPassword(asString(m_key.ref())) << m_key.hex(); + m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); + cdebug << hashPassword(_pass) << _pass; + m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; return true; } catch (...) { @@ -89,18 +119,35 @@ Secret KeyManager::secret(Address const& _address, function const Secret KeyManager::secret(h128 const& _uuid, function const& _pass) const { - return Secret(m_store.secret(_uuid, [&](){ - auto kit = m_keyInfo.find(_uuid); - if (kit != m_keyInfo.end()) + return Secret(m_store.secret(_uuid, [&](){ return getPassword(_uuid, _pass); })); +} + +std::string KeyManager::getPassword(h128 const& _uuid, function const& _pass) const +{ + auto kit = m_keyInfo.find(_uuid); + h256 ph; + if (kit != m_keyInfo.end()) + ph = kit->second.passHash; + return getPassword(ph, _pass); +} + +std::string KeyManager::getPassword(h256 const& _passHash, function const& _pass) const +{ + auto it = m_cachedPasswords.find(_passHash); + if (it != m_cachedPasswords.end()) + return it->second; + for (unsigned i = 0; i< 10; ++i) + { + std::string p = _pass(); + if (p.empty()) + break; + if (hashPassword(p) == _passHash || !_passHash) { - auto it = m_cachedPasswords.find(kit->second.passHash); - if (it != m_cachedPasswords.end()) - return it->second; + m_cachedPasswords[hashPassword(p)] = p; + return p; } - std::string p = _pass(); - m_cachedPasswords[hashPassword(p)] = p; - return p; - })); + } + return string(); } h128 KeyManager::uuid(Address const& _a) const @@ -171,7 +218,7 @@ std::unordered_map> KeyManager::acc std::unordered_map> ret; for (auto const& i: m_addrLookup) if (m_keyInfo.count(i.second) > 0) - ret[i.first] = make_pair(m_keyInfo.at(i.second).info, m_passwordInfo.at(m_keyInfo.at(i.second).passHash)); + ret[i.first] = make_pair(m_keyInfo.count(i.second) ? m_keyInfo.at(i.second).info : "", m_keyInfo.count(i.second) && m_passwordInfo.count(m_keyInfo.at(i.second).passHash) ? m_passwordInfo.at(m_keyInfo.at(i.second).passHash) : ""); return ret; } @@ -194,6 +241,9 @@ void KeyManager::write(std::string const& _pass, std::string const& _keysFile) c bytes salt = h256::random().asBytes(); writeFile(_keysFile + ".salt", salt); auto key = h128(pbkdf2(_pass, salt, 262144, 16)); + + m_cachedPasswords[hashPassword(_pass)] = _pass; + m_master = hashPassword(_pass); write(key, _keysFile); } diff --git a/libethereum/KeyManager.h b/libethcore/KeyManager.h similarity index 74% rename from libethereum/KeyManager.h rename to libethcore/KeyManager.h index 0a83fcc00..155805c5d 100644 --- a/libethereum/KeyManager.h +++ b/libethcore/KeyManager.h @@ -42,6 +42,12 @@ struct KeyInfo static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::string(); }; +enum class SemanticPassword +{ + Existing, + Master +}; + // TODO: This one is specifically for Ethereum, but we can make it generic in due course. // TODO: hidden-partition style key-store. /** @@ -55,7 +61,7 @@ static const auto DontKnowThrow = [](){ throw UnknownPassword(); return std::str class KeyManager { public: - KeyManager(std::string const& _keysFile = getDataDir("ethereum") + "/keys.info"); + KeyManager(std::string const& _keysFile = defaultPath(), std::string const& _secretsPath = SecretStore::defaultPath()); ~KeyManager(); void setKeysFile(std::string const& _keysFile) { m_keysFile = _keysFile; } @@ -67,9 +73,12 @@ public: void save(std::string const& _pass) const { write(_pass, m_keysFile); } void notePassword(std::string const& _pass) { m_cachedPasswords[hashPassword(_pass)] = _pass; } + void noteHint(std::string const& _pass, std::string const& _hint) { if (!_hint.empty()) m_passwordInfo[hashPassword(_pass)] = _hint; } + bool haveHint(std::string const& _pass) const { auto h = hashPassword(_pass); return m_cachedPasswords.count(h) && !m_cachedPasswords.at(h).empty(); } AddressHash accounts() const; std::unordered_map> accountDetails() const; + std::string const& hint(Address const& _a) const { try { return m_passwordInfo.at(m_keyInfo.at(m_addrLookup.at(_a)).passHash); } catch (...) { return EmptyString; } } h128 uuid(Address const& _a) const; Address address(h128 const& _uuid) const; @@ -84,15 +93,23 @@ public: Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; + bool recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); + bool recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); + void kill(h128 const& _id) { kill(address(_id)); } void kill(Address const& _a); + static std::string defaultPath() { return getDataDir("ethereum") + "/keys.info"; } + private: - std::string defaultPassword() const { return asString(m_key.ref()); } + std::string getPassword(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; + std::string getPassword(h256 const& _passHash, std::function const& _pass = DontKnowThrow) const; + std::string defaultPassword(std::function const& _pass = DontKnowThrow) const { return getPassword(m_master, _pass); } h256 hashPassword(std::string const& _pass) const; // Only use if previously loaded ok. // @returns false if wasn't previously loaded ok. + bool write() const { return write(m_keysFile); } bool write(std::string const& _keysFile) const; void write(std::string const& _pass, std::string const& _keysFile) const; void write(h128 const& _key, std::string const& _keysFile) const; @@ -112,9 +129,10 @@ private: // we have an upgrade strategy. std::string m_password; - SecretStore m_store; - mutable h128 m_key; mutable std::string m_keysFile; + mutable h128 m_key; + mutable h256 m_master; + SecretStore m_store; }; } diff --git a/libethereum/AccountDiff.h b/libethereum/AccountDiff.h index 22107b958..dd494c0a5 100644 --- a/libethereum/AccountDiff.h +++ b/libethereum/AccountDiff.h @@ -62,7 +62,7 @@ struct AccountDiff Diff exist; ///< The account's existance; was it created/deleted or not? Diff balance; ///< The account's balance; did it alter? Diff nonce; ///< The account's nonce; did it alter? - std::unordered_map> storage; ///< The account's storage addresses; each has its own Diff. + std::map> storage; ///< The account's storage addresses; each has its own Diff. Diff code; ///< The account's code; in general this should only have changed if exist also changed. }; diff --git a/libscrypt/CMakeLists.txt b/libscrypt/CMakeLists.txt new file mode 100644 index 000000000..8543244c5 --- /dev/null +++ b/libscrypt/CMakeLists.txt @@ -0,0 +1,24 @@ +cmake_policy(SET CMP0015 NEW) +# this policy was introduced in cmake 3.0 +# remove if, once 3.0 will be used on unix +if (${CMAKE_MAJOR_VERSION} GREATER 2) + # old policy do not use MACOSX_RPATH + cmake_policy(SET CMP0042 OLD) +endif() +set(CMAKE_AUTOMOC OFF) + +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DSTATICLIB") + +aux_source_directory(. SRC_LIST) + +include_directories(BEFORE ..) + +set(EXECUTABLE scrypt) + +file(GLOB HEADERS "*.h") + +add_library(${EXECUTABLE} ${SRC_LIST} ${HEADERS}) + +install( TARGETS ${EXECUTABLE} RUNTIME DESTINATION bin ARCHIVE DESTINATION lib LIBRARY DESTINATION lib ) +install( FILES ${HEADERS} DESTINATION include/${EXECUTABLE} ) + diff --git a/libscrypt/LICENSE b/libscrypt/LICENSE new file mode 100644 index 000000000..46a743175 --- /dev/null +++ b/libscrypt/LICENSE @@ -0,0 +1,9 @@ +Copyright (c) 2013, Joshua Small + All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/libscrypt/b64.c b/libscrypt/b64.c new file mode 100644 index 000000000..b797dd0d9 --- /dev/null +++ b/libscrypt/b64.c @@ -0,0 +1,313 @@ +/* + * Copyright (c) 1996 by Internet Software Consortium. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS + * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE + * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR + * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS + * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + * SOFTWARE. + */ + +/* + * Portions Copyright (c) 1995 by International Business Machines, Inc. + * + * International Business Machines, Inc. (hereinafter called IBM) grants + * permission under its copyrights to use, copy, modify, and distribute this + * Software with or without fee, provided that the above copyright notice and + * all paragraphs of this notice appear in all copies, and that the name of IBM + * not be used in connection with the marketing of any product incorporating + * the Software or modifications thereof, without specific, written prior + * permission. + * + * To the extent it has a right to do so, IBM grants an immunity from suit + * under its patents, if any, for the use, sale or manufacture of products to + * the extent that such products are used for performing Domain Name System + * dynamic updates in TCP/IP networks by means of the Software. No immunity is + * granted for any product per se or for any other function of any product. + * + * THE SOFTWARE IS PROVIDED "AS IS", AND IBM DISCLAIMS ALL WARRANTIES, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE. IN NO EVENT SHALL IBM BE LIABLE FOR ANY SPECIAL, + * DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER ARISING + * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE, EVEN + * IF IBM IS APPRISED OF THE POSSIBILITY OF SUCH DAMAGES. + */ + +/* + * Base64 encode/decode functions from OpenBSD (src/lib/libc/net/base64.c). + */ +#include +#include +#include +#include +#include + +#include "b64.h" + + +static const char Base64[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; +static const char Pad64 = '='; + +/* (From RFC1521 and draft-ietf-dnssec-secext-03.txt) + The following encoding technique is taken from RFC 1521 by Borenstein + and Freed. It is reproduced here in a slightly edited form for + convenience. + + A 65-character subset of US-ASCII is used, enabling 6 bits to be + represented per printable character. (The extra 65th character, "=", + is used to signify a special processing function.) + + The encoding process represents 24-bit groups of input bits as output + strings of 4 encoded characters. Proceeding from left to right, a + 24-bit input group is formed by concatenating 3 8-bit input groups. + These 24 bits are then treated as 4 concatenated 6-bit groups, each + of which is translated into a single digit in the base64 alphabet. + + Each 6-bit group is used as an index into an array of 64 printable + characters. The character referenced by the index is placed in the + output string. + + Table 1: The Base64 Alphabet + + Value Encoding Value Encoding Value Encoding Value Encoding + 0 A 17 R 34 i 51 z + 1 B 18 S 35 j 52 0 + 2 C 19 T 36 k 53 1 + 3 D 20 U 37 l 54 2 + 4 E 21 V 38 m 55 3 + 5 F 22 W 39 n 56 4 + 6 G 23 X 40 o 57 5 + 7 H 24 Y 41 p 58 6 + 8 I 25 Z 42 q 59 7 + 9 J 26 a 43 r 60 8 + 10 K 27 b 44 s 61 9 + 11 L 28 c 45 t 62 + + 12 M 29 d 46 u 63 / + 13 N 30 e 47 v + 14 O 31 f 48 w (pad) = + 15 P 32 g 49 x + 16 Q 33 h 50 y + + Special processing is performed if fewer than 24 bits are available + at the end of the data being encoded. A full encoding quantum is + always completed at the end of a quantity. When fewer than 24 input + bits are available in an input group, zero bits are added (on the + right) to form an integral number of 6-bit groups. Padding at the + end of the data is performed using the '=' character. + + Since all base64 input is an integral number of octets, only the + ------------------------------------------------- + following cases can arise: + + (1) the final quantum of encoding input is an integral + multiple of 24 bits; here, the final unit of encoded + output will be an integral multiple of 4 characters + with no "=" padding, + (2) the final quantum of encoding input is exactly 8 bits; + here, the final unit of encoded output will be two + characters followed by two "=" padding characters, or + (3) the final quantum of encoding input is exactly 16 bits; + here, the final unit of encoded output will be three + characters followed by one "=" padding character. +*/ + +int +libscrypt_b64_encode(src, srclength, target, targsize) + unsigned char const *src; + size_t srclength; + char *target; + size_t targsize; +{ + size_t datalength = 0; + unsigned char input[3]; + unsigned char output[4]; + unsigned int i; + + while (2 < srclength) { + input[0] = *src++; + input[1] = *src++; + input[2] = *src++; + srclength -= 3; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + output[3] = input[2] & 0x3f; + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + target[datalength++] = Base64[output[2]]; + target[datalength++] = Base64[output[3]]; + } + + /* Now we worry about padding. */ + if (0 != srclength) { + /* Get what's left. */ + input[0] = input[1] = input[2] = '\0'; + for (i = 0; i < srclength; i++) + input[i] = *src++; + + output[0] = input[0] >> 2; + output[1] = ((input[0] & 0x03) << 4) + (input[1] >> 4); + output[2] = ((input[1] & 0x0f) << 2) + (input[2] >> 6); + + if (datalength + 4 > targsize) + return (-1); + target[datalength++] = Base64[output[0]]; + target[datalength++] = Base64[output[1]]; + if (srclength == 1) + target[datalength++] = Pad64; + else + target[datalength++] = Base64[output[2]]; + target[datalength++] = Pad64; + } + if (datalength >= targsize) + return (-1); + target[datalength] = '\0'; /* Returned value doesn't count \0. */ + return (int)(datalength); +} + +/* skips all whitespace anywhere. + converts characters, four at a time, starting at (or after) + src from base - 64 numbers into three 8 bit bytes in the target area. + it returns the number of data bytes stored at the target, or -1 on error. + */ + +int +libscrypt_b64_decode(src, target, targsize) + char const *src; + unsigned char *target; + size_t targsize; +{ + int state, ch; + unsigned int tarindex; + unsigned char nextbyte; + char *pos; + + state = 0; + tarindex = 0; + + while ((ch = (unsigned char)*src++) != '\0') { + if (isspace(ch)) /* Skip whitespace anywhere. */ + continue; + + if (ch == Pad64) + break; + + pos = strchr(Base64, ch); + if (pos == 0) /* A non-base64 character. */ + return (-1); + + switch (state) { + case 0: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] = (pos - Base64) << 2; + } + state = 1; + break; + case 1: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 4; + nextbyte = ((pos - Base64) & 0x0f) << 4; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 2; + break; + case 2: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64) >> 2; + nextbyte = ((pos - Base64) & 0x03) << 6; + if (tarindex + 1 < targsize) + target[tarindex+1] = nextbyte; + else if (nextbyte) + return (-1); + } + tarindex++; + state = 3; + break; + case 3: + if (target) { + if (tarindex >= targsize) + return (-1); + target[tarindex] |= (pos - Base64); + } + tarindex++; + state = 0; + break; + } + } + + /* + * We are done decoding Base-64 chars. Let's see if we ended + * on a byte boundary, and/or with erroneous trailing characters. + */ + + if (ch == Pad64) { /* We got a pad char. */ + ch = (unsigned char)*src++; /* Skip it, get next. */ + switch (state) { + case 0: /* Invalid = in first position */ + case 1: /* Invalid = in second position */ + return (-1); + + case 2: /* Valid, means one byte of info */ + /* Skip any number of spaces. */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + break; + /* Make sure there is another trailing = sign. */ + if (ch != Pad64) + return (-1); + ch = (unsigned char)*src++; /* Skip the = */ + /* Fall through to "single trailing =" case. */ + /* FALLTHROUGH */ + + case 3: /* Valid, means two bytes of info */ + /* + * We know this char is an =. Is there anything but + * whitespace after it? + */ + for (; ch != '\0'; ch = (unsigned char)*src++) + if (!isspace(ch)) + return (-1); + + /* + * Now make sure for cases 2 and 3 that the "extra" + * bits that slopped past the last full byte were + * zeros. If we don't check them, they become a + * subliminal channel. + */ + if (target && tarindex < targsize && + target[tarindex] != 0) + return (-1); + } + } else { + /* + * We ended by seeing the end of the string. Make sure we + * have no partial bytes lying around. + */ + if (state != 0) + return (-1); + } + + return (tarindex); +} diff --git a/libscrypt/b64.h b/libscrypt/b64.h new file mode 100644 index 000000000..2e271eb5b --- /dev/null +++ b/libscrypt/b64.h @@ -0,0 +1,10 @@ + +/* BASE64 libraries used internally - should not need to be packaged */ + +#define b64_encode_len(A) ((A+2)/3 * 4 + 1) +#define b64_decode_len(A) (A / 4 * 3 + 2) + +int libscrypt_b64_encode(unsigned char const *src, size_t srclength, + /*@out@*/ char *target, size_t targetsize); +int libscrypt_b64_decode(char const *src, /*@out@*/ unsigned char *target, + size_t targetsize); diff --git a/libscrypt/crypto-mcf.c b/libscrypt/crypto-mcf.c new file mode 100644 index 000000000..9f7ddc376 --- /dev/null +++ b/libscrypt/crypto-mcf.c @@ -0,0 +1,73 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */ +//#include +#endif + +#include "libscrypt.h" + +/* ilog2 for powers of two */ +static uint32_t scrypt_ilog2(uint32_t n) +{ +#ifndef S_SPLINT_S + + /* Check for a valid power of two */ + if (n < 2 || (n & (n - 1))) + return -1; +#endif + uint32_t t = 1; + while (((uint32_t)1 << t) < n) + { + if(t > SCRYPT_SAFE_N) + return (uint32_t) -1; /* Check for insanity */ + t++; + } + + return t; +} + +#ifdef _MSC_VER + #define SNPRINTF _snprintf +#else + #define SNPRINTF snprintf +#endif + +int libscrypt_mcf(uint32_t N, uint32_t r, uint32_t p, const char *salt, + const char *hash, char *mcf) +{ + + uint32_t t, params; + int s; + + if(!mcf || !hash) + return 0; + /* Although larger values of r, p are valid in scrypt, this mcf format + * limits to 8 bits. If your number is larger, current computers will + * struggle + */ + if(r > (uint8_t)(-1) || p > (uint8_t)(-1)) + return 0; + + t = scrypt_ilog2(N); + if (t < 1) + return 0; + + params = (r << 8) + p; + params += (uint32_t)t << 16; + + /* Using snprintf - not checking for overflows. We've already + * determined that mcf should be defined as at least SCRYPT_MCF_LEN + * in length + */ + s = SNPRINTF(mcf, SCRYPT_MCF_LEN, SCRYPT_MCF_ID "$%06x$%s$%s", (unsigned int)params, salt, hash); + if (s > SCRYPT_MCF_LEN) + return 0; + + return 1; +} diff --git a/libscrypt/crypto-scrypt-saltgen.c b/libscrypt/crypto-scrypt-saltgen.c new file mode 100644 index 000000000..e69de29bb diff --git a/libscrypt/crypto_scrypt-check.c b/libscrypt/crypto_scrypt-check.c new file mode 100644 index 000000000..99477ffe9 --- /dev/null +++ b/libscrypt/crypto_scrypt-check.c @@ -0,0 +1,100 @@ +#include +#include +#include +#include + +#include "b64.h" +#include "slowequals.h" +#include "libscrypt.h" + +#ifdef _WIN32 +/* On windows, strtok uses a thread-local static variable in strtok to + * make strtok thread-safe. It also neglects to provide a strtok_r. */ +#define strtok_r(str, val, saveptr) strtok((str), (val)) +#endif + +int libscrypt_check(char *mcf, const char *password) +{ + /* Return values: + * <0 error + * == 0 password incorrect + * >0 correct password + */ + +#ifndef _WIN32 + char *saveptr = NULL; +#endif + uint32_t params; + uint64_t N; + uint8_t r, p; + int retval; + uint8_t hashbuf[64]; + char outbuf[128]; + uint8_t salt[32]; + char *tok; + + if(memcmp(mcf, SCRYPT_MCF_ID, 3) != 0) + { + /* Only version 0 supported */ + return -1; + } + + tok = strtok_r(mcf, "$", &saveptr); + if ( !tok ) + return -1; + + tok = strtok_r(NULL, "$", &saveptr); + + if ( !tok ) + return -1; + + params = (uint32_t)strtoul(tok, NULL, 16); + if ( params == 0 ) + return -1; + + tok = strtok_r(NULL, "$", &saveptr); + + if ( !tok ) + return -1; + + p = params & 0xff; + r = (params >> 8) & 0xff; + N = params >> 16; + + if (N > SCRYPT_SAFE_N) + return -1; + + N = (uint64_t)1 << N; + + /* Useful debugging: + printf("We've obtained salt 'N' r p of '%s' %d %d %d\n", tok, N,r,p); + */ + + memset(salt, 0, sizeof(salt)); /* Keeps splint happy */ + retval = libscrypt_b64_decode(tok, (unsigned char*)salt, sizeof(salt)); + if (retval < 1) + return -1; + + retval = libscrypt_scrypt((uint8_t*)password, strlen(password), salt, + (uint32_t)retval, N, r, p, hashbuf, sizeof(hashbuf)); + + if (retval != 0) + return -1; + + retval = libscrypt_b64_encode((unsigned char*)hashbuf, sizeof(hashbuf), + outbuf, sizeof(outbuf)); + + if (retval == 0) + return -1; + + tok = strtok_r(NULL, "$", &saveptr); + + if ( !tok ) + return -1; + + if(slow_equals(tok, outbuf) == 0) + return 0; + + return 1; /* This is the "else" condition */ +} + diff --git a/libscrypt/crypto_scrypt-hash.c b/libscrypt/crypto_scrypt-hash.c new file mode 100644 index 000000000..e69de29bb diff --git a/libscrypt/crypto_scrypt-hexconvert.c b/libscrypt/crypto_scrypt-hexconvert.c new file mode 100644 index 000000000..3df12a023 --- /dev/null +++ b/libscrypt/crypto_scrypt-hexconvert.c @@ -0,0 +1,35 @@ +#include +#include +#include +#include + +/* The hexconvert function is only used to test reference vectors against + * known answers. The contents of this file are therefore a component + * to assist with test harnesses only + */ + +int libscrypt_hexconvert(uint8_t *buf, size_t s, char *outbuf, size_t obs) +{ + + size_t i; + int len = 0; + + if (!buf || s < 1 || obs < (s * 2 + 1)) + return 0; + + memset(outbuf, 0, obs); + + + for(i=0; i<=(s-1); i++) + { + /* snprintf(outbuf, s,"%s...", outbuf....) has undefined results + * and can't be used. Using offests like this makes snprintf + * nontrivial. we therefore have use inescure sprintf() and + * lengths checked elsewhere (start of function) */ + /*@ -bufferoverflowhigh @*/ + len += sprintf(outbuf+len, "%02x", (unsigned int) buf[i]); + } + + return 1; +} + diff --git a/libscrypt/crypto_scrypt-hexconvert.h b/libscrypt/crypto_scrypt-hexconvert.h new file mode 100644 index 000000000..8175b24f1 --- /dev/null +++ b/libscrypt/crypto_scrypt-hexconvert.h @@ -0,0 +1,9 @@ + +#include + +/** + * Converts a binary string to a hex representation of that string + * outbuf must have size of at least buf * 2 + 1. + */ +int libscrypt_hexconvert(const uint8_t *buf, size_t s, char *outbuf, + size_t obs); diff --git a/libscrypt/crypto_scrypt-nosse.c b/libscrypt/crypto_scrypt-nosse.c new file mode 100644 index 000000000..12c860f2d --- /dev/null +++ b/libscrypt/crypto_scrypt-nosse.c @@ -0,0 +1,342 @@ +/*- + * Copyright 2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file was originally written by Colin Percival as part of the Tarsnap + * online backup system. + */ + +#include +#ifndef _WIN32 +#include +#endif +#include +#include +#include +#include + +#include "sha256.h" +#include "sysendian.h" + +#include "libscrypt.h" + +static void blkcpy(void *, void *, size_t); +static void blkxor(void *, void *, size_t); +static void salsa20_8(uint32_t[16]); +static void blockmix_salsa8(uint32_t *, uint32_t *, uint32_t *, size_t); +static uint64_t integerify(void *, size_t); +static void smix(uint8_t *, size_t, uint64_t, uint32_t *, uint32_t *); + +static void +blkcpy(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] = S[i]; +} + +static void +blkxor(void * dest, void * src, size_t len) +{ + size_t * D = dest; + size_t * S = src; + size_t L = len / sizeof(size_t); + size_t i; + + for (i = 0; i < L; i++) + D[i] ^= S[i]; +} + +/** + * salsa20_8(B): + * Apply the salsa20/8 core to the provided block. + */ +static void +salsa20_8(uint32_t B[16]) +{ + uint32_t x[16]; + size_t i; + + blkcpy(x, B, 64); + for (i = 0; i < 8; i += 2) { +#define R(a,b) (((a) << (b)) | ((a) >> (32 - (b)))) + /* Operate on columns. */ + x[ 4] ^= R(x[ 0]+x[12], 7); x[ 8] ^= R(x[ 4]+x[ 0], 9); + x[12] ^= R(x[ 8]+x[ 4],13); x[ 0] ^= R(x[12]+x[ 8],18); + + x[ 9] ^= R(x[ 5]+x[ 1], 7); x[13] ^= R(x[ 9]+x[ 5], 9); + x[ 1] ^= R(x[13]+x[ 9],13); x[ 5] ^= R(x[ 1]+x[13],18); + + x[14] ^= R(x[10]+x[ 6], 7); x[ 2] ^= R(x[14]+x[10], 9); + x[ 6] ^= R(x[ 2]+x[14],13); x[10] ^= R(x[ 6]+x[ 2],18); + + x[ 3] ^= R(x[15]+x[11], 7); x[ 7] ^= R(x[ 3]+x[15], 9); + x[11] ^= R(x[ 7]+x[ 3],13); x[15] ^= R(x[11]+x[ 7],18); + + /* Operate on rows. */ + x[ 1] ^= R(x[ 0]+x[ 3], 7); x[ 2] ^= R(x[ 1]+x[ 0], 9); + x[ 3] ^= R(x[ 2]+x[ 1],13); x[ 0] ^= R(x[ 3]+x[ 2],18); + + x[ 6] ^= R(x[ 5]+x[ 4], 7); x[ 7] ^= R(x[ 6]+x[ 5], 9); + x[ 4] ^= R(x[ 7]+x[ 6],13); x[ 5] ^= R(x[ 4]+x[ 7],18); + + x[11] ^= R(x[10]+x[ 9], 7); x[ 8] ^= R(x[11]+x[10], 9); + x[ 9] ^= R(x[ 8]+x[11],13); x[10] ^= R(x[ 9]+x[ 8],18); + + x[12] ^= R(x[15]+x[14], 7); x[13] ^= R(x[12]+x[15], 9); + x[14] ^= R(x[13]+x[12],13); x[15] ^= R(x[14]+x[13],18); +#undef R + } + for (i = 0; i < 16; i++) + B[i] += x[i]; +} + +/** + * blockmix_salsa8(Bin, Bout, X, r): + * Compute Bout = BlockMix_{salsa20/8, r}(Bin). The input Bin must be 128r + * bytes in length; the output Bout must also be the same size. The + * temporary space X must be 64 bytes. + */ +static void +blockmix_salsa8(uint32_t * Bin, uint32_t * Bout, uint32_t * X, size_t r) +{ + size_t i; + + /* 1: X <-- B_{2r - 1} */ + blkcpy(X, &Bin[(2 * r - 1) * 16], 64); + + /* 2: for i = 0 to 2r - 1 do */ + for (i = 0; i < 2 * r; i += 2) { + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8], X, 64); + + /* 3: X <-- H(X \xor B_i) */ + blkxor(X, &Bin[i * 16 + 16], 64); + salsa20_8(X); + + /* 4: Y_i <-- X */ + /* 6: B' <-- (Y_0, Y_2 ... Y_{2r-2}, Y_1, Y_3 ... Y_{2r-1}) */ + blkcpy(&Bout[i * 8 + r * 16], X, 64); + } +} + +/** + * integerify(B, r): + * Return the result of parsing B_{2r-1} as a little-endian integer. + */ +static uint64_t +integerify(void * B, size_t r) +{ + uint32_t * X = (void *)((uintptr_t)(B) + (2 * r - 1) * 64); + + return (((uint64_t)(X[1]) << 32) + X[0]); +} + +/** + * smix(B, r, N, V, XY): + * Compute B = SMix_r(B, N). The input B must be 128r bytes in length; + * the temporary storage V must be 128rN bytes in length; the temporary + * storage XY must be 256r + 64 bytes in length. The value N must be a + * power of 2 greater than 1. The arrays B, V, and XY must be aligned to a + * multiple of 64 bytes. + */ +static void +smix(uint8_t * B, size_t r, uint64_t N, uint32_t * V, uint32_t * XY) +{ + uint32_t * X = XY; + uint32_t * Y = &XY[32 * r]; + uint32_t * Z = &XY[64 * r]; + uint64_t i; + uint64_t j; + size_t k; + + /* 1: X <-- B */ + for (k = 0; k < 32 * r; k++) + X[k] = le32dec(&B[4 * k]); + + /* 2: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 3: V_i <-- X */ + blkcpy(&V[i * (32 * r)], X, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(X, Y, Z, r); + + /* 3: V_i <-- X */ + blkcpy(&V[(i + 1) * (32 * r)], Y, 128 * r); + + /* 4: X <-- H(X) */ + blockmix_salsa8(Y, X, Z, r); + } + + /* 6: for i = 0 to N - 1 do */ + for (i = 0; i < N; i += 2) { + /* 7: j <-- Integerify(X) mod N */ + j = integerify(X, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(X, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(X, Y, Z, r); + + /* 7: j <-- Integerify(X) mod N */ + j = integerify(Y, r) & (N - 1); + + /* 8: X <-- H(X \xor V_j) */ + blkxor(Y, &V[j * (32 * r)], 128 * r); + blockmix_salsa8(Y, X, Z, r); + } + + /* 10: B' <-- X */ + for (k = 0; k < 32 * r; k++) + le32enc(&B[4 * k], X[k]); +} + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * Return 0 on success; or -1 on error + */ +int +libscrypt_scrypt(const uint8_t * passwd, size_t passwdlen, + const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p, + uint8_t * buf, size_t buflen) +{ + void * B0, * V0, * XY0; + uint8_t * B; + uint32_t * V; + uint32_t * XY; + uint32_t i; + + /* Sanity-check parameters. */ +#if SIZE_MAX > UINT32_MAX + if (buflen > (((uint64_t)(1) << 32) - 1) * 32) { + errno = EFBIG; + goto err0; + } +#endif + if ((uint64_t)(r) * (uint64_t)(p) >= (1 << 30)) { + errno = EFBIG; + goto err0; + } + if (r == 0 || p == 0) { + errno = EINVAL; + goto err0; + } + if (((N & (N - 1)) != 0) || (N < 2)) { + errno = EINVAL; + goto err0; + } + if ((r > SIZE_MAX / 128 / p) || +#if SIZE_MAX / 256 <= UINT32_MAX + (r > SIZE_MAX / 256) || +#endif + (N > SIZE_MAX / 128 / r)) { + errno = ENOMEM; + goto err0; + } + + /* Allocate memory. */ +#ifdef HAVE_POSIX_MEMALIGN + if ((errno = posix_memalign(&B0, 64, 128 * r * p)) != 0) + goto err0; + B = (uint8_t *)(B0); + if ((errno = posix_memalign(&XY0, 64, 256 * r + 64)) != 0) + goto err1; + XY = (uint32_t *)(XY0); +#ifndef MAP_ANON + if ((errno = posix_memalign(&V0, 64, 128 * r * N)) != 0) + goto err2; + V = (uint32_t *)(V0); +#endif +#else + if ((B0 = malloc(128 * r * p + 63)) == NULL) + goto err0; + B = (uint8_t *)(((uintptr_t)(B0) + 63) & ~ (uintptr_t)(63)); + if ((XY0 = malloc(256 * r + 64 + 63)) == NULL) + goto err1; + XY = (uint32_t *)(((uintptr_t)(XY0) + 63) & ~ (uintptr_t)(63)); +#ifndef MAP_ANON + if ((V0 = malloc(128 * r * N + 63)) == NULL) + goto err2; + V = (uint32_t *)(((uintptr_t)(V0) + 63) & ~ (uintptr_t)(63)); +#endif +#endif +#ifdef MAP_ANON + if ((V0 = mmap(NULL, 128 * r * N, PROT_READ | PROT_WRITE, +#ifdef MAP_NOCORE + MAP_ANON | MAP_PRIVATE | MAP_NOCORE, +#else + MAP_ANON | MAP_PRIVATE, +#endif + -1, 0)) == MAP_FAILED) + goto err2; + V = (uint32_t *)(V0); +#endif + + /* 1: (B_0 ... B_{p-1}) <-- PBKDF2(P, S, 1, p * MFLen) */ + libscrypt_PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, 1, B, p * 128 * r); + + /* 2: for i = 0 to p - 1 do */ + for (i = 0; i < p; i++) { + /* 3: B_i <-- MF(B_i, N) */ + smix(&B[i * 128 * r], r, N, V, XY); + } + + /* 5: DK <-- PBKDF2(P, B, 1, dkLen) */ + libscrypt_PBKDF2_SHA256(passwd, passwdlen, B, p * 128 * r, 1, buf, buflen); + + /* Free memory. */ +#ifdef MAP_ANON + if (munmap(V0, 128 * r * N)) + goto err2; +#else + free(V0); +#endif + free(XY0); + free(B0); + + /* Success! */ + return (0); + +err2: + free(XY0); +err1: + free(B0); +err0: + /* Failure! */ + return (-1); +} diff --git a/libscrypt/libscrypt.h b/libscrypt/libscrypt.h new file mode 100644 index 000000000..889ba13aa --- /dev/null +++ b/libscrypt/libscrypt.h @@ -0,0 +1,56 @@ +/*- + */ +#ifndef _CRYPTO_SCRYPT_H_ +#define _CRYPTO_SCRYPT_H_ + + +#include + +#ifdef __cplusplus +extern "C"{ +#endif + +/** + * crypto_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * Compute scrypt(passwd[0 .. passwdlen - 1], salt[0 .. saltlen - 1], N, r, + * p, buflen) and write the result into buf. The parameters r, p, and buflen + * must satisfy r * p < 2^30 and buflen <= (2^32 - 1) * 32. The parameter N + * must be a power of 2 greater than 1. + * + * libscrypt_scrypt(passwd, passwdlen, salt, saltlen, N, r, p, buf, buflen): + * password; duh + * N: CPU AND RAM cost (first modifier) + * r: RAM Cost + * p: CPU cost (parallelisation) + * In short, N is your main performance modifier. Values of r = 8, p = 1 are + * standard unless you want to modify the CPU/RAM ratio. + * Return 0 on success; or -1 on error. + */ +int libscrypt_scrypt(const uint8_t *, size_t, const uint8_t *, size_t, uint64_t, + uint32_t, uint32_t, /*@out@*/ uint8_t *, size_t); + +/* Converts a series of input parameters to a MCF form for storage */ +int libscrypt_mcf(uint32_t N, uint32_t r, uint32_t p, const char *salt, + const char *hash, char *mcf); + +/* Checks a given MCF against a password */ +int libscrypt_check(char *mcf, const char *password); + +#ifdef __cplusplus +} +#endif + +/* Sane default values */ +#define SCRYPT_HASH_LEN 64 /* This can be user defined - + *but 64 is the reference size + */ +#define SCRYPT_SAFE_N 30 /* This is much higher than you want. It's just + * a blocker for insane defines + */ +#define SCRYPT_SALT_LEN 16 /* This is just a recommended size */ +#define SCRYPT_MCF_LEN 125 /* mcf is 120 byte + nul */ +#define SCRYPT_MCF_ID "$s1" +#define SCRYPT_N 16384 +#define SCRYPT_r 8 +#define SCRYPT_p 16 +#endif /* !_CRYPTO_SCRYPT_H_ */ diff --git a/libscrypt/libscrypt.version b/libscrypt/libscrypt.version new file mode 100644 index 000000000..9cc574db2 --- /dev/null +++ b/libscrypt/libscrypt.version @@ -0,0 +1,8 @@ +libscrypt { + global: libscrypt_check; +libscrypt_hash; +libscrypt_mcf; +libscrypt_salt_gen; +libscrypt_scrypt; + local: *; +}; diff --git a/libscrypt/sha256.c b/libscrypt/sha256.c new file mode 100644 index 000000000..279e3cf8d --- /dev/null +++ b/libscrypt/sha256.c @@ -0,0 +1,411 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include + +#include +#include + +#include "sysendian.h" + +#include "sha256.h" + +/* + * Encode a length len/4 vector of (uint32_t) into a length len vector of + * (unsigned char) in big-endian form. Assumes len is a multiple of 4. + */ +static void +be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + be32enc(dst + i * 4, src[i]); +} + +/* + * Decode a big-endian length len vector of (unsigned char) into a length + * len/4 vector of (uint32_t). Assumes len is a multiple of 4. + */ +static void +be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len) +{ + size_t i; + + for (i = 0; i < len / 4; i++) + dst[i] = be32dec(src + i * 4); +} + +/* Elementary functions used by SHA256 */ +#define Ch(x, y, z) ((x & (y ^ z)) ^ z) +#define Maj(x, y, z) ((x & (y | z)) | (y & z)) +#define SHR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << (32 - n))) +#define S0(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define S1(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define s0(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHR(x, 3)) +#define s1(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHR(x, 10)) + +/* SHA256 round function */ +#define RND(a, b, c, d, e, f, g, h, k) \ + t0 = h + S1(e) + Ch(e, f, g) + k; \ + t1 = S0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + +/* Adjusted round function for rotating state */ +#define RNDr(S, W, i, k) \ + RND(S[(64 - i) % 8], S[(65 - i) % 8], \ + S[(66 - i) % 8], S[(67 - i) % 8], \ + S[(68 - i) % 8], S[(69 - i) % 8], \ + S[(70 - i) % 8], S[(71 - i) % 8], \ + W[i] + k) + +/* + * SHA256 block compression function. The 256-bit state is transformed via + * the 512-bit input block to produce a new state. + */ +static void +SHA256_Transform(uint32_t * state, const unsigned char block[64]) +{ + uint32_t W[64]; + uint32_t S[8]; + uint32_t t0, t1; + int i; + + /* 1. Prepare message schedule W. */ + be32dec_vect(W, block, 64); + for (i = 16; i < 64; i++) + W[i] = s1(W[i - 2]) + W[i - 7] + s0(W[i - 15]) + W[i - 16]; + + /* 2. Initialize working variables. */ + memcpy(S, state, 32); + + /* 3. Mix. */ + RNDr(S, W, 0, 0x428a2f98); + RNDr(S, W, 1, 0x71374491); + RNDr(S, W, 2, 0xb5c0fbcf); + RNDr(S, W, 3, 0xe9b5dba5); + RNDr(S, W, 4, 0x3956c25b); + RNDr(S, W, 5, 0x59f111f1); + RNDr(S, W, 6, 0x923f82a4); + RNDr(S, W, 7, 0xab1c5ed5); + RNDr(S, W, 8, 0xd807aa98); + RNDr(S, W, 9, 0x12835b01); + RNDr(S, W, 10, 0x243185be); + RNDr(S, W, 11, 0x550c7dc3); + RNDr(S, W, 12, 0x72be5d74); + RNDr(S, W, 13, 0x80deb1fe); + RNDr(S, W, 14, 0x9bdc06a7); + RNDr(S, W, 15, 0xc19bf174); + RNDr(S, W, 16, 0xe49b69c1); + RNDr(S, W, 17, 0xefbe4786); + RNDr(S, W, 18, 0x0fc19dc6); + RNDr(S, W, 19, 0x240ca1cc); + RNDr(S, W, 20, 0x2de92c6f); + RNDr(S, W, 21, 0x4a7484aa); + RNDr(S, W, 22, 0x5cb0a9dc); + RNDr(S, W, 23, 0x76f988da); + RNDr(S, W, 24, 0x983e5152); + RNDr(S, W, 25, 0xa831c66d); + RNDr(S, W, 26, 0xb00327c8); + RNDr(S, W, 27, 0xbf597fc7); + RNDr(S, W, 28, 0xc6e00bf3); + RNDr(S, W, 29, 0xd5a79147); + RNDr(S, W, 30, 0x06ca6351); + RNDr(S, W, 31, 0x14292967); + RNDr(S, W, 32, 0x27b70a85); + RNDr(S, W, 33, 0x2e1b2138); + RNDr(S, W, 34, 0x4d2c6dfc); + RNDr(S, W, 35, 0x53380d13); + RNDr(S, W, 36, 0x650a7354); + RNDr(S, W, 37, 0x766a0abb); + RNDr(S, W, 38, 0x81c2c92e); + RNDr(S, W, 39, 0x92722c85); + RNDr(S, W, 40, 0xa2bfe8a1); + RNDr(S, W, 41, 0xa81a664b); + RNDr(S, W, 42, 0xc24b8b70); + RNDr(S, W, 43, 0xc76c51a3); + RNDr(S, W, 44, 0xd192e819); + RNDr(S, W, 45, 0xd6990624); + RNDr(S, W, 46, 0xf40e3585); + RNDr(S, W, 47, 0x106aa070); + RNDr(S, W, 48, 0x19a4c116); + RNDr(S, W, 49, 0x1e376c08); + RNDr(S, W, 50, 0x2748774c); + RNDr(S, W, 51, 0x34b0bcb5); + RNDr(S, W, 52, 0x391c0cb3); + RNDr(S, W, 53, 0x4ed8aa4a); + RNDr(S, W, 54, 0x5b9cca4f); + RNDr(S, W, 55, 0x682e6ff3); + RNDr(S, W, 56, 0x748f82ee); + RNDr(S, W, 57, 0x78a5636f); + RNDr(S, W, 58, 0x84c87814); + RNDr(S, W, 59, 0x8cc70208); + RNDr(S, W, 60, 0x90befffa); + RNDr(S, W, 61, 0xa4506ceb); + RNDr(S, W, 62, 0xbef9a3f7); + RNDr(S, W, 63, 0xc67178f2); + + /* 4. Mix local working variables into global state */ + for (i = 0; i < 8; i++) + state[i] += S[i]; + + /* Clean the stack. */ + memset(W, 0, 256); + memset(S, 0, 32); + t0 = t1 = 0; +} + +static unsigned char PAD[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* Add padding and terminating bit-count. */ +static void +SHA256_Pad(SHA256_CTX * ctx) +{ + unsigned char len[8]; + uint32_t r, plen; + + /* + * Convert length to a vector of bytes -- we do this now rather + * than later because the length will change after we pad. + */ + be32enc_vect(len, ctx->count, 8); + + /* Add 1--64 bytes so that the resulting length is 56 mod 64 */ + r = (ctx->count[1] >> 3) & 0x3f; + plen = (r < 56) ? (56 - r) : (120 - r); + libscrypt_SHA256_Update(ctx, PAD, (size_t)plen); + + /* Add the terminating bit-count */ + libscrypt_SHA256_Update(ctx, len, 8); +} + +/* SHA-256 initialization. Begins a SHA-256 operation. */ +void +libscrypt_SHA256_Init(SHA256_CTX * ctx) +{ + + /* Zero bits processed so far */ + ctx->count[0] = ctx->count[1] = 0; + + /* Magic initialization constants */ + ctx->state[0] = 0x6A09E667; + ctx->state[1] = 0xBB67AE85; + ctx->state[2] = 0x3C6EF372; + ctx->state[3] = 0xA54FF53A; + ctx->state[4] = 0x510E527F; + ctx->state[5] = 0x9B05688C; + ctx->state[6] = 0x1F83D9AB; + ctx->state[7] = 0x5BE0CD19; +} + +/* Add bytes into the hash */ +void +libscrypt_SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len) +{ + uint32_t bitlen[2]; + uint32_t r; + const unsigned char *src = in; + + /* Number of bytes left in the buffer from previous updates */ + r = (ctx->count[1] >> 3) & 0x3f; + + /* Convert the length into a number of bits */ + bitlen[1] = ((uint32_t)len) << 3; + bitlen[0] = (uint32_t)(len >> 29); + + /* Update number of bits */ + if ((ctx->count[1] += bitlen[1]) < bitlen[1]) + ctx->count[0]++; + ctx->count[0] += bitlen[0]; + + /* Handle the case where we don't need to perform any transforms */ + if (len < 64 - r) { + memcpy(&ctx->buf[r], src, len); + return; + } + + /* Finish the current block */ + memcpy(&ctx->buf[r], src, 64 - r); + SHA256_Transform(ctx->state, ctx->buf); + src += 64 - r; + len -= 64 - r; + + /* Perform complete blocks */ + while (len >= 64) { + SHA256_Transform(ctx->state, src); + src += 64; + len -= 64; + } + + /* Copy left over data into buffer */ + memcpy(ctx->buf, src, len); +} + +/* + * SHA-256 finalization. Pads the input data, exports the hash value, + * and clears the context state. + */ +void +libscrypt_SHA256_Final(unsigned char digest[32], SHA256_CTX * ctx) +{ + + /* Add padding */ + SHA256_Pad(ctx); + + /* Write the hash */ + be32enc_vect(digest, ctx->state, 32); + + /* Clear the context state */ + memset((void *)ctx, 0, sizeof(*ctx)); +} + +/* Initialize an HMAC-SHA256 operation with the given key. */ +void +libscrypt_HMAC_SHA256_Init(HMAC_SHA256_CTX * ctx, const void * _K, size_t Klen) +{ + unsigned char pad[64]; + unsigned char khash[32]; + const unsigned char * K = _K; + size_t i; + + /* If Klen > 64, the key is really SHA256(K). */ + if (Klen > 64) { + libscrypt_SHA256_Init(&ctx->ictx); + libscrypt_SHA256_Update(&ctx->ictx, K, Klen); + libscrypt_SHA256_Final(khash, &ctx->ictx); + K = khash; + Klen = 32; + } + + /* Inner SHA256 operation is SHA256(K xor [block of 0x36] || data). */ + libscrypt_SHA256_Init(&ctx->ictx); + memset(pad, 0x36, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + libscrypt_SHA256_Update(&ctx->ictx, pad, 64); + + /* Outer SHA256 operation is SHA256(K xor [block of 0x5c] || hash). */ + libscrypt_SHA256_Init(&ctx->octx); + memset(pad, 0x5c, 64); + for (i = 0; i < Klen; i++) + pad[i] ^= K[i]; + libscrypt_SHA256_Update(&ctx->octx, pad, 64); + + /* Clean the stack. */ + memset(khash, 0, 32); +} + +/* Add bytes to the HMAC-SHA256 operation. */ +void +libscrypt_HMAC_SHA256_Update(HMAC_SHA256_CTX * ctx, const void *in, size_t len) +{ + + /* Feed data to the inner SHA256 operation. */ + libscrypt_SHA256_Update(&ctx->ictx, in, len); +} + +/* Finish an HMAC-SHA256 operation. */ +void +libscrypt_HMAC_SHA256_Final(unsigned char digest[32], HMAC_SHA256_CTX * ctx) +{ + unsigned char ihash[32]; + + /* Finish the inner SHA256 operation. */ + libscrypt_SHA256_Final(ihash, &ctx->ictx); + + /* Feed the inner hash to the outer SHA256 operation. */ + libscrypt_SHA256_Update(&ctx->octx, ihash, 32); + + /* Finish the outer SHA256 operation. */ + libscrypt_SHA256_Final(digest, &ctx->octx); + + /* Clean the stack. */ + memset(ihash, 0, 32); +} + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void +libscrypt_PBKDF2_SHA256(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, + size_t saltlen, uint64_t c, uint8_t * buf, size_t dkLen) +{ + HMAC_SHA256_CTX PShctx, hctx; + size_t i; + uint8_t ivec[4]; + uint8_t U[32]; + uint8_t T[32]; + uint64_t j; + int k; + size_t clen; + + /* Compute HMAC state after processing P and S. */ + libscrypt_HMAC_SHA256_Init(&PShctx, passwd, passwdlen); + libscrypt_HMAC_SHA256_Update(&PShctx, salt, saltlen); + + /* Iterate through the blocks. */ + for (i = 0; i * 32 < dkLen; i++) { + /* Generate INT(i + 1). */ + be32enc(ivec, (uint32_t)(i + 1)); + + /* Compute U_1 = PRF(P, S || INT(i)). */ + memcpy(&hctx, &PShctx, sizeof(HMAC_SHA256_CTX)); + libscrypt_HMAC_SHA256_Update(&hctx, ivec, 4); + libscrypt_HMAC_SHA256_Final(U, &hctx); + + /* T_i = U_1 ... */ + memcpy(T, U, 32); + + for (j = 2; j <= c; j++) { + /* Compute U_j. */ + libscrypt_HMAC_SHA256_Init(&hctx, passwd, passwdlen); + libscrypt_HMAC_SHA256_Update(&hctx, U, 32); + libscrypt_HMAC_SHA256_Final(U, &hctx); + + /* ... xor U_j ... */ + for (k = 0; k < 32; k++) + T[k] ^= U[k]; + } + + /* Copy as many bytes as necessary into buf. */ + clen = dkLen - i * 32; + if (clen > 32) + clen = 32; + memcpy(&buf[i * 32], T, clen); + } + + /* Clean PShctx, since we never called _Final on it. */ + memset(&PShctx, 0, sizeof(HMAC_SHA256_CTX)); +} diff --git a/libscrypt/sha256.h b/libscrypt/sha256.h new file mode 100644 index 000000000..f7138b417 --- /dev/null +++ b/libscrypt/sha256.h @@ -0,0 +1,70 @@ +/*- + * Copyright 2005,2007,2009 Colin Percival + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/lib/libmd/sha256.h,v 1.2 2006/01/17 15:35:56 phk Exp $ + */ + +#ifndef _SHA256_H_ +#define _SHA256_H_ + +#include + +#include + +typedef struct libscrypt_SHA256Context { + uint32_t state[8]; + uint32_t count[2]; + unsigned char buf[64]; +} SHA256_CTX; + +typedef struct libscrypt_HMAC_SHA256Context { + SHA256_CTX ictx; + SHA256_CTX octx; +} HMAC_SHA256_CTX; + +void libscrypt_SHA256_Init(/*@out@*/ SHA256_CTX *); +void libscrypt_SHA256_Update(SHA256_CTX *, const void *, size_t); + +/* Original declaration: + * void SHA256_Final(unsigned char [32], SHA256_CTX *); +*/ +void libscrypt_SHA256_Final(/*@out@*/ unsigned char [], SHA256_CTX *); +void libscrypt_HMAC_SHA256_Init(HMAC_SHA256_CTX *, const void *, size_t); +void libscrypt_HMAC_SHA256_Update(HMAC_SHA256_CTX *, const void *, size_t); + +/* Original declaration: + * void HMAC_SHA256_Final(unsigned char [32], HMAC_SHA256_CTX *); +*/ +void libscrypt_HMAC_SHA256_Final(unsigned char [], HMAC_SHA256_CTX *); + +/** + * PBKDF2_SHA256(passwd, passwdlen, salt, saltlen, c, buf, dkLen): + * Compute PBKDF2(passwd, salt, c, dkLen) using HMAC-SHA256 as the PRF, and + * write the output to buf. The value dkLen must be at most 32 * (2^32 - 1). + */ +void libscrypt_PBKDF2_SHA256(const uint8_t *, size_t, const uint8_t *, size_t, + uint64_t, uint8_t *, size_t); + +#endif /* !_SHA256_H_ */ diff --git a/libscrypt/slowequals.c b/libscrypt/slowequals.c new file mode 100644 index 000000000..48e488e4e --- /dev/null +++ b/libscrypt/slowequals.c @@ -0,0 +1,26 @@ +#include + +/* Implements a constant time version of strcmp() + * Will return 1 if a and b are equal, 0 if they are not */ +int slow_equals(const char* a, const char* b) +{ + size_t lena, lenb, diff, i; + lena = strlen(a); + lenb = strlen(b); + diff = strlen(a) ^ strlen(b); + + for(i=0; i we have isn't usable. */ +#if !HAVE_DECL_BE64ENC +#undef HAVE_SYS_ENDIAN_H +#endif + +#ifdef HAVE_SYS_ENDIAN_H + +#include + +#else + +#include +#ifdef _MSC_VER + #define INLINE __inline +#else + #define INLINE inline +#endif + +static INLINE uint32_t +be32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[3]) + ((uint32_t)(p[2]) << 8) + + ((uint32_t)(p[1]) << 16) + ((uint32_t)(p[0]) << 24)); +} + +static INLINE void +be32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[3] = x & 0xff; + p[2] = (x >> 8) & 0xff; + p[1] = (x >> 16) & 0xff; + p[0] = (x >> 24) & 0xff; +} + +static INLINE uint64_t +be64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[7]) + ((uint64_t)(p[6]) << 8) + + ((uint64_t)(p[5]) << 16) + ((uint64_t)(p[4]) << 24) + + ((uint64_t)(p[3]) << 32) + ((uint64_t)(p[2]) << 40) + + ((uint64_t)(p[1]) << 48) + ((uint64_t)(p[0]) << 56)); +} + +static INLINE void +be64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[7] = x & 0xff; + p[6] = (x >> 8) & 0xff; + p[5] = (x >> 16) & 0xff; + p[4] = (x >> 24) & 0xff; + p[3] = (x >> 32) & 0xff; + p[2] = (x >> 40) & 0xff; + p[1] = (x >> 48) & 0xff; + p[0] = (x >> 56) & 0xff; +} + +static INLINE uint32_t +le32dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint32_t)(p[0]) + ((uint32_t)(p[1]) << 8) + + ((uint32_t)(p[2]) << 16) + ((uint32_t)(p[3]) << 24)); +} + +static INLINE void +le32enc(void *pp, uint32_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; +} + +static INLINE uint64_t +le64dec(const void *pp) +{ + const uint8_t *p = (uint8_t const *)pp; + + return ((uint64_t)(p[0]) + ((uint64_t)(p[1]) << 8) + + ((uint64_t)(p[2]) << 16) + ((uint64_t)(p[3]) << 24) + + ((uint64_t)(p[4]) << 32) + ((uint64_t)(p[5]) << 40) + + ((uint64_t)(p[6]) << 48) + ((uint64_t)(p[7]) << 56)); +} + +static INLINE void +le64enc(void *pp, uint64_t x) +{ + uint8_t * p = (uint8_t *)pp; + + p[0] = x & 0xff; + p[1] = (x >> 8) & 0xff; + p[2] = (x >> 16) & 0xff; + p[3] = (x >> 24) & 0xff; + p[4] = (x >> 32) & 0xff; + p[5] = (x >> 40) & 0xff; + p[6] = (x >> 48) & 0xff; + p[7] = (x >> 56) & 0xff; +} +#endif /* !HAVE_SYS_ENDIAN_H */ + +#endif /* !_SYSENDIAN_H_ */ diff --git a/libsolidity/Token.h b/libsolidity/Token.h index 1435dcc57..8a373da34 100644 --- a/libsolidity/Token.h +++ b/libsolidity/Token.h @@ -142,34 +142,34 @@ namespace solidity K(Delete, "delete", 0) \ \ /* Keywords */ \ + K(Anonymous, "anonymous", 0) \ K(Break, "break", 0) \ K(Const, "constant", 0) \ - K(Anonymous, "anonymous", 0) \ K(Continue, "continue", 0) \ K(Contract, "contract", 0) \ K(Default, "default", 0) \ K(Do, "do", 0) \ K(Else, "else", 0) \ + K(Enum, "enum", 0) \ K(Event, "event", 0) \ K(External, "external", 0) \ - K(Is, "is", 0) \ - K(Indexed, "indexed", 0) \ K(For, "for", 0) \ K(Function, "function", 0) \ K(If, "if", 0) \ + K(Indexed, "indexed", 0) \ + K(Internal, "internal", 0) \ K(Import, "import", 0) \ + K(Is, "is", 0) \ K(Mapping, "mapping", 0) \ K(Modifier, "modifier", 0) \ K(New, "new", 0) \ K(Public, "public", 0) \ K(Private, "private", 0) \ - K(Internal, "internal", 0) \ K(Return, "return", 0) \ K(Returns, "returns", 0) \ K(Struct, "struct", 0) \ K(Var, "var", 0) \ K(While, "while", 0) \ - K(Enum, "enum", 0) \ \ /* Ether subdenominations */ \ K(SubWei, "wei", 0) \ @@ -304,15 +304,21 @@ namespace solidity T(Identifier, NULL, 0) \ \ /* Keywords reserved for future. use*/ \ - T(String, "string", 0) \ + K(As, "as", 0) \ K(Case, "case", 0) \ + K(Catch, "catch", 0) \ + K(Final, "final", 0) \ + K(Let, "let", 0) \ + K(Match, "match", 0) \ + K(Of, "of", 0) \ + K(Relocatable, "relocatable", 0) \ + T(String, "string", 0) \ K(Switch, "switch", 0) \ K(Throw, "throw", 0) \ K(Try, "try", 0) \ - K(Catch, "catch", 0) \ - K(Using, "using", 0) \ K(Type, "type", 0) \ K(TypeOf, "typeof", 0) \ + K(Using, "using", 0) \ /* Illegal token - not able to scan. */ \ T(Illegal, "ILLEGAL", 0) \ \ diff --git a/libweb3jsonrpc/AccountHolder.cpp b/libweb3jsonrpc/AccountHolder.cpp index a73f20680..abd0a1adf 100644 --- a/libweb3jsonrpc/AccountHolder.cpp +++ b/libweb3jsonrpc/AccountHolder.cpp @@ -26,7 +26,8 @@ #include #include #include -#include +#include + using namespace std; using namespace dev; diff --git a/test/libdevcrypto/SecretStore.cpp b/test/libdevcrypto/SecretStore.cpp new file mode 100644 index 000000000..1f927db5d --- /dev/null +++ b/test/libdevcrypto/SecretStore.cpp @@ -0,0 +1,64 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see . +*/ +/** @file SecretStore.cpp + * @author Gav Wood + * @date 2015 + * Secret store test functions. + */ + +#include +#include +#include +#include "../JsonSpiritHeaders.h" +#include +#include +#include +#include +#include "MemTrie.h" +#include "../TestHelper.h" +using namespace std; +using namespace dev; + +namespace js = json_spirit; + +BOOST_AUTO_TEST_SUITE(KeyStore) + +BOOST_AUTO_TEST_CASE(basic_tests) +{ + string testPath = test::getTestPath(); + + testPath += "/KeyStoreTests"; + + cnote << "Testing Key Store..."; + js::mValue v; + string s = asString(contents(testPath + "/basic_tests.json")); + BOOST_REQUIRE_MESSAGE(s.length() > 0, "Contents of 'KeyStoreTests/basic_tests.json' is empty. Have you cloned the 'tests' repo branch develop?"); + js::read_string(s, v); + for (auto& i: v.get_obj()) + { + cnote << i.first; + js::mObject& o = i.second.get_obj(); + SecretStore store("."); + h128 u = store.readKeyContent(js::write_string(o["json"], false)); + cdebug << "read uuid" << u; + bytes s = store.secret(u, [&](){ return o["password"].get_str(); }); + cdebug << "got secret" << toHex(s); + BOOST_REQUIRE_EQUAL(toHex(s), o["priv"].get_str()); + } +} + +BOOST_AUTO_TEST_SUITE_END()