From 27654ae52c54eb9419d843fb2025a01adef0fc33 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 19:22:16 +0200 Subject: [PATCH 01/14] Scrypt support --- CMakeLists.txt | 1 + exp/main.cpp | 19 +- libdevcrypto/CMakeLists.txt | 1 + libdevcrypto/Common.cpp | 8 + libdevcrypto/Common.h | 3 + libdevcrypto/SecretStore.cpp | 5 + libscrypt/CMakeLists.txt | 24 ++ libscrypt/LICENSE | 9 + libscrypt/b64.c | 313 ++++++++++++++++++++ libscrypt/b64.h | 10 + libscrypt/crypto-mcf.c | 73 +++++ libscrypt/crypto-scrypt-saltgen.c | 48 ++++ libscrypt/crypto_scrypt-check.c | 100 +++++++ libscrypt/crypto_scrypt-hash.c | 44 +++ libscrypt/crypto_scrypt-hexconvert.c | 35 +++ libscrypt/crypto_scrypt-hexconvert.h | 9 + libscrypt/crypto_scrypt-nosse.c | 342 ++++++++++++++++++++++ libscrypt/libscrypt.h | 67 +++++ libscrypt/libscrypt.version | 8 + libscrypt/sha256.c | 411 +++++++++++++++++++++++++++ libscrypt/sha256.h | 70 +++++ libscrypt/slowequals.c | 26 ++ libscrypt/slowequals.h | 5 + libscrypt/sysendian.h | 144 ++++++++++ 24 files changed, 1771 insertions(+), 4 deletions(-) create mode 100644 libscrypt/CMakeLists.txt create mode 100644 libscrypt/LICENSE create mode 100644 libscrypt/b64.c create mode 100644 libscrypt/b64.h create mode 100644 libscrypt/crypto-mcf.c create mode 100644 libscrypt/crypto-scrypt-saltgen.c create mode 100644 libscrypt/crypto_scrypt-check.c create mode 100644 libscrypt/crypto_scrypt-hash.c create mode 100644 libscrypt/crypto_scrypt-hexconvert.c create mode 100644 libscrypt/crypto_scrypt-hexconvert.h create mode 100644 libscrypt/crypto_scrypt-nosse.c create mode 100644 libscrypt/libscrypt.h create mode 100644 libscrypt/libscrypt.version create mode 100644 libscrypt/sha256.c create mode 100644 libscrypt/sha256.h create mode 100644 libscrypt/slowequals.c create mode 100644 libscrypt/slowequals.h create mode 100644 libscrypt/sysendian.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aafdad099..8239e20a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,7 @@ if (JSCONSOLE) endif () add_subdirectory(secp256k1) +add_subdirectory(libscrypt) add_subdirectory(libdevcrypto) if (GENERAL) diff --git a/exp/main.cpp b/exp/main.cpp index 47efe576a..5162d915b 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -36,7 +36,6 @@ #include #include #include -/* #include #include #include @@ -57,17 +56,29 @@ #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/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..2a3561314 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -180,6 +181,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..a0f894a25 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -123,6 +123,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..7bc870bcf 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -188,6 +188,11 @@ 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["p"].get_int(), p["r"].get_int(), p["dklen"].get_int()); + } else { cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; 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..8ad3eb826 --- /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..a0e29988c --- /dev/null +++ b/libscrypt/crypto-scrypt-saltgen.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +#ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */ +#include +#endif + +#define RNGDEV "/dev/urandom" + +int libscrypt_salt_gen(uint8_t *salt, size_t len) +{ + unsigned char buf[len]; + size_t data_read = 0; + int urandom = open(RNGDEV, O_RDONLY); + + if (urandom < 0) + { + return -1; + } + + while (data_read < len) { + ssize_t result = read(urandom, buf + data_read, len - data_read); + + if (result < 0) + { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + + else { + (void)close(urandom); + return -1; + } + } + + data_read += result; + } + + /* Failures on close() shouldn't occur with O_RDONLY */ + (void)close(urandom); + + memcpy(salt, buf, len); + + return 0; +} 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..4b41007db --- /dev/null +++ b/libscrypt/crypto_scrypt-hash.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +#include "b64.h" +#include "libscrypt.h" + +int libscrypt_hash(char *dst, const char *passphrase, uint32_t N, uint8_t r, + uint8_t p) +{ + + int retval; + uint8_t salt[SCRYPT_SALT_LEN]; + uint8_t hashbuf[SCRYPT_HASH_LEN]; + char outbuf[256]; + char saltbuf[256]; + + if(libscrypt_salt_gen(salt, SCRYPT_SALT_LEN) == -1) + { + return 0; + } + + retval = libscrypt_scrypt((const uint8_t*)passphrase, strlen(passphrase), + (uint8_t*)salt, SCRYPT_SALT_LEN, N, r, p, hashbuf, sizeof(hashbuf)); + if(retval == -1) + return 0; + + retval = libscrypt_b64_encode((unsigned char*)hashbuf, sizeof(hashbuf), + outbuf, sizeof(outbuf)); + if(retval == -1) + return 0; + + retval = libscrypt_b64_encode((unsigned char *)salt, sizeof(salt), + saltbuf, sizeof(saltbuf)); + if(retval == -1) + return 0; + + retval = libscrypt_mcf(N, r, p, saltbuf, outbuf, dst); + if(retval != 1) + return 0; + + return 1; +} 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..b7141f5f5 --- /dev/null +++ b/libscrypt/libscrypt.h @@ -0,0 +1,67 @@ +/*- + */ +#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); + +#ifndef _MSC_VER +/* Generates a salt. Uses /dev/urandom/ + */ +int libscrypt_salt_gen(/*@out@*/ uint8_t *rand, size_t len); + +/* Creates a hash of a passphrase using a randomly generated salt */ +/* Returns >0 on success, or 0 for fail */ +int libscrypt_hash(char *dst, const char* passphrase, uint32_t N, uint8_t r, + uint8_t p); +#endif + +/* 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_ */ From e789f645eacd213c461277c787bd645c15e52ffd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 22:06:21 +0200 Subject: [PATCH 02/14] Ability to import version 1 keys. Possibly. --- alethzero/Main.ui | 8 ++- alethzero/MainWin.cpp | 27 +++++++++ alethzero/MainWin.h | 1 + libdevcrypto/SecretStore.cpp | 105 +++++++++++++++++++++++++++-------- libdevcrypto/SecretStore.h | 4 +- 5 files changed, 120 insertions(+), 25 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index b5e22409d..f37f532a2 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -157,6 +157,7 @@ + @@ -1656,7 +1657,7 @@ font-size: 14pt &Enable Local Addresses - + true @@ -1745,6 +1746,11 @@ font-size: 14pt Co&nfirm Transactions + + + Import &Secret Key File... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 07f48c0bc..4f1da1c1a 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -813,6 +813,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 diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 9f2082dd0..fdf7a9a72 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -138,6 +138,7 @@ private slots: void on_killAccount_triggered(); void on_importKey_triggered(); void on_importKeyFile_triggered(); + void on_claimPresale_triggered(); void on_exportKey_triggered(); // Account pane diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 7bc870bcf..e422c32f8 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -22,6 +22,7 @@ #include "SecretStore.h" #include #include +#include #include #include #include @@ -42,10 +43,10 @@ SecretStore::~SecretStore() { } -bytes SecretStore::secret(h128 const& _uuid, function const& _pass) const +bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const { 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()) @@ -101,28 +102,71 @@ void SecretStore::save(std::string const& _keysPath) } } +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) + return ret; + return js::mValue(); +} + 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; + js::mValue u = upgraded(contentsString(_file)); + 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), _deleteFile ? _file : string()); + return uuid; + } + else + cwarn << "Invalid JSON in key file" << _file; + return h128(); } std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) @@ -202,13 +246,28 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) 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 = sha3(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 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") diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 18c290c1f..171603053 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -36,7 +36,8 @@ public: SecretStore(); ~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 importSecret(bytes const& _s, std::string const& _pass); void kill(h128 const& _uuid); @@ -48,6 +49,7 @@ private: void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); static std::string encrypt(bytes const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass); + h128 readKey(std::string const& _file, bool _deleteFile); mutable std::unordered_map m_cached; std::unordered_map> m_keys; From b0117dcf575cae67518d37a841c34f0e5409d132 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 19:22:16 +0200 Subject: [PATCH 03/14] Scrypt support --- CMakeLists.txt | 1 + exp/main.cpp | 19 +- libdevcrypto/CMakeLists.txt | 1 + libdevcrypto/Common.cpp | 8 + libdevcrypto/Common.h | 3 + libdevcrypto/SecretStore.cpp | 5 + libscrypt/CMakeLists.txt | 24 ++ libscrypt/LICENSE | 9 + libscrypt/b64.c | 313 ++++++++++++++++++++ libscrypt/b64.h | 10 + libscrypt/crypto-mcf.c | 73 +++++ libscrypt/crypto-scrypt-saltgen.c | 48 ++++ libscrypt/crypto_scrypt-check.c | 100 +++++++ libscrypt/crypto_scrypt-hash.c | 44 +++ libscrypt/crypto_scrypt-hexconvert.c | 35 +++ libscrypt/crypto_scrypt-hexconvert.h | 9 + libscrypt/crypto_scrypt-nosse.c | 342 ++++++++++++++++++++++ libscrypt/libscrypt.h | 67 +++++ libscrypt/libscrypt.version | 8 + libscrypt/sha256.c | 411 +++++++++++++++++++++++++++ libscrypt/sha256.h | 70 +++++ libscrypt/slowequals.c | 26 ++ libscrypt/slowequals.h | 5 + libscrypt/sysendian.h | 144 ++++++++++ 24 files changed, 1771 insertions(+), 4 deletions(-) create mode 100644 libscrypt/CMakeLists.txt create mode 100644 libscrypt/LICENSE create mode 100644 libscrypt/b64.c create mode 100644 libscrypt/b64.h create mode 100644 libscrypt/crypto-mcf.c create mode 100644 libscrypt/crypto-scrypt-saltgen.c create mode 100644 libscrypt/crypto_scrypt-check.c create mode 100644 libscrypt/crypto_scrypt-hash.c create mode 100644 libscrypt/crypto_scrypt-hexconvert.c create mode 100644 libscrypt/crypto_scrypt-hexconvert.h create mode 100644 libscrypt/crypto_scrypt-nosse.c create mode 100644 libscrypt/libscrypt.h create mode 100644 libscrypt/libscrypt.version create mode 100644 libscrypt/sha256.c create mode 100644 libscrypt/sha256.h create mode 100644 libscrypt/slowequals.c create mode 100644 libscrypt/slowequals.h create mode 100644 libscrypt/sysendian.h diff --git a/CMakeLists.txt b/CMakeLists.txt index aafdad099..8239e20a4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -364,6 +364,7 @@ if (JSCONSOLE) endif () add_subdirectory(secp256k1) +add_subdirectory(libscrypt) add_subdirectory(libdevcrypto) if (GENERAL) diff --git a/exp/main.cpp b/exp/main.cpp index 47efe576a..5162d915b 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -36,7 +36,6 @@ #include #include #include -/* #include #include #include @@ -57,17 +56,29 @@ #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/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..2a3561314 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -180,6 +181,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..a0f894a25 100644 --- a/libdevcrypto/Common.h +++ b/libdevcrypto/Common.h @@ -123,6 +123,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..7bc870bcf 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -188,6 +188,11 @@ 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["p"].get_int(), p["r"].get_int(), p["dklen"].get_int()); + } else { cwarn << "Unknown KDF" << o["kdf"].get_str() << "not supported."; 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..8ad3eb826 --- /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..a0e29988c --- /dev/null +++ b/libscrypt/crypto-scrypt-saltgen.c @@ -0,0 +1,48 @@ +#include +#include +#include +#include +#include + +#ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */ +#include +#endif + +#define RNGDEV "/dev/urandom" + +int libscrypt_salt_gen(uint8_t *salt, size_t len) +{ + unsigned char buf[len]; + size_t data_read = 0; + int urandom = open(RNGDEV, O_RDONLY); + + if (urandom < 0) + { + return -1; + } + + while (data_read < len) { + ssize_t result = read(urandom, buf + data_read, len - data_read); + + if (result < 0) + { + if (errno == EINTR || errno == EAGAIN) { + continue; + } + + else { + (void)close(urandom); + return -1; + } + } + + data_read += result; + } + + /* Failures on close() shouldn't occur with O_RDONLY */ + (void)close(urandom); + + memcpy(salt, buf, len); + + return 0; +} 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..4b41007db --- /dev/null +++ b/libscrypt/crypto_scrypt-hash.c @@ -0,0 +1,44 @@ +#include +#include +#include +#include + +#include "b64.h" +#include "libscrypt.h" + +int libscrypt_hash(char *dst, const char *passphrase, uint32_t N, uint8_t r, + uint8_t p) +{ + + int retval; + uint8_t salt[SCRYPT_SALT_LEN]; + uint8_t hashbuf[SCRYPT_HASH_LEN]; + char outbuf[256]; + char saltbuf[256]; + + if(libscrypt_salt_gen(salt, SCRYPT_SALT_LEN) == -1) + { + return 0; + } + + retval = libscrypt_scrypt((const uint8_t*)passphrase, strlen(passphrase), + (uint8_t*)salt, SCRYPT_SALT_LEN, N, r, p, hashbuf, sizeof(hashbuf)); + if(retval == -1) + return 0; + + retval = libscrypt_b64_encode((unsigned char*)hashbuf, sizeof(hashbuf), + outbuf, sizeof(outbuf)); + if(retval == -1) + return 0; + + retval = libscrypt_b64_encode((unsigned char *)salt, sizeof(salt), + saltbuf, sizeof(saltbuf)); + if(retval == -1) + return 0; + + retval = libscrypt_mcf(N, r, p, saltbuf, outbuf, dst); + if(retval != 1) + return 0; + + return 1; +} 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..b7141f5f5 --- /dev/null +++ b/libscrypt/libscrypt.h @@ -0,0 +1,67 @@ +/*- + */ +#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); + +#ifndef _MSC_VER +/* Generates a salt. Uses /dev/urandom/ + */ +int libscrypt_salt_gen(/*@out@*/ uint8_t *rand, size_t len); + +/* Creates a hash of a passphrase using a randomly generated salt */ +/* Returns >0 on success, or 0 for fail */ +int libscrypt_hash(char *dst, const char* passphrase, uint32_t N, uint8_t r, + uint8_t p); +#endif + +/* 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_ */ From a3cc9cc10bffb3dcbdd06a86d7636993b00c7ff6 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 22:06:21 +0200 Subject: [PATCH 04/14] Ability to import version 1 keys. Possibly. --- alethzero/Main.ui | 8 ++- alethzero/MainWin.cpp | 27 +++++++++ alethzero/MainWin.h | 1 + libdevcrypto/SecretStore.cpp | 105 +++++++++++++++++++++++++++-------- libdevcrypto/SecretStore.h | 4 +- 5 files changed, 120 insertions(+), 25 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index b5e22409d..f37f532a2 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -157,6 +157,7 @@ + @@ -1656,7 +1657,7 @@ font-size: 14pt &Enable Local Addresses - + true @@ -1745,6 +1746,11 @@ font-size: 14pt Co&nfirm Transactions + + + Import &Secret Key File... + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 07f48c0bc..4f1da1c1a 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -813,6 +813,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 diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 9f2082dd0..fdf7a9a72 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -138,6 +138,7 @@ private slots: void on_killAccount_triggered(); void on_importKey_triggered(); void on_importKeyFile_triggered(); + void on_claimPresale_triggered(); void on_exportKey_triggered(); // Account pane diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 7bc870bcf..e422c32f8 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -22,6 +22,7 @@ #include "SecretStore.h" #include #include +#include #include #include #include @@ -42,10 +43,10 @@ SecretStore::~SecretStore() { } -bytes SecretStore::secret(h128 const& _uuid, function const& _pass) const +bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const { 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()) @@ -101,28 +102,71 @@ void SecretStore::save(std::string const& _keysPath) } } +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) + return ret; + return js::mValue(); +} + 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; + js::mValue u = upgraded(contentsString(_file)); + 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), _deleteFile ? _file : string()); + return uuid; + } + else + cwarn << "Invalid JSON in key file" << _file; + return h128(); } std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) @@ -202,13 +246,28 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) 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 = sha3(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 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") diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 18c290c1f..171603053 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -36,7 +36,8 @@ public: SecretStore(); ~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 importSecret(bytes const& _s, std::string const& _pass); void kill(h128 const& _uuid); @@ -48,6 +49,7 @@ private: void load(std::string const& _keysPath = getDataDir("web3") + "/keys"); static std::string encrypt(bytes const& _v, std::string const& _pass); static bytes decrypt(std::string const& _v, std::string const& _pass); + h128 readKey(std::string const& _file, bool _deleteFile); mutable std::unordered_map m_cached; std::unordered_map> m_keys; From 29cbfdb25dfb2c464038a6ce0c48a60a5eec1167 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 23:01:51 +0200 Subject: [PATCH 05/14] allow reencrypting key. windows fix. --- alethzero/Main.ui | 6 +++++ alethzero/MainWin.cpp | 16 +++++++++++- alethzero/MainWin.h | 1 + libdevcrypto/SecretStore.cpp | 47 ++++++++++++++++++++++++++++-------- libdevcrypto/SecretStore.h | 8 +++++- libethereum/KeyManager.cpp | 33 ++++++++++++++++--------- libethereum/KeyManager.h | 3 +++ libscrypt/crypto-mcf.c | 2 +- 8 files changed, 91 insertions(+), 25 deletions(-) diff --git a/alethzero/Main.ui b/alethzero/Main.ui index f37f532a2..b97ed08ed 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -159,6 +159,7 @@ + @@ -1751,6 +1752,11 @@ font-size: 14pt Import &Secret Key File... + + + &Re-encrypt Key + + diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 4f1da1c1a..a2d7a3dde 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -2016,7 +2016,7 @@ 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 ( @@ -2036,6 +2036,20 @@ 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"}; + QString kdf = QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your key:", kdfs); + m_keyManager.reencode(a, [&](){ + return QInputDialog::getText(nullptr, "Re-Encrypt Key", "Enter the password for this key to re-encrypt it.", QLineEdit::Password, QString()).toStdString(); + }, (KDF)kdfs.indexOf(kdf)); + } +} + void Main::on_go_triggered() { if (!ui->net->isChecked()) diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index fdf7a9a72..6628c649b 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -137,6 +137,7 @@ private slots: void on_newAccount_triggered(); void on_killAccount_triggered(); void on_importKey_triggered(); + void on_reencryptKey_triggered(); void on_importKeyFile_triggered(); void on_claimPresale_triggered(); void on_exportKey_triggered(); diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index e422c32f8..6a975525b 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -169,24 +169,51 @@ h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) return h128(); } -std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass) +void SecretStore::recode(h128 const& _uuid, string const& _pass, KDF _kdf) +{ + m_keys[_uuid].first = encrypt(secret(_uuid, [&](){ return _pass; }), _pass, _kdf); + save(); +} + +std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf) { js::mObject ret; // KDF info unsigned dklen = 16; - unsigned iterations = 262144; 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 = 262144; + unsigned r = 262144; + ret["kdf"] = "scrypt"; + { + js::mObject params; + params["n"] = (int)iterations; + params["p"] = 1; + params["r"] = 8; + params["dklen"] = (int)dklen; + params["salt"] = toHex(salt); + ret["kdfparams"] = params; + } + derivedKey = scrypt(_pass, salt, iterations, p, r, 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); // cipher info ret["cipher"] = "aes-128-cbc"; diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 171603053..6e66ce967 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -30,6 +30,11 @@ namespace dev { +enum class KDF { + PBKDF2_SHA256, + Scrypt, +}; + class SecretStore { public: @@ -39,6 +44,7 @@ public: 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 importSecret(bytes const& _s, std::string const& _pass); + void recode(h128 const& _uuid, std::string const& _pass, KDF _kdf = KDF::Scrypt); void kill(h128 const& _uuid); // Clear any cached keys. @@ -47,7 +53,7 @@ public: 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); + 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); h128 readKey(std::string const& _file, bool _deleteFile); diff --git a/libethereum/KeyManager.cpp b/libethereum/KeyManager.cpp index 8c3fd28c3..687e13991 100644 --- a/libethereum/KeyManager.cpp +++ b/libethereum/KeyManager.cpp @@ -49,6 +49,12 @@ void KeyManager::create(std::string const& _pass) write(_pass, m_keysFile); } +void KeyManager::reencode(Address const& _address, std::function const& _pass, KDF _kdf) +{ + h128 u = uuid(_address); + store().recode(u, getPassword(u, _pass), _kdf); +} + bool KeyManager::load(std::string const& _pass) { try { @@ -89,18 +95,21 @@ 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()) - { - auto it = m_cachedPasswords.find(kit->second.passHash); - if (it != m_cachedPasswords.end()) - return it->second; - } - std::string p = _pass(); - m_cachedPasswords[hashPassword(p)] = p; - return p; - })); + 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); + if (kit != m_keyInfo.end()) + { + auto it = m_cachedPasswords.find(kit->second.passHash); + if (it != m_cachedPasswords.end()) + return it->second; + } + std::string p = _pass(); + m_cachedPasswords[hashPassword(p)] = p; + return p; } h128 KeyManager::uuid(Address const& _a) const diff --git a/libethereum/KeyManager.h b/libethereum/KeyManager.h index 0a83fcc00..2cacf7bfe 100644 --- a/libethereum/KeyManager.h +++ b/libethereum/KeyManager.h @@ -84,10 +84,13 @@ public: Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; + void reencode(Address const& _address, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); + void kill(h128 const& _id) { kill(address(_id)); } void kill(Address const& _a); private: + std::string getPassword(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; std::string defaultPassword() const { return asString(m_key.ref()); } h256 hashPassword(std::string const& _pass) const; diff --git a/libscrypt/crypto-mcf.c b/libscrypt/crypto-mcf.c index 8ad3eb826..9f7ddc376 100644 --- a/libscrypt/crypto-mcf.c +++ b/libscrypt/crypto-mcf.c @@ -7,7 +7,7 @@ #include #ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */ -#include +//#include #endif #include "libscrypt.h" From 74a69ebb16804f86ea4efc523d20439028d521d7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Wed, 20 May 2015 23:23:23 +0200 Subject: [PATCH 06/14] Windows build fix? --- libscrypt/crypto-scrypt-saltgen.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libscrypt/crypto-scrypt-saltgen.c b/libscrypt/crypto-scrypt-saltgen.c index a0e29988c..3c22f84f7 100644 --- a/libscrypt/crypto-scrypt-saltgen.c +++ b/libscrypt/crypto-scrypt-saltgen.c @@ -5,7 +5,7 @@ #include #ifndef S_SPLINT_S /* Including this here triggers a known bug in splint */ -#include +//#include #endif #define RNGDEV "/dev/urandom" From 82145e1bb46c5a098994e3b2232cf433865d1654 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 21 May 2015 11:03:36 +0200 Subject: [PATCH 07/14] avoid unixy salt gen on windows. --- libscrypt/crypto-scrypt-saltgen.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libscrypt/crypto-scrypt-saltgen.c b/libscrypt/crypto-scrypt-saltgen.c index 3c22f84f7..998a1abcb 100644 --- a/libscrypt/crypto-scrypt-saltgen.c +++ b/libscrypt/crypto-scrypt-saltgen.c @@ -1,3 +1,4 @@ +#ifndef _MSC_VER #include #include #include @@ -46,3 +47,4 @@ int libscrypt_salt_gen(uint8_t *salt, size_t len) return 0; } +#endif From 8399161a8e9b4f0cf3a38e14f5c83e566aad21c2 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 21 May 2015 23:29:27 +0200 Subject: [PATCH 08/14] Nicer password dialog. Cleanups and fixes for secretstore and keyman. --- alethzero/CMakeLists.txt | 3 +- alethzero/GetPassword.ui | 123 ++++++++++++++++++++++++++++ alethzero/Main.ui | 8 +- alethzero/MainWin.cpp | 98 ++++++++++++++++++---- alethzero/MainWin.h | 3 +- alethzero/Transact.cpp | 27 +++++-- alethzero/Transact.h | 1 + libdevcore/Common.h | 3 + libdevcrypto/Common.cpp | 14 ++-- libdevcrypto/Common.h | 16 +++- libdevcrypto/SecretStore.cpp | 152 ++++++++++++++++++++++------------- libdevcrypto/SecretStore.h | 2 +- libethcore/ICAP.h | 2 - libethereum/AccountDiff.h | 2 +- libethereum/KeyManager.cpp | 14 +++- libethereum/KeyManager.h | 12 ++- 16 files changed, 380 insertions(+), 100 deletions(-) create mode 100644 alethzero/GetPassword.ui 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 b97ed08ed..ecdc07ab6 100644 --- a/alethzero/Main.ui +++ b/alethzero/Main.ui @@ -159,6 +159,7 @@ + @@ -1754,7 +1755,12 @@ font-size: 14pt - &Re-encrypt Key + &Re-Encrypt Key + + + + + Re-Encrypt All Keys... diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index a2d7a3dde..1dcdcb67f 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 @@ -2003,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 @@ -2043,11 +2072,48 @@ void Main::on_reencryptKey_triggered() auto hba = ui->ourAccounts->currentItem()->data(Qt::UserRole).toByteArray(); Address a((byte const*)hba.data(), Address::ConstructFromPointer); QStringList kdfs = {"PBKDF2-SHA256", "Scrypt"}; - QString kdf = QInputDialog::getItem(this, "Re-Encrypt Key", "Select a key derivation function to use for storing your key:", kdfs); - m_keyManager.reencode(a, [&](){ - return QInputDialog::getText(nullptr, "Re-Encrypt Key", "Enter the password for this key to re-encrypt it.", QLineEdit::Password, QString()).toStdString(); - }, (KDF)kdfs.indexOf(kdf)); + 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() diff --git a/alethzero/MainWin.h b/alethzero/MainWin.h index 6628c649b..0e74d8f69 100644 --- a/alethzero/MainWin.h +++ b/alethzero/MainWin.h @@ -138,6 +138,7 @@ private slots: 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(); @@ -248,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..fcca822fe 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -76,6 +76,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 +85,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 +306,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 +352,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 +425,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 +435,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 +482,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..8c079a2fe 100644 --- a/alethzero/Transact.h +++ b/alethzero/Transact.h @@ -44,6 +44,7 @@ public: void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); private slots: + void on_from_currentIndexChanged(int) { rejigData(); } void on_destination_currentTextChanged(QString); void on_value_valueChanged(int) { updateFee(); } void on_gas_valueChanged(int) { updateFee(); } 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/libdevcrypto/Common.cpp b/libdevcrypto/Common.cpp index 2a3561314..814f8309e 100644 --- a/libdevcrypto/Common.cpp +++ b/libdevcrypto/Common.cpp @@ -120,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; @@ -139,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; diff --git a/libdevcrypto/Common.h b/libdevcrypto/Common.h index a0f894a25..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 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. -bytes decryptSymNoAuth(h128 const& _k, h128 const& _iv, bytesConstRef _cipher); +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); diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 6a975525b..982b7bae6 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -34,6 +34,56 @@ using namespace dev; namespace js = json_spirit; namespace fs = boost::filesystem; +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() { load(); @@ -45,6 +95,7 @@ SecretStore::~SecretStore() bytes SecretStore::secret(h128 const& _uuid, function const& _pass, bool _useCache) const { + (void)_pass; auto rit = m_cached.find(_uuid); if (_useCache && rit != m_cached.end()) return rit->second; @@ -94,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); @@ -102,48 +153,6 @@ void SecretStore::save(std::string const& _keysPath) } } -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) - return ret; - return js::mValue(); -} - void SecretStore::load(std::string const& _keysPath) { fs::path p(_keysPath); @@ -169,10 +178,17 @@ h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) return h128(); } -void SecretStore::recode(h128 const& _uuid, string const& _pass, KDF _kdf) +bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function const& _pass, KDF _kdf) { - m_keys[_uuid].first = encrypt(secret(_uuid, [&](){ return _pass; }), _pass, _kdf); + cdebug << "recode:" << toUUID(_uuid); + cdebug << "newPass:" << _newPass; + bytes s = secret(_uuid, _pass, true); + if (s.empty()) + return false; + cdebug << "secret:" << toHex(s); + m_keys[_uuid].first = encrypt(s, _newPass, _kdf); save(); + return true; } std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF _kdf) @@ -180,25 +196,26 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF js::mObject ret; // KDF info - unsigned dklen = 16; + unsigned dklen = 32; bytes salt = h256::random().asBytes(); bytes derivedKey; if (_kdf == KDF::Scrypt) { unsigned iterations = 262144; - unsigned p = 262144; - unsigned r = 262144; + unsigned p = 1; + unsigned r = 8; ret["kdf"] = "scrypt"; { js::mObject params; - params["n"] = (int)iterations; - params["p"] = 1; - params["r"] = 8; + params["n"] = (int64_t)iterations; + params["p"] = (int)p; + params["r"] = (int)r; params["dklen"] = (int)dklen; params["salt"] = toHex(salt); ret["kdfparams"] = params; } derivedKey = scrypt(_pass, salt, iterations, p, r, dklen); + cdebug << "derivedKey" << toHex(derivedKey); } else { @@ -213,11 +230,13 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF ret["kdfparams"] = params; } 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; @@ -230,7 +249,9 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF 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); @@ -270,13 +291,23 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) 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 if (o.count("mac")) { h256 mac(o["mac"].get_str()); - h256 macExp = sha3(bytesConstRef(&derivedKey).cropped(derivedKey.size() - 16).toBytes() + cipherText); + 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); @@ -297,12 +328,17 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) 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 6e66ce967..6a62147b8 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -44,7 +44,7 @@ public: 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 importSecret(bytes const& _s, std::string const& _pass); - void recode(h128 const& _uuid, std::string const& _pass, KDF _kdf = KDF::Scrypt); + bool recode(h128 const& _uuid, std::string const& _newPass, std::function const& _pass, KDF _kdf = KDF::Scrypt); void kill(h128 const& _uuid); // Clear any cached keys. 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/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/libethereum/KeyManager.cpp b/libethereum/KeyManager.cpp index 687e13991..4a03d8435 100644 --- a/libethereum/KeyManager.cpp +++ b/libethereum/KeyManager.cpp @@ -49,10 +49,20 @@ void KeyManager::create(std::string const& _pass) write(_pass, m_keysFile); } -void KeyManager::reencode(Address const& _address, std::function const& _pass, KDF _kdf) +bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf) +{ + noteHint(_newPass, _hint); + return store().recode(uuid(_address), _newPass, _pass, _kdf); +} + +bool KeyManager::recode(Address const& _address, SemanticPassword _newPass, std::function const& _pass, KDF _kdf) { h128 u = uuid(_address); - store().recode(u, getPassword(u, _pass), _kdf); + if (_newPass == SemanticPassword::Existing) + return store().recode(u, getPassword(u, _pass), _pass, _kdf); + else if (_newPass == SemanticPassword::Master) + return store().recode(u, defaultPassword(), _pass, _kdf); + return false; } bool KeyManager::load(std::string const& _pass) diff --git a/libethereum/KeyManager.h b/libethereum/KeyManager.h index 2cacf7bfe..2fc47e9b5 100644 --- a/libethereum/KeyManager.h +++ b/libethereum/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. /** @@ -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,7 +93,8 @@ public: Secret secret(Address const& _address, std::function const& _pass = DontKnowThrow) const; Secret secret(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; - void reencode(Address const& _address, std::function const& _pass = DontKnowThrow, KDF _kdf = KDF::Scrypt); + 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); From 422be5eb46052224efdacae652ccd0483b305323 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 22 May 2015 00:22:10 +0200 Subject: [PATCH 09/14] Fixes and use actual master password for encrypting by default. --- alethzero/Transact.h | 12 ++++----- libdevcrypto/SecretStore.cpp | 13 ++++----- libethereum/KeyManager.cpp | 52 +++++++++++++++++++++++++++--------- libethereum/KeyManager.h | 5 +++- 4 files changed, 55 insertions(+), 27 deletions(-) diff --git a/alethzero/Transact.h b/alethzero/Transact.h index 8c079a2fe..c14fcc7e1 100644 --- a/alethzero/Transact.h +++ b/alethzero/Transact.h @@ -44,13 +44,13 @@ public: void setEnvironment(dev::AddressHash const& _accounts, dev::eth::Client* _eth, NatSpecFace* _natSpecDB); private slots: - void on_from_currentIndexChanged(int) { rejigData(); } + 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/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 982b7bae6..68e18b5ab 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -180,12 +180,10 @@ h128 SecretStore::readKey(std::string const& _file, bool _deleteFile) bool SecretStore::recode(h128 const& _uuid, string const& _newPass, std::function const& _pass, KDF _kdf) { - cdebug << "recode:" << toUUID(_uuid); - cdebug << "newPass:" << _newPass; +// cdebug << "recode:" << toUUID(_uuid); bytes s = secret(_uuid, _pass, true); if (s.empty()) return false; - cdebug << "secret:" << toHex(s); m_keys[_uuid].first = encrypt(s, _newPass, _kdf); save(); return true; @@ -215,7 +213,6 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF ret["kdfparams"] = params; } derivedKey = scrypt(_pass, salt, iterations, p, r, dklen); - cdebug << "derivedKey" << toHex(derivedKey); } else { @@ -230,13 +227,13 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF ret["kdfparams"] = params; } derivedKey = pbkdf2(_pass, salt, iterations, dklen); - cdebug << "derivedKey" << toHex(derivedKey); } +// cdebug << "derivedKey" << toHex(derivedKey); // cipher info ret["cipher"] = "aes-128-ctr"; h128 key(derivedKey, h128::AlignLeft); - cdebug << "cipherKey" << key.hex(); +// cdebug << "cipherKey" << key.hex(); h128 iv = h128::random(); { js::mObject params; @@ -250,8 +247,8 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF // and mac. h256 mac = sha3(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); - cdebug << "macBody" << toHex(ref(derivedKey).cropped(16, 16).toBytes() + cipherText); - cdebug << "mac" << mac.hex(); +// 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); diff --git a/libethereum/KeyManager.cpp b/libethereum/KeyManager.cpp index 4a03d8435..1edfe9cff 100644 --- a/libethereum/KeyManager.cpp +++ b/libethereum/KeyManager.cpp @@ -52,17 +52,27 @@ void KeyManager::create(std::string const& _pass) bool KeyManager::recode(Address const& _address, std::string const& _newPass, std::string const& _hint, std::function const& _pass, KDF _kdf) { noteHint(_newPass, _hint); - return store().recode(uuid(_address), _newPass, _pass, _kdf); + 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) - return store().recode(u, getPassword(u, _pass), _pass, _kdf); + p = getPassword(u, _pass); else if (_newPass == SemanticPassword::Master) - return store().recode(u, defaultPassword(), _pass, _kdf); - return false; + p = defaultPassword(); + else + return false; + + return recode(_address, p, string(), _pass, _kdf); } bool KeyManager::load(std::string const& _pass) @@ -87,7 +97,8 @@ bool KeyManager::load(std::string const& _pass) m_password = (string)s[3]; } m_cachedPasswords[hashPassword(m_password)] = m_password; - m_cachedPasswords[hashPassword(defaultPassword())] = defaultPassword(); + m_cachedPasswords[hashPassword(asString(m_key.ref()))] = asString(m_key.ref()); + m_cachedPasswords[m_master = hashPassword(_pass)] = _pass; return true; } catch (...) { @@ -111,15 +122,29 @@ Secret KeyManager::secret(h128 const& _uuid, function const& _pas 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) { - auto it = m_cachedPasswords.find(kit->second.passHash); - if (it != m_cachedPasswords.end()) - return it->second; + std::string p = _pass(); + if (p.empty()) + break; + if (hashPassword(p) == _passHash || !_passHash) + { + 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 @@ -190,7 +215,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; } @@ -213,6 +238,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/libethereum/KeyManager.h index 2fc47e9b5..3fcf83c3e 100644 --- a/libethereum/KeyManager.h +++ b/libethereum/KeyManager.h @@ -101,11 +101,13 @@ public: private: std::string getPassword(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; - std::string defaultPassword() const { return asString(m_key.ref()); } + 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; @@ -127,6 +129,7 @@ private: SecretStore m_store; mutable h128 m_key; + mutable h256 m_master; mutable std::string m_keysFile; }; From 9ba9536f73dcbb9714a6c5c180c67eb2ccac5ca9 Mon Sep 17 00:00:00 2001 From: Vitalik Buterin Date: Thu, 21 May 2015 21:19:57 -0400 Subject: [PATCH 10/14] Switched r and p --- libdevcrypto/SecretStore.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libdevcrypto/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 68e18b5ab..602ccce8d 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -206,13 +206,13 @@ std::string SecretStore::encrypt(bytes const& _v, std::string const& _pass, KDF { js::mObject params; params["n"] = (int64_t)iterations; - params["p"] = (int)p; params["r"] = (int)r; + params["p"] = (int)p; params["dklen"] = (int)dklen; params["salt"] = toHex(salt); ret["kdfparams"] = params; } - derivedKey = scrypt(_pass, salt, iterations, p, r, dklen); + derivedKey = scrypt(_pass, salt, iterations, r, p, dklen); } else { @@ -280,7 +280,7 @@ bytes SecretStore::decrypt(std::string const& _v, std::string const& _pass) 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["p"].get_int(), p["r"].get_int(), p["dklen"].get_int()); + 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 { From fcb629acdabf017b537ff873512abafdb6b8ccb7 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 22 May 2015 18:14:13 +0200 Subject: [PATCH 11/14] ethkey utility with bare key tools. --- CMakeLists.txt | 19 +- alethzero/MainWin.h | 2 +- alethzero/Transact.cpp | 3 +- eth/main.cpp | 3 +- ethkey/CMakeLists.txt | 33 ++ ethkey/KeyAux.h | 351 +++++++++++++++++++++ ethkey/main.cpp | 84 +++++ ethminer/MinerAux.h | 4 +- exp/main.cpp | 3 +- libdevcore/CommonData.h | 9 + libdevcrypto/SecretStore.cpp | 2 +- libdevcrypto/SecretStore.h | 14 +- {libethereum => libethcore}/KeyManager.cpp | 7 +- {libethereum => libethcore}/KeyManager.h | 8 +- libweb3jsonrpc/AccountHolder.cpp | 3 +- 15 files changed, 528 insertions(+), 17 deletions(-) create mode 100644 ethkey/CMakeLists.txt create mode 100644 ethkey/KeyAux.h create mode 100644 ethkey/main.cpp rename {libethereum => libethcore}/KeyManager.cpp (96%) rename {libethereum => libethcore}/KeyManager.h (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8239e20a4..e7461eb1c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,6 +251,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 +270,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 +308,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}") @@ -387,10 +400,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/MainWin.h b/alethzero/MainWin.h index 0e74d8f69..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" diff --git a/alethzero/Transact.cpp b/alethzero/Transact.cpp index fcca822fe..b485091d9 100644 --- a/alethzero/Transact.cpp +++ b/alethzero/Transact.cpp @@ -39,7 +39,8 @@ #include #include #include -#include +#include + #if ETH_SERPENT #include #include 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..daac72588 --- /dev/null +++ b/ethkey/KeyAux.h @@ -0,0 +1,351 @@ +#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) +{ + string pass; + 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; + if (!_keyManager.haveHint(pass)) + { + cout << "Enter a hint to help you remember this password: " << flush; + cin >> hint; + } + return make_pair(pass, hint); +} + +class KeyCLI +{ +public: + enum class OperationMode + { + None, + ListBare, + NewBare, + ImportBare, + ExportBare, + RecodeBare, + FirstWallet, + CreateWallet, + List = FirstWallet, + 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 == "-n" || arg == "--new") + m_mode = OperationMode::New; + else 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 == "--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::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::FirstWallet) + { + 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(); + store.importSecret(k.secret().asBytes(), m_lock); + cout << "Created key " << k.address().abridged() << 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; + } + 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); + } + } + } + + 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 + << "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 + << 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; + 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 5162d915b..f0574fa7c 100644 --- a/exp/main.cpp +++ b/exp/main.cpp @@ -49,7 +49,8 @@ #include #include #include -#include +#include + #include #include #include 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/SecretStore.cpp b/libdevcrypto/SecretStore.cpp index 602ccce8d..c6f9803fa 100644 --- a/libdevcrypto/SecretStore.cpp +++ b/libdevcrypto/SecretStore.cpp @@ -84,7 +84,7 @@ static js::mValue upgraded(std::string const& _s) return js::mValue(); } -SecretStore::SecretStore() +SecretStore::SecretStore(std::string const& _path): m_path(_path) { load(); } diff --git a/libdevcrypto/SecretStore.h b/libdevcrypto/SecretStore.h index 6a62147b8..c4e5d5cf7 100644 --- a/libdevcrypto/SecretStore.h +++ b/libdevcrypto/SecretStore.h @@ -38,7 +38,7 @@ enum class KDF { class SecretStore { public: - SecretStore(); + SecretStore(std::string const& _path = defaultPath()); ~SecretStore(); bytes secret(h128 const& _uuid, std::function const& _pass, bool _useCache = true) const; @@ -47,18 +47,26 @@ public: 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; + 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"); + void save(std::string const& _keysPath); + void load(std::string const& _keysPath); + void save() { save(m_path); } + 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); h128 readKey(std::string const& _file, bool _deleteFile); mutable std::unordered_map m_cached; std::unordered_map> m_keys; + + std::string m_path; }; } diff --git a/libethereum/KeyManager.cpp b/libethcore/KeyManager.cpp similarity index 96% rename from libethereum/KeyManager.cpp rename to libethcore/KeyManager.cpp index 1edfe9cff..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() @@ -96,8 +96,11 @@ 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; + 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; } diff --git a/libethereum/KeyManager.h b/libethcore/KeyManager.h similarity index 96% rename from libethereum/KeyManager.h rename to libethcore/KeyManager.h index 3fcf83c3e..155805c5d 100644 --- a/libethereum/KeyManager.h +++ b/libethcore/KeyManager.h @@ -61,7 +61,7 @@ enum class SemanticPassword 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; } @@ -99,6 +99,8 @@ public: 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 getPassword(h128 const& _uuid, std::function const& _pass = DontKnowThrow) const; std::string getPassword(h256 const& _passHash, std::function const& _pass = DontKnowThrow) const; @@ -127,10 +129,10 @@ private: // we have an upgrade strategy. std::string m_password; - SecretStore m_store; + mutable std::string m_keysFile; mutable h128 m_key; mutable h256 m_master; - mutable std::string m_keysFile; + SecretStore m_store; }; } 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; From a7490d16cca1005fc5c371aa18e229f6d62cd99d Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Fri, 22 May 2015 22:07:31 +0200 Subject: [PATCH 12/14] List and add wallet keys. --- ethkey/KeyAux.h | 90 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 12 deletions(-) diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index daac72588..3c4a625a4 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -64,22 +64,23 @@ string createPassword(std::string const& _prompt) // return make_pair(ret, hint); } -pair createPassword(KeyManager& _keyManager, std::string const& _prompt) +pair createPassword(KeyManager& _keyManager, std::string const& _prompt, std::string const& _pass = std::string(), std::string const& _hint = std::string()) { - string pass; - while (true) + string pass = _pass; + while (pass.empty()) { 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; + pass.clear(); } - string hint; - if (!_keyManager.haveHint(pass)) + string hint = _hint; + if (hint.empty() && !_keyManager.haveHint(pass)) { cout << "Enter a hint to help you remember this password: " << flush; - cin >> hint; + getline(cin, hint); } return make_pair(pass, hint); } @@ -95,6 +96,7 @@ public: ImportBare, ExportBare, RecodeBare, + KillBare, FirstWallet, CreateWallet, List = FirstWallet, @@ -140,6 +142,8 @@ public: 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") @@ -161,7 +165,7 @@ public: m_mode = OperationMode::Recode; else if (arg == "--no-icap") m_icap = false; - else if (m_mode == OperationMode::ImportBare || m_mode == OperationMode::Recode || m_mode == OperationMode::Export || m_mode == OperationMode::RecodeBare || m_mode == OperationMode::ExportBare) + 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; @@ -202,10 +206,10 @@ public: if (m_lock.empty()) m_lock = createPassword("Enter a password with which to secure this account: "); auto k = makeKey(); - store.importSecret(k.secret().asBytes(), m_lock); - cout << "Created key " << k.address().abridged() << endl; - cout << "Address: " << k.address().hex() << endl; - cout << "ICAP: " << ICAP(k.address()).encoded() << endl; + 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: @@ -244,6 +248,16 @@ public: 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; } } @@ -267,6 +281,55 @@ public: cerr << "Couldn't open wallet. Does it exist?" << endl; exit(-1); } + switch (m_mode) + { + case OperationMode::New: + { + cout << "Enter a description of this key: " << flush; + string info; + getline(cin, info); + + 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(); + h128 u = m_lock.empty() ? 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; + if (!m_lockHint.empty()) + cout << " Password hint: " << m_lockHint; + 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; + } } } @@ -284,6 +347,7 @@ public: << " --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 @@ -303,7 +367,8 @@ public: << " --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 + << " --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 @@ -336,6 +401,7 @@ private: string m_masterPassword; strings m_unlocks; string m_lock; + string m_lockHint; bool m_icap = true; /// Creating From db48be76699957f28ddff4be92456f05c1bfc82a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 23 May 2015 10:58:46 +0200 Subject: [PATCH 13/14] Adding and listing wallet keys. --- ethkey/KeyAux.h | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/ethkey/KeyAux.h b/ethkey/KeyAux.h index 3c4a625a4..df2ba5a5a 100644 --- a/ethkey/KeyAux.h +++ b/ethkey/KeyAux.h @@ -67,17 +67,17 @@ string createPassword(std::string const& _prompt) pair createPassword(KeyManager& _keyManager, std::string const& _prompt, std::string const& _pass = std::string(), std::string const& _hint = std::string()) { string pass = _pass; - while (pass.empty()) - { - 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; - pass.clear(); - } + 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() && !_keyManager.haveHint(pass)) + if (hint.empty() && !pass.empty() && !_keyManager.haveHint(pass)) { cout << "Enter a hint to help you remember this password: " << flush; getline(cin, hint); @@ -97,9 +97,8 @@ public: ExportBare, RecodeBare, KillBare, - FirstWallet, CreateWallet, - List = FirstWallet, + List, New, Import, Export, @@ -112,9 +111,7 @@ public: bool interpretOption(int& i, int argc, char** argv) { string arg = argv[i]; - if (arg == "-n" || arg == "--new") - m_mode = OperationMode::New; - else if (arg == "--wallet-path" && i + 1 < argc) + if (arg == "--wallet-path" && i + 1 < argc) m_walletPath = argv[++i]; else if (arg == "--secrets-path" && i + 1 < argc) m_secretsPath = argv[++i]; @@ -192,7 +189,7 @@ public: else wallet.create(m_masterPassword); } - else if (m_mode < OperationMode::FirstWallet) + else if (m_mode < OperationMode::CreateWallet) { SecretStore store(m_secretsPath); switch (m_mode) @@ -285,17 +282,16 @@ public: { case OperationMode::New: { - cout << "Enter a description of this key: " << flush; - string info; - getline(cin, info); - 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(); - h128 u = m_lock.empty() ? wallet.import(k.secret(), m_name) : wallet.import(k.secret(), m_name, m_lock, m_lockHint); + 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; - if (!m_lockHint.empty()) - cout << " Password hint: " << m_lockHint; + 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; From 3fcbab1423af5d5dd13bdb94d6c9b760f4fcb98a Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Sat, 23 May 2015 11:09:39 +0200 Subject: [PATCH 14/14] Fixed #2007 --- alethzero/MainWin.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/alethzero/MainWin.cpp b/alethzero/MainWin.cpp index 1dcdcb67f..6a11736a4 100644 --- a/alethzero/MainWin.cpp +++ b/alethzero/MainWin.cpp @@ -2048,12 +2048,11 @@ void Main::on_killAccount_triggered() 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())