diff options
Diffstat (limited to 'src')
39 files changed, 550 insertions, 1654 deletions
diff --git a/src/blockchain_utilities/CMakeLists.txt b/src/blockchain_utilities/CMakeLists.txt index fe57895e1..37bca671f 100644 --- a/src/blockchain_utilities/CMakeLists.txt +++ b/src/blockchain_utilities/CMakeLists.txt @@ -28,7 +28,9 @@ set(blocksdat "") if(PER_BLOCK_CHECKPOINT) - if(APPLE) + if(APPLE AND DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(APPLE AND NOT DEPENDS) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) else() add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) diff --git a/src/checkpoints/CMakeLists.txt b/src/checkpoints/CMakeLists.txt index 02bb2891a..715006522 100644 --- a/src/checkpoints/CMakeLists.txt +++ b/src/checkpoints/CMakeLists.txt @@ -27,9 +27,13 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(APPLE) - find_library(IOKIT_LIBRARY IOKit) - mark_as_advanced(IOKIT_LIBRARY) - list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + if(DEPENDS) + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) + list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + endif() endif() set(checkpoints_sources diff --git a/src/crypto/crypto_ops_builder/ietf.txt b/src/crypto/crypto_ops_builder/ietf.txt index 0736f71ec..609f5e75a 100644 --- a/src/crypto/crypto_ops_builder/ietf.txt +++ b/src/crypto/crypto_ops_builder/ietf.txt @@ -1,1402 +1,4 @@ - +https://tools.ietf.org/id/draft-josefsson-eddsa-ed25519-02.txt -[Docs] [txt|pdf] [Tracker] [Email] [Diff1] [Diff2] [Nits] - -Versions: 00 01 02 - -Network Working Group S. Josefsson -Internet-Draft SJD AB -Intended status: Informational N. Moeller -Expires: August 26, 2015 - February 22, 2015 - - - EdDSA and Ed25519 - - draft-josefsson-eddsa-ed25519-02 - - -Abstract - - The elliptic curve signature scheme EdDSA and one instance of it - called Ed25519 is described. An example implementation and test - vectors are provided. - -Status of This Memo - - This Internet-Draft is submitted in full conformance with the - provisions of BCP 78 and BCP 79. - - Internet-Drafts are working documents of the Internet Engineering - Task Force (IETF). Note that other groups may also distribute - working documents as Internet-Drafts. The list of current Internet- - Drafts is at http://datatracker.ietf.org/drafts/current/. - - Internet-Drafts are draft documents valid for a maximum of six months - and may be updated, replaced, or obsoleted by other documents at any - time. It is inappropriate to use Internet-Drafts as reference - material or to cite them other than as "work in progress." - - This Internet-Draft will expire on August 26, 2015. - -Copyright Notice - - Copyright (c) 2015 IETF Trust and the persons identified as the - document authors. All rights reserved. - - This document is subject to BCP 78 and the IETF Trust's Legal - Provisions Relating to IETF Documents - (http://trustee.ietf.org/license-info) in effect on the date of - publication of this document. Please review these documents - carefully, as they describe your rights and restrictions with respect - to this document. Code Components extracted from this document must - include Simplified BSD License text as described in Section 4.e of - the Trust Legal Provisions and are provided without warranty as - described in the Simplified BSD License. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 1] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -Table of Contents - - 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . 2 - 2. Notation . . . . . . . . . . . . . . . . . . . . . . . . . . 3 - 3. Background . . . . . . . . . . . . . . . . . . . . . . . . . 3 - 4. EdDSA . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 - 4.1. Encoding . . . . . . . . . . . . . . . . . . . . . . . . 4 - 4.2. Keys . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 4.3. Sign . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 4.4. Verify . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 5. Ed25519 . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 - 5.1. Modular arithmetic . . . . . . . . . . . . . . . . . . . 6 - 5.2. Encoding . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.3. Decoding . . . . . . . . . . . . . . . . . . . . . . . . 6 - 5.4. Point addition . . . . . . . . . . . . . . . . . . . . . 7 - 5.5. Key Generation . . . . . . . . . . . . . . . . . . . . . 8 - 5.6. Sign . . . . . . . . . . . . . . . . . . . . . . . . . . 8 - 5.7. Verify . . . . . . . . . . . . . . . . . . . . . . . . . 9 - 5.8. Python illustration . . . . . . . . . . . . . . . . . . . 9 - 6. Test Vectors for Ed25519 . . . . . . . . . . . . . . . . . . 14 - 7. Acknowledgements . . . . . . . . . . . . . . . . . . . . . . 17 - 8. IANA Considerations . . . . . . . . . . . . . . . . . . . . . 18 - 9. Security Considerations . . . . . . . . . . . . . . . . . . . 18 - 9.1. Side-channel leaks . . . . . . . . . . . . . . . . . . . 18 - 10. References . . . . . . . . . . . . . . . . . . . . . . . . . 18 - 10.1. Normative References . . . . . . . . . . . . . . . . . . 18 - 10.2. Informative References . . . . . . . . . . . . . . . . . 18 - Appendix A. Ed25519 Python Library . . . . . . . . . . . . . . . 19 - Appendix B. Library driver . . . . . . . . . . . . . . . . . . . 23 - Authors' Addresses . . . . . . . . . . . . . . . . . . . . . . . 24 - -1. Introduction - - - The Edwards-curve Digital Signature Algorithm (EdDSA) is a variant of - Schnorr's signature system with Twisted Edwards curves. EdDSA needs - to be instantiated with certain parameters and this document describe - Ed25519 - an instantiation of EdDSA in a curve over GF(2^255-19). To - facilitate adoption in the Internet community of Ed25519, this - document describe the signature scheme in an implementation-oriented - way, and we provide sample code and test vectors. - - The advantages with EdDSA and Ed25519 include: - - 1. High-performance on a variety of platforms. - - 2. Does not require the use of a unique random number for each - signature. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 2] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - 3. More resilient to side-channel attacks. - - 4. Small public keys (32 bytes) and signatures (64 bytes). - - 5. The formulas are "strongly unified", i.e., they are valid for all - points on the curve, with no exceptions. This obviates the need - for EdDSA to perform expensive point validation on untrusted - public values. - - 6. Collision resilience, meaning that hash-function collisions do - not break this system. - - For further background, see the original EdDSA paper [EDDSA]. - -2. Notation - - - The following notation is used throughout the document: - - GF(p) finite field with p elements - - x^y x multiplied by itself y times - - B generator of the group or subgroup of interest - - n B B added to itself n times. - - h_i the i'th bit of h - - a || b (bit-)string a concatenated with (bit-)string b - -3. Background - - - EdDSA is defined using an elliptic curve over GF(p) of the form - - -x^2 + y^2 = 1 + d x^2 y^2 - - In general, p could be a prime power, but it is usually chosen as a - prime number. It is required that p = 1 modulo 4 (which implies that - -1 is a square modulo p) and that d is a non-square modulo p. For - Ed25519, the curve used is equivalent to Curve25519 [CURVE25519], - under a change of coordinates, which means that the difficulty of the - discrete logarithm problem is the same as for Curve25519. - - Points on this curve form a group under addition, (x3, y3) = (x1, y1) - + (x2, y2), with the formulas - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 3] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - x1 y2 + x2 y1 y1 y2 + x1 x2 - x3 = -------------------, y3 = ------------------- - 1 + d x1 x2 y1 y2 1 - d x1 x2 y1 y2 - - The neutral element in the group is (0, 1). - - Unlike manyy other curves used for cryptographic applications, these - formulas are "strongly unified": they are valid for all points on the - curve, with no exceptions. In particular, the denominators are non- - zero for all input points. - - There are more efficient formulas, which are still strongly unified, - which use homogeneous coordinates to avoid the expensive modulo p - inversions. See [Faster-ECC] and [Edwards-revisited]. - -4. EdDSA - - - EdDSA is a digital signature system with several parameters. The - generic EdDSA digital signature system is normally not implemented - directly, but instead a particular instance of EdDSA (like Ed25519) - is implemented. A precise explanation of the generic EdDSA is thus - not particulary useful for implementers, but for background and - completeness, a succint description of the generic EdDSA algorithm is - given here. - - EdDSA has seven parameters: - - 1. an integer b >= 10. - - 2. a cryptographic hash function H producing 2b-bit outputs. - - 3. a prime power p congruent to 1 modulo 4. - - 4. a (b-1)-bit encoding of elements of the finite field GF(p). - - 5. a non-square element d of GF(p) - - 6. an element B != (0,1) of the set E = { (x,y) is a member of GF(p) - x GF(p) such that -x^2 + y^2 = 1 + dx^2y^2 }. - - 7. a prime q, of size b-3 bits, such that qB = (0, 1), i.e., q is - the order of B or a multiple thereof. - -4.1. Encoding - - - An element (x,y) of E is encoded as a b-bit string called ENC(x,y) - which is the (b-1)-bit encoding of y concatenated with one bit that - is 1 if x is negative and 0 if x is not negative. Negative elements - - - -Josefsson & Moeller Expires August 26, 2015 [Page 4] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - of GF(q) are those x which the (b-1)-bit encoding of x is - lexicographically larger than the (b-1)-bit encoding of -x. - -4.2. Keys - - - An EdDSA secret key is a b-bit string k. Let the hash H(k) = (h_0, - h_1, ..., h_(2b-1)) determine an integer a which is 2^(b-2) plus the - sum of m = 2^i * h_i for all i equal or larger than 3 and equal to or - less than b-3 such that m is a member of the set { 2^(b-2), 2^(b-2) + - 8, ..., 2^(b-1) - 8 }. The EdDSA public key is ENC(A) = ENC(aB). - The bits h_b, ..., h_(2b-1) is used below during signing. - -4.3. Sign - - - The signature of a message M under a secret key k is the 2b-bit - string ENC(R) || ENC'(S), where ENC'(S) is defined as the b-bit - little-endian encoding of S. R and S are derived as follows. First - define r = H(h_b, ... h_(2b-1)), M) interpreting 2b-bit strings in - little-endian form as integers in {0, 1, ..., 2^(2b)-1}. Let R=rB - and S=(r+H(ENC(R) || ENC(A) || M)a) mod l. - -4.4. Verify - - - To verify a signature ENC(R) || ENC'(S) on a message M under a public - key ENC(A), proceed as follows. Parse the inputs so that A and R is - an element of E, and S is a member of the set {0, 1, ..., l-1 }. - Compute H' = H(ENC(R) || ENC(A) || M) and check the group equation - 8SB = 8R + 8H'A in E. Verification is rejected if parsing fails or - the group equation does not hold. - -5. Ed25519 - - - Theoretically, Ed25519 is EdDSA instantiated with b=256, H being - SHA-512 [RFC4634], p is the prime 2^255-19, the 255-bit encoding of - GF(2^255-19) being the little-endian encoding of {0, 1, ..., - 2^255-20}, q is the prime 2^252 + 0x14def9dea2f79cd65812631a5cf5d3ed, - d = -121665/121666 which is a member of GF(p), and B is the unique - point (x, 4/5) in E for which x is "positive", which with the - encoding used simply means that the least significant bit of x is 0. - The curve p, prime q, d and B follows from [I-D.irtf-cfrg-curves]. - - Written out explicitly, B is the point (15112221349535400772501151409 - 588531511454012693041857206046113283949847762202, 4631683569492647816 - 9428394003475163141307993866256225615783033603165251855960). - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 5] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -5.1. Modular arithmetic - - - For advise on how to implement arithmetic modulo p = 2^255 - 1 - efficiently and securely, see Curve25519 [CURVE25519]. For inversion - modulo p, it is recommended to use the identity x^-1 = x^(p-2) (mod - p). - - For point decoding or "decompression", square roots modulo p are - needed. They can be computed using the Tonelli-Shanks algorithm, or - the special case for p = 5 (mod 8). To find a square root of a, - first compute the candidate root x = a^((p+3)/8) (mod p). Then there - are three cases: - - x^2 = a (mod p). Then x is a square root. - - x^2 = -a (mod p). Then 2^((p-1)/4) x is a square root. - - a is not a square modulo p. - -5.2. Encoding - - - All values are coded as octet strings, and integers are coded using - little endian convention. I.e., a 32-octet string h h[0],...h[31] - represents the integer h[0] + 2^8 h[1] + ... + 2^248 h[31]. - - A curve point (x,y), with coordiantes in the range 0 <= x,y < p, is - coded as follows. First encode the y-coordinate as a little-endian - string of 32 octets. The most significant bit of the final octet is - always zero. To form the encoding of the point, copy the least - significant bit of the x-coordinate to the most significant bit of - the final octet. - -5.3. Decoding - - - Decoding a point, given as a 32-octet string, is a little more - complicated. - - 1. First interpret the string as an integer in little-endian - representation. Bit 255 of this number is the least significant - bit of the x-coordinate, and denote this value x_0. The - y-coordinate is recovered simply by clearing this bit. If the - resulting value is >= p, decoding fails. - - 2. To recover the x coordinate, the curve equation implies x^2 = - (y^2 - 1) / (d y^2 + 1) (mod p). Since d is a non-square and -1 - is a square, the numerator, (d y^2 + 1), is always invertible - modulo p. Let u = y^2 - 1 and v = d y^2 + 1. To compute the - square root of (u/v), the first step is to compute the candidate - - - -Josefsson & Moeller Expires August 26, 2015 [Page 6] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - root x = (u/v)^((p+3)/8). This can be done using the following - trick, to use a single modular powering for both the inversion of - v and the square root: - - (p+3)/8 3 (p-5)/8 - x = (u/v) = u v (u v^7) (mod p) - - 3. Again, there are three cases: - - 1. If v x^2 = u (mod p), x is a square root. - - 2. If v x^2 = -u (mod p), set x <-- x 2^((p-1)/4), which is a - square root. - - 3. Otherwise, no square root exists modulo p, and decoding - fails. - - 4. Finally, use the x_0 bit to select the right square root. If x = - 0, and x_0 = 1, decoding fails. Otherwise, if x_0 != x mod 2, - set x <-- p - x. Return the decoded point (x,y). - -5.4. Point addition - - - For point addition, the following method is recommended. A point - (x,y) is represented in extended homogeneous coordinates (X, Y, Z, - T), with x = X/Z, y = Y/Z, x y = T/Z. - - The following formulas for adding two points, (x3,y3) = - (x1,y1)+(x2,y2) are described in [Edwards-revisited], section 3.1. - They are strongly unified, i.e., they work for any pair of valid - input points. - - A = (Y1-X1)*(Y2-X2) - B = (Y1+X1)*(Y2+X2) - C = T1*2*d*T2 - D = Z1*2*Z2 - E = B-A - F = D-C - G = D+C - H = B+A - X3 = E*F - Y3 = G*H - T3 = E*H - Z3 = F*G - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 7] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -5.5. Key Generation - - - The secret is 32 octets (256 bits, corresponding to b) of - cryptographically-secure random data. See [RFC4086] for a discussion - about randomness. - - The 32-byte public key is generated by the following steps. - - 1. Hash the 32-byte secret using SHA-512, storing the digest in a - 64-octet large buffer, denoted h. Only the lower 32 bytes are - used for generating the public key. - - 2. Prune the buffer. In C terminology: - - h[0] &= ~0x07; - h[31] &= 0x7F; - h[31] |= 0x40; - - 3. Interpret the buffer as the little-endian integer, forming a - secret scalar a. Perform a known-base-point scalar - multiplication a B. - - 4. The public key A is the encoding of the point aB. First encode - the y coordinate (in the range 0 <= y < p) as a little-endian - string of 32 octets. The most significant bit of the final octet - is always zero. To form the encoding of the point aB, copy the - least significant bit of the x coordinate to the most significant - bit of the final octet. The result is the public key. - -5.6. Sign - - - The imputs to the signing procedure is the secret key, a 32-octet - string, and a message M of arbitrary size. - - 1. Hash the secret key, 32-octets, using SHA-512. Let h denote the - resulting digest. Construct the secret scalar a from the first - half of the digest, and the corresponding public key A, as - described in the previous section. Let prefix denote the second - half of the hash digest, h[32],...,h[63]. - - 2. Compute SHA-512(prefix || M), where M is the message to be - signed. Interpret the 64-octet digest as a little-endian integer - r. - - 3. Compute the point rB. For efficiency, do this by first reducing - r modulo q, the group order of B. Let the string R be the - encoding of this point. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 8] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - 4. Compute SHA512(R || A || M), and interpret the 64-octet digest as - a little-endian integer k. - - 5. Compute s = (r + k a) mod q. For efficiency, again reduce k - modulo q first. - - 6. Form the signature of the concatenation of R (32 octets) and the - little-endian encoding of s (32 octets, three most significant - bits of the final octets always zero). - -5.7. Verify - - - 1. To verify a signature on a message M, first split the signature - into two 32-octet halves. Decode the first half as a point R, - and the second half as an integer s, in the range 0 <= s < q. If - the decoding fails, the signature is invalid. - - 2. Compute SHA512(R || A || M), and interpret the 64-octet digest as - a little-endian integer k. - - 3. Check the group equation 8s B = 8 R + 8k A. It's sufficient, but - not required, to instead check s B = R + k A. - -5.8. Python illustration - - - The rest of this section describes how Ed25519 can be implemented in - Python (version 3.2 or later) for illustration. See appendix A for - the complete implementation and appendix B for a test-driver to run - it through some test vectors. - - First some preliminaries that will be needed. - - - - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 9] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - import hashlib - - def sha512(s): - return hashlib.sha512(s).digest() - - # Base field Z_p - p = 2**255 - 19 - - def modp_inv(x): - return pow(x, p-2, p) - - # Curve constant - d = -121665 * modp_inv(121666) % p - - # Group order - q = 2**252 + 27742317777372353535851937790883648493 - - def sha512_modq(s): - return int.from_bytes(sha512(s), "little") % q - - Then follows functions to perform point operations. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 10] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -# Points are represented as tuples (X, Y, Z, T) of extended coordinates, -# with x = X/Z, y = Y/Z, x*y = T/Z - -def point_add(P, Q): - A = (P[1]-P[0])*(Q[1]-Q[0]) % p - B = (P[1]+P[0])*(Q[1]+Q[0]) % p - C = 2 * P[3] * Q[3] * d % p - D = 2 * P[2] * Q[2] % p - E = B-A - F = D-C - G = D+C - H = B+A - return (E*F, G*H, F*G, E*H) - -# Computes Q = s * Q -def point_mul(s, P): - Q = (0, 1, 1, 0) # Neutral element - while s > 0: - # Is there any bit-set predicate? - if s & 1: - Q = point_add(Q, P) - P = point_add(P, P) - s >>= 1 - return Q - -def point_equal(P, Q): - # x1 / z1 == x2 / z2 <==> x1 * z2 == x2 * z1 - if (P[0] * Q[2] - Q[0] * P[2]) % p != 0: - return False - if (P[1] * Q[2] - Q[1] * P[2]) % p != 0: - return False - return True - - Now follows functions for point compression. - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 11] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -# Square root of -1 -modp_sqrt_m1 = pow(2, (p-1) // 4, p) - -# Compute corresponding x coordinate, with low bit corresponding to sign, -# or return None on failure -def recover_x(y, sign): - x2 = (y*y-1) * modp_inv(d*y*y+1) - if x2 == 0: - if sign: - return None - else: - return 0 - - # Compute square root of x2 - x = pow(x2, (p+3) // 8, p) - if (x*x - x2) % p != 0: - x = x * modp_sqrt_m1 % p - if (x*x - x2) % p != 0: - return None - - if (x & 1) != sign: - x = p - x - return x - -# Base point -g_y = 4 * modp_inv(5) % p -g_x = recover_x(g_y, 0) -G = (g_x, g_y, 1, g_x * g_y % p) - -def point_compress(P): - zinv = modp_inv(P[2]) - x = P[0] * zinv % p - y = P[1] * zinv % p - return int.to_bytes(y | ((x & 1) << 255), 32, "little") - -def point_decompress(s): - if len(s) != 32: - raise Exception("Invalid input length for decompression") - y = int.from_bytes(s, "little") - sign = y >> 255 - y &= (1 << 255) - 1 - - x = recover_x(y, sign) - if x is None: - return None - else: - return (x, y, 1, x*y % p) - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 12] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - These are functions for manipulating the secret. - - def secret_expand(secret): - if len(secret) != 32: - raise Exception("Bad size of private key") - h = sha512(secret) - a = int.from_bytes(h[:32], "little") - a &= (1 << 254) - 8 - a |= (1 << 254) - return (a, h[32:]) - - def secret_to_public(secret): - (a, dummy) = secret_expand(secret) - return point_compress(point_mul(a, G)) - - The signature function works as below. - - def sign(secret, msg): - a, prefix = secret_expand(secret) - A = point_compress(point_mul(a, G)) - r = sha512_modq(prefix + msg) - R = point_mul(r, G) - Rs = point_compress(R) - h = sha512_modq(Rs + A + msg) - s = (r + h * a) % q - return Rs + int.to_bytes(s, 32, "little") - - And finally the verification function. - - def verify(public, msg, signature): - if len(public) != 32: - raise Exception("Bad public-key length") - if len(signature) != 64: - Exception("Bad signature length") - A = point_decompress(public) - if not A: - return False - Rs = signature[:32] - R = point_decompress(Rs) - if not R: - return False - s = int.from_bytes(signature[32:], "little") - h = sha512_modq(Rs + public + msg) - sB = point_mul(s, G) - hA = point_mul(h, A) - return point_equal(sB, point_add(R, hA)) - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 13] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -6. Test Vectors for Ed25519 - - - Below is a sequence of octets with test vectors for the the Ed25519 - signature algorithm. The octets are hex encoded and whitespace is - inserted for readability. Private keys are 64 bytes, public keys 32 - bytes, message of arbitrary length, and signatures are 64 bytes. The - test vectors are taken from [ED25519-TEST-VECTORS] (but we removed - the public key as a suffix of the secret key, and removed the message - from the signature) and [ED25519-LIBGCRYPT-TEST-VECTORS]. - - -----TEST 1 - SECRET KEY: - 9d61b19deffd5a60ba844af492ec2cc4 - 4449c5697b326919703bac031cae7f60 - - PUBLIC KEY: - d75a980182b10ab7d54bfed3c964073a - 0ee172f3daa62325af021a68f707511a - - MESSAGE (length 0 bytes): - - SIGNATURE: - e5564300c360ac729086e2cc806e828a - 84877f1eb8e5d974d873e06522490155 - 5fb8821590a33bacc61e39701cf9b46b - d25bf5f0595bbe24655141438e7a100b - - -----TEST 2 - SECRET KEY: - 4ccd089b28ff96da9db6c346ec114e0f - 5b8a319f35aba624da8cf6ed4fb8a6fb - - PUBLIC KEY: - 3d4017c3e843895a92b70aa74d1b7ebc - 9c982ccf2ec4968cc0cd55f12af4660c - - MESSAGE (length 1 byte): - 72 - - SIGNATURE: - 92a009a9f0d4cab8720e820b5f642540 - a2b27b5416503f8fb3762223ebdb69da - 085ac1e43e15996e458f3613d0f11d8c - 387b2eaeb4302aeeb00d291612bb0c00 - - -----TEST 3 - SECRET KEY: - c5aa8df43f9f837bedb7442f31dcb7b1 - - - -Josefsson & Moeller Expires August 26, 2015 [Page 14] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - 66d38535076f094b85ce3a2e0b4458f7 - - PUBLIC KEY: - fc51cd8e6218a1a38da47ed00230f058 - 0816ed13ba3303ac5deb911548908025 - - MESSAGE (length 2 bytes): - af82 - - SIGNATURE: - 6291d657deec24024827e69c3abe01a3 - 0ce548a284743a445e3680d7db5ac3ac - 18ff9b538d16f290ae67f760984dc659 - 4a7c15e9716ed28dc027beceea1ec40a - - -----TEST 1024 - SECRET KEY: - f5e5767cf153319517630f226876b86c - 8160cc583bc013744c6bf255f5cc0ee5 - - PUBLIC KEY: - 278117fc144c72340f67d0f2316e8386 - ceffbf2b2428c9c51fef7c597f1d426e - - MESSAGE: - 08b8b2b733424243760fe426a4b54908 - 632110a66c2f6591eabd3345e3e4eb98 - fa6e264bf09efe12ee50f8f54e9f77b1 - e355f6c50544e23fb1433ddf73be84d8 - 79de7c0046dc4996d9e773f4bc9efe57 - 38829adb26c81b37c93a1b270b20329d - 658675fc6ea534e0810a4432826bf58c - 941efb65d57a338bbd2e26640f89ffbc - 1a858efcb8550ee3a5e1998bd177e93a - 7363c344fe6b199ee5d02e82d522c4fe - ba15452f80288a821a579116ec6dad2b - 3b310da903401aa62100ab5d1a36553e - 06203b33890cc9b832f79ef80560ccb9 - a39ce767967ed628c6ad573cb116dbef - efd75499da96bd68a8a97b928a8bbc10 - 3b6621fcde2beca1231d206be6cd9ec7 - aff6f6c94fcd7204ed3455c68c83f4a4 - 1da4af2b74ef5c53f1d8ac70bdcb7ed1 - 85ce81bd84359d44254d95629e9855a9 - 4a7c1958d1f8ada5d0532ed8a5aa3fb2 - d17ba70eb6248e594e1a2297acbbb39d - 502f1a8c6eb6f1ce22b3de1a1f40cc24 - 554119a831a9aad6079cad88425de6bd - - - -Josefsson & Moeller Expires August 26, 2015 [Page 15] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - e1a9187ebb6092cf67bf2b13fd65f270 - 88d78b7e883c8759d2c4f5c65adb7553 - 878ad575f9fad878e80a0c9ba63bcbcc - 2732e69485bbc9c90bfbd62481d9089b - eccf80cfe2df16a2cf65bd92dd597b07 - 07e0917af48bbb75fed413d238f5555a - 7a569d80c3414a8d0859dc65a46128ba - b27af87a71314f318c782b23ebfe808b - 82b0ce26401d2e22f04d83d1255dc51a - ddd3b75a2b1ae0784504df543af8969b - e3ea7082ff7fc9888c144da2af58429e - c96031dbcad3dad9af0dcbaaaf268cb8 - fcffead94f3c7ca495e056a9b47acdb7 - 51fb73e666c6c655ade8297297d07ad1 - ba5e43f1bca32301651339e22904cc8c - 42f58c30c04aafdb038dda0847dd988d - cda6f3bfd15c4b4c4525004aa06eeff8 - ca61783aacec57fb3d1f92b0fe2fd1a8 - 5f6724517b65e614ad6808d6f6ee34df - f7310fdc82aebfd904b01e1dc54b2927 - 094b2db68d6f903b68401adebf5a7e08 - d78ff4ef5d63653a65040cf9bfd4aca7 - 984a74d37145986780fc0b16ac451649 - de6188a7dbdf191f64b5fc5e2ab47b57 - f7f7276cd419c17a3ca8e1b939ae49e4 - 88acba6b965610b5480109c8b17b80e1 - b7b750dfc7598d5d5011fd2dcc5600a3 - 2ef5b52a1ecc820e308aa342721aac09 - 43bf6686b64b2579376504ccc493d97e - 6aed3fb0f9cd71a43dd497f01f17c0e2 - cb3797aa2a2f256656168e6c496afc5f - b93246f6b1116398a346f1a641f3b041 - e989f7914f90cc2c7fff357876e506b5 - 0d334ba77c225bc307ba537152f3f161 - 0e4eafe595f6d9d90d11faa933a15ef1 - 369546868a7f3a45a96768d40fd9d034 - 12c091c6315cf4fde7cb68606937380d - b2eaaa707b4c4185c32eddcdd306705e - 4dc1ffc872eeee475a64dfac86aba41c - 0618983f8741c5ef68d3a101e8a3b8ca - c60c905c15fc910840b94c00a0b9d0 - - SIGNATURE: - 0aab4c900501b3e24d7cdf4663326a3a - 87df5e4843b2cbdb67cbf6e460fec350 - aa5371b1508f9f4528ecea23c436d94b - 5e8fcd4f681e30a6ac00a9704a188a03 - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 16] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - -----TEST 1A - -----An additional test with the data from test 1 but using an - -----uncompressed public key. - SECRET KEY: - 9d61b19deffd5a60ba844af492ec2cc4 - 4449c5697b326919703bac031cae7f60 - - PUBLIC KEY: - 0455d0e09a2b9d34292297e08d60d0f6 - 20c513d47253187c24b12786bd777645 - ce1a5107f7681a02af2523a6daf372e1 - 0e3a0764c9d3fe4bd5b70ab18201985a - d7 - - MSG (length 0 bytes): - - SIGNATURE: - e5564300c360ac729086e2cc806e828a - 84877f1eb8e5d974d873e06522490155 - 5fb8821590a33bacc61e39701cf9b46b - d25bf5f0595bbe24655141438e7a100b - - -----TEST 1B - -----An additional test with the data from test 1 but using an - -----compressed prefix. - SECRET KEY: - 9d61b19deffd5a60ba844af492ec2cc4 - 4449c5697b326919703bac031cae7f60 - - PUBLIC KEY: - 40d75a980182b10ab7d54bfed3c96407 - 3a0ee172f3daa62325af021a68f70751 - 1a - - MESSAGE (length 0 bytes): - - SIGNATURE: - e5564300c360ac729086e2cc806e828a - 84877f1eb8e5d974d873e06522490155 - 5fb8821590a33bacc61e39701cf9b46b - d25bf5f0595bbe24655141438e7a100b - ----- - -7. Acknowledgements - - - Feedback on this document was received from Werner Koch and Damien - Miller. - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 17] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -8. IANA Considerations - - - None. - -9. Security Considerations - - -9.1. Side-channel leaks - - - For implementations performing signatures, secrecy of the key is - fundamental. It is possible to protect against some side-channel - attacks by ensuring that the implementation executes exactly the same - sequence of instructions and performs exactly the same memory - accesses, for any value of the secret key. - - To make an implementation side-channel silent in this way, the modulo - p arithmetic must not use any data-dependent branches, e.g., related - to carry propagation. Side channel-silent point addition is - straight-forward, thanks to the unified formulas. - - Scalar multiplication, multiplying a point by an integer, needs some - additional effort to implement in a side-channel silent manner. One - simple approach is to implement a side-channel silent conditional - assignment, and use together with the binary algorithm to examine one - bit of the integer at a time. - - Note that the example implementation in this document does not - attempt to be side-channel silent. - -10. References - - -10.1. Normative References - - - [RFC4634] Eastlake, D. and T. Hansen, "US Secure Hash Algorithms - (SHA and HMAC-SHA)", RFC 4634, July 2006. - - [I-D.irtf-cfrg-curves] - Langley, A., Salz, R., and S. Turner, "Elliptic Curves for - Security", draft-irtf-cfrg-curves-01 (work in progress), - January 2015. - -10.2. Informative References - - - [RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness - Requirements for Security", BCP 106, RFC 4086, June 2005. - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 18] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - [EDDSA] Bernstein, D., Duif, N., Lange, T., Schwabe, P., and B. - Yang, "High-speed high-security signatures", WWW - http://ed25519.cr.yp.to/ed25519-20110926.pdf, September - 2011. - - [Faster-ECC] - Bernstein, D. and T. Lange, "Faster addition and doubling - on elliptic curves", WWW http://eprint.iacr.org/2007/286, - July 2007. - - [Edwards-revisited] - Hisil, H., Wong, K., Carter, G., and E. Dawson, "Twisted - Edwards Curves Revisited", WWW - http://eprint.iacr.org/2008/522, December 2008. - - [CURVE25519] - Bernstein, D., "Curve25519: new Diffie-Hellman speed - records", WWW http://cr.yp.to/ecdh.html, February 2006. - - [ED25519-TEST-VECTORS] - Bernstein, D., Duif, N., Lange, T., Schwabe, P., and B. - Yang, "Ed25519 test vectors", WWW - http://ed25519.cr.yp.to/python/sign.input, July 2011. - - [ED25519-LIBGCRYPT-TEST-VECTORS] - Koch, W., "Ed25519 Libgcrypt test vectors", WWW - http://git.gnupg.org/cgi- - bin/gitweb.cgi?p=libgcrypt.git;a=blob;f=tests/t-ed25519.in - p;h=e13566f826321eece65e02c593bc7d885b3dbe23;hb=refs/ - heads/master, July 2014. - -Appendix A. Ed25519 Python Library - - - Below is an example implementation of Ed25519 written in Python, - version 3.2 or higher is required. - -# Loosely based on the public domain code at -# http://ed25519.cr.yp.to/software.html -# -# Needs python-3.2 - -import hashlib - - -def sha512(s): - return hashlib.sha512(s).digest() - -# Base field Z_p - - - -Josefsson & Moeller Expires August 26, 2015 [Page 19] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - -p = 2**255 - 19 - - -def modp_inv(x): - return pow(x, p-2, p) - -# Curve constant -d = -121665 * modp_inv(121666) % p - -# Group order -q = 2**252 + 27742317777372353535851937790883648493 - - -def sha512_modq(s): - return int.from_bytes(sha512(s), "little") % q - -# Points are represented as tuples (X, Y, Z, T) of extended coordinates, -# with x = X/Z, y = Y/Z, x*y = T/Z - - -def point_add(P, Q): - A = (P[1]-P[0])*(Q[1]-Q[0]) % p - B = (P[1]+P[0])*(Q[1]+Q[0]) % p - C = 2 * P[3] * Q[3] * d % p - D = 2 * P[2] * Q[2] % p - E = B-A - F = D-C - G = D+C - H = B+A - return (E*F, G*H, F*G, E*H) - - -# Computes Q = s * Q -def point_mul(s, P): - Q = (0, 1, 1, 0) # Neutral element - while s > 0: - # Is there any bit-set predicate? - if s & 1: - Q = point_add(Q, P) - P = point_add(P, P) - s >>= 1 - return Q - - -def point_equal(P, Q): - # x1 / z1 == x2 / z2 <==> x1 * z2 == x2 * z1 - if (P[0] * Q[2] - Q[0] * P[2]) % p != 0: - return False - - - -Josefsson & Moeller Expires August 26, 2015 [Page 20] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - if (P[1] * Q[2] - Q[1] * P[2]) % p != 0: - return False - return True - -# Square root of -1 -modp_sqrt_m1 = pow(2, (p-1) // 4, p) - - -# Compute corresponding x coordinate, with low bit corresponding to sign, -# or return None on failure -def recover_x(y, sign): - x2 = (y*y-1) * modp_inv(d*y*y+1) - if x2 == 0: - if sign: - return None - else: - return 0 - - # Compute square root of x2 - x = pow(x2, (p+3) // 8, p) - if (x*x - x2) % p != 0: - x = x * modp_sqrt_m1 % p - if (x*x - x2) % p != 0: - return None - - if (x & 1) != sign: - x = p - x - return x - -# Base point -g_y = 4 * modp_inv(5) % p -g_x = recover_x(g_y, 0) -G = (g_x, g_y, 1, g_x * g_y % p) - - -def point_compress(P): - zinv = modp_inv(P[2]) - x = P[0] * zinv % p - y = P[1] * zinv % p - return int.to_bytes(y | ((x & 1) << 255), 32, "little") - - -def point_decompress(s): - if len(s) != 32: - raise Exception("Invalid input length for decompression") - y = int.from_bytes(s, "little") - sign = y >> 255 - y &= (1 << 255) - 1 - - - -Josefsson & Moeller Expires August 26, 2015 [Page 21] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - x = recover_x(y, sign) - if x is None: - return None - else: - return (x, y, 1, x*y % p) - - -def secret_expand(secret): - if len(secret) != 32: - raise Exception("Bad size of private key") - h = sha512(secret) - a = int.from_bytes(h[:32], "little") - a &= (1 << 254) - 8 - a |= (1 << 254) - return (a, h[32:]) - - -def secret_to_public(secret): - (a, dummy) = secret_expand(secret) - return point_compress(point_mul(a, G)) - - -def sign(secret, msg): - a, prefix = secret_expand(secret) - A = point_compress(point_mul(a, G)) - r = sha512_modq(prefix + msg) - R = point_mul(r, G) - Rs = point_compress(R) - h = sha512_modq(Rs + A + msg) - s = (r + h * a) % q - return Rs + int.to_bytes(s, 32, "little") - - -def verify(public, msg, signature): - if len(public) != 32: - raise Exception("Bad public-key length") - if len(signature) != 64: - Exception("Bad signature length") - A = point_decompress(public) - if not A: - return False - Rs = signature[:32] - R = point_decompress(Rs) - if not R: - return False - s = int.from_bytes(signature[32:], "little") - h = sha512_modq(Rs + public + msg) - sB = point_mul(s, G) - - - -Josefsson & Moeller Expires August 26, 2015 [Page 22] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - hA = point_mul(h, A) - return point_equal(sB, point_add(R, hA)) - -Appendix B. Library driver - - - Below is a command-line tool that uses the library above to perform - computations, for interactive use or for self-checking. - - import sys - import binascii - - from ed25519 import * - - def point_valid(P): - zinv = modp_inv(P[2]) - x = P[0] * zinv % p - y = P[1] * zinv % p - assert (x*y - P[3]*zinv) % p == 0 - return (-x*x + y*y - 1 - d*x*x*y*y) % p == 0 - - assert point_valid(G) - Z = (0, 1, 1, 0) - assert point_valid(Z) - - assert point_equal(Z, point_add(Z, Z)) - assert point_equal(G, point_add(Z, G)) - assert point_equal(Z, point_mul(0, G)) - assert point_equal(G, point_mul(1, G)) - assert point_equal(point_add(G, G), point_mul(2, G)) - for i in range(0, 100): - assert point_valid(point_mul(i, G)) - assert point_equal(Z, point_mul(q, G)) - - def munge_string(s, pos, change): - return (s[:pos] + - int.to_bytes(s[pos] ^ change, 1, "little") + - s[pos+1:]) - - # Read a file in the format of - # http://ed25519.cr.yp.to/python/sign.input - lineno = 0 - while True: - line = sys.stdin.readline() - if not line: - break - lineno = lineno + 1 - print(lineno) - fields = line.split(":") - - - -Josefsson & Moeller Expires August 26, 2015 [Page 23] - - -Internet-Draft EdDSA & Ed25519 February 2015 - - - secret = (binascii.unhexlify(fields[0]))[:32] - public = binascii.unhexlify(fields[1]) - msg = binascii.unhexlify(fields[2]) - signature = binascii.unhexlify(fields[3])[:64] - - assert public == secret_to_public(secret) - assert signature == sign(secret, msg) - assert verify(public, msg, signature) - if len(msg) == 0: - bad_msg = b"x" - else: - bad_msg = munge_string(msg, len(msg) // 3, 4) - assert not verify(public, bad_msg, signature) - bad_signature = munge_string(signature, 20, 8) - assert not verify(public, msg, bad_signature) - bad_signature = munge_string(signature, 40, 16) - assert not verify(public, msg, bad_signature) - -Authors' Addresses - - Simon Josefsson - SJD AB - - Email: simon@josefsson.org - URI: http://josefsson.org/ - - - Niels Moeller - - Email: nisse@lysator.liu.se - - - - - - - - - - - - - - - - - - - - - -Josefsson & Moeller Expires August 26, 2015 [Page 24] - - - -Html markup produced by rfcmarkup 1.113, available from https://tools.ietf.org/tools/rfcmarkup/ +Note: This draft is now superseded by https://datatracker.ietf.org/doc/rfc8032/ +(review of the differences is left as an exercise for the reader) diff --git a/src/cryptonote_basic/CMakeLists.txt b/src/cryptonote_basic/CMakeLists.txt index d50a9df67..21445959d 100644 --- a/src/cryptonote_basic/CMakeLists.txt +++ b/src/cryptonote_basic/CMakeLists.txt @@ -27,9 +27,13 @@ # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. if(APPLE) - find_library(IOKIT_LIBRARY IOKit) - mark_as_advanced(IOKIT_LIBRARY) - list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + if(DEPENDS) + list(APPEND EXTRA_LIBRARIES "-framework Foundation -framework ApplicationServices -framework AppKit -framework IOKit") + else() + find_library(IOKIT_LIBRARY IOKit) + mark_as_advanced(IOKIT_LIBRARY) + list(APPEND EXTRA_LIBRARIES ${IOKIT_LIBRARY}) + endif() endif() set(cryptonote_basic_sources diff --git a/src/cryptonote_basic/account.cpp b/src/cryptonote_basic/account.cpp index 4cbfa8142..e891a748d 100644 --- a/src/cryptonote_basic/account.cpp +++ b/src/cryptonote_basic/account.cpp @@ -197,10 +197,14 @@ DISABLE_VS_WARNINGS(4244 4345) //----------------------------------------------------------------- void account_base::create_from_device(const std::string &device_name) { - hw::device &hwdev = hw::get_device(device_name); - m_keys.set_device(hwdev); hwdev.set_name(device_name); + create_from_device(hwdev); + } + + void account_base::create_from_device(hw::device &hwdev) + { + m_keys.set_device(hwdev); MCDEBUG("ledger", "device type: "<<typeid(hwdev).name()); hwdev.init(); hwdev.connect(); diff --git a/src/cryptonote_basic/account.h b/src/cryptonote_basic/account.h index dac66ff1a..98bba55b1 100644 --- a/src/cryptonote_basic/account.h +++ b/src/cryptonote_basic/account.h @@ -77,7 +77,8 @@ namespace cryptonote public: account_base(); crypto::secret_key generate(const crypto::secret_key& recovery_key = crypto::secret_key(), bool recover = false, bool two_random = false); - void create_from_device(const std::string &device_name) ; + void create_from_device(const std::string &device_name); + void create_from_device(hw::device &hwdev); void create_from_keys(const cryptonote::account_public_address& address, const crypto::secret_key& spendkey, const crypto::secret_key& viewkey); void create_from_viewkey(const cryptonote::account_public_address& address, const crypto::secret_key& viewkey); bool make_multisig(const crypto::secret_key &view_secret_key, const crypto::secret_key &spend_secret_key, const crypto::public_key &spend_public_key, const std::vector<crypto::secret_key> &multisig_keys); diff --git a/src/cryptonote_core/blockchain.cpp b/src/cryptonote_core/blockchain.cpp index c7e2f5a65..3751d6473 100644 --- a/src/cryptonote_core/blockchain.cpp +++ b/src/cryptonote_core/blockchain.cpp @@ -109,6 +109,12 @@ static const struct { // version 7 starts from block 1546000, which is on or around the 6th of April, 2018. Fork time finalised on 2018-03-17. { 7, 1546000, 0, 1521303150 }, + + // version 8 starts from block 1685555, which is on or around the 18th of October, 2018. Fork time finalised on 2018-09-02. + { 8, 1685555, 0, 1535889547 }, + + // version 9 starts from block 1686275, which is on or around the 19th of October, 2018. Fork time finalised on 2018-09-02. + { 9, 1686275, 0, 1535889548 }, }; static const uint64_t mainnet_hard_fork_version_1_till = 1009826; diff --git a/src/daemon/CMakeLists.txt b/src/daemon/CMakeLists.txt index 84004c3c6..b1c4b711d 100644 --- a/src/daemon/CMakeLists.txt +++ b/src/daemon/CMakeLists.txt @@ -28,7 +28,9 @@ set(blocksdat "") if(PER_BLOCK_CHECKPOINT) - if(APPLE) + if(APPLE AND DEPENDS) + add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} --target=x86_64-apple-darwin11 -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) + elseif(APPLE AND NOT DEPENDS) add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && touch stub.c && ${CMAKE_C_COMPILER} -o stub.o -c stub.c COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -sectcreate __DATA __blocks_dat ../blocks/checkpoints.dat -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o stub.o && rm -f stub.*) else() add_custom_command(OUTPUT blocksdat.o MAIN_DEPENDENCY ../blocks/checkpoints.dat COMMAND cd ${CMAKE_CURRENT_SOURCE_DIR} && cp ../blocks/checkpoints.dat blocks.dat && ${CMAKE_LINKER} ${LD_RAW_FLAGS} -r -b binary -o ${CMAKE_CURRENT_BINARY_DIR}/blocksdat.o blocks.dat && rm -f blocks.dat) diff --git a/src/daemon/main.cpp b/src/daemon/main.cpp index 82ece62a9..f483ba6c9 100644 --- a/src/daemon/main.cpp +++ b/src/daemon/main.cpp @@ -239,11 +239,14 @@ int main(int argc, char const * argv[]) return 1; } + const char *env_rpc_login = nullptr; + const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login); + const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0; boost::optional<tools::login> login{}; - if (command_line::has_arg(vm, arg.rpc_login)) + if (has_rpc_arg || use_rpc_env) { login = tools::login::parse( - command_line::get_arg(vm, arg.rpc_login), false, [](bool verify) { + has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), false, [](bool verify) { #ifdef HAVE_READLINE rdln::suspend_readline pause_readline; #endif diff --git a/src/daemon/rpc_command_executor.cpp b/src/daemon/rpc_command_executor.cpp index 9ab1be246..6b6c88907 100644 --- a/src/daemon/rpc_command_executor.cpp +++ b/src/daemon/rpc_command_executor.cpp @@ -533,6 +533,7 @@ bool t_rpc_command_executor::print_blockchain_info(uint64_t start_block_index, u req.start_height = start_block_index; req.end_height = end_block_index; + req.fill_pow_hash = false; std::string fail_message = "Unsuccessful"; @@ -1746,6 +1747,7 @@ bool t_rpc_command_executor::print_blockchain_dynamic_stats(uint64_t nblocks) bhreq.start_height = ires.height - nblocks; bhreq.end_height = ires.height - 1; + bhreq.fill_pow_hash = false; if (m_is_rpc) { if (!m_rpc_client->json_rpc_request(bhreq, bhres, "getblockheadersrange", fail_message.c_str())) diff --git a/src/device/device.cpp b/src/device/device.cpp index 983f59b60..8a8b40061 100644 --- a/src/device/device.cpp +++ b/src/device/device.cpp @@ -39,32 +39,60 @@ namespace hw { /* ======================================================================= */ /* SETUP */ - /* ======================================================================= */ - device& get_device(const std::string device_descriptor) { - - struct s_devices { - std::map<std::string, std::unique_ptr<device>> registry; - s_devices() : registry() { - hw::core::register_all(registry); - #ifdef HAVE_PCSC - hw::ledger::register_all(registry); - #endif - }; - }; - - static const s_devices devices; + /* ======================================================================= */ + + static std::unique_ptr<device_registry> registry; + + device_registry::device_registry(){ + hw::core::register_all(registry); + #ifdef HAVE_PCSC + hw::ledger::register_all(registry); + #endif + } + + bool device_registry::register_device(const std::string & device_name, device * hw_device){ + auto search = registry.find(device_name); + if (search != registry.end()){ + return false; + } + + registry.insert(std::make_pair(device_name, std::unique_ptr<device>(hw_device))); + return true; + } + + device& device_registry::get_device(const std::string & device_descriptor){ + // Device descriptor can contain further specs after first : + auto delim = device_descriptor.find(':'); + auto device_descriptor_lookup = device_descriptor; + if (delim != std::string::npos) { + device_descriptor_lookup = device_descriptor.substr(0, delim); + } - auto device = devices.registry.find(device_descriptor); - if (device == devices.registry.end()) { - MERROR("device not found in registry: '" << device_descriptor << "'\n" << - "known devices:"); - - for( const auto& sm_pair : devices.registry ) { + auto device = registry.find(device_descriptor_lookup); + if (device == registry.end()) { + MERROR("Device not found in registry: '" << device_descriptor << "'. Known devices: "); + for( const auto& sm_pair : registry ) { MERROR(" - " << sm_pair.first); } - throw std::runtime_error("device not found: "+ device_descriptor); + throw std::runtime_error("device not found: " + device_descriptor); } return *device->second; } + device& get_device(const std::string & device_descriptor) { + if (!registry){ + registry.reset(new device_registry()); + } + + return registry->get_device(device_descriptor); + } + + bool register_device(const std::string & device_name, device * hw_device){ + if (!registry){ + registry.reset(new device_registry()); + } + + return registry->register_device(device_name, hw_device); + } + } diff --git a/src/device/device.hpp b/src/device/device.hpp index c21456daf..d14b8848c 100644 --- a/src/device/device.hpp +++ b/src/device/device.hpp @@ -202,6 +202,17 @@ namespace hw { ~reset_mode() { hwref.set_mode(hw::device::NONE);} }; - device& get_device(const std::string device_descriptor) ; + class device_registry { + private: + std::map<std::string, std::unique_ptr<device>> registry; + + public: + device_registry(); + bool register_device(const std::string & device_name, device * hw_device); + device& get_device(const std::string & device_descriptor); + }; + + device& get_device(const std::string & device_descriptor); + bool register_device(const std::string & device_name, device * hw_device); } diff --git a/src/gen_multisig/gen_multisig.cpp b/src/gen_multisig/gen_multisig.cpp index e680a8157..f11f442bc 100644 --- a/src/gen_multisig/gen_multisig.cpp +++ b/src/gen_multisig/gen_multisig.cpp @@ -91,8 +91,8 @@ static bool generate_multisig(uint32_t threshold, uint32_t total, const std::str for (size_t n = 0; n < total; ++n) { std::string name = basename + "-" + std::to_string(n + 1); - wallets[n].reset(new tools::wallet2(nettype)); - wallets[n]->init(false, ""); + wallets[n].reset(new tools::wallet2(nettype, 1, false)); + wallets[n]->init(""); wallets[n]->generate(name, pwd_container->password(), rct::rct2sk(rct::skGen()), false, false, create_address_file); } diff --git a/src/mnemonics/chinese_simplified.h b/src/mnemonics/chinese_simplified.h index 1ae8c89d6..0566b1079 100644 --- a/src/mnemonics/chinese_simplified.h +++ b/src/mnemonics/chinese_simplified.h @@ -72,7 +72,10 @@ namespace Language class Chinese_Simplified: public Base
{
public:
- Chinese_Simplified(): Base("简体中文 (中国)", "Chinese (simplified)", std::vector<std::string>({
+ Chinese_Simplified(): Base("简体中文 (中国)", "Chinese (simplified)", {}, 1)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"的",
"一",
"是",
@@ -1699,8 +1702,8 @@ namespace Language "秒",
"浙",
"貌"
- }), 1)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/dutch.h b/src/mnemonics/dutch.h index c9806f450..801caf986 100644 --- a/src/mnemonics/dutch.h +++ b/src/mnemonics/dutch.h @@ -49,7 +49,10 @@ namespace Language class Dutch: public Base
{
public:
- Dutch(): Base("Nederlands", "Dutch", std::vector<std::string>({
+ Dutch(): Base("Nederlands", "Dutch", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"aalglad",
"aalscholver",
"aambeeld",
@@ -1676,8 +1679,8 @@ namespace Language "zwiep",
"zwijmel",
"zworen"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/english.h b/src/mnemonics/english.h index ee087674d..d5c5594ef 100644 --- a/src/mnemonics/english.h +++ b/src/mnemonics/english.h @@ -49,7 +49,10 @@ namespace Language class English: public Base
{
public:
- English(): Base("English", "English", std::vector<std::string>({
+ English(): Base("English", "English", {}, 3)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abbey",
"abducts",
"ability",
@@ -1676,8 +1679,8 @@ namespace Language "zombie",
"zones",
"zoom"
- }), 3)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/english_old.h b/src/mnemonics/english_old.h index b31491646..e35b907df 100644 --- a/src/mnemonics/english_old.h +++ b/src/mnemonics/english_old.h @@ -51,7 +51,10 @@ namespace Language class EnglishOld: public Base
{
public:
- EnglishOld(): Base("EnglishOld", "English (old)", std::vector<std::string>({
+ EnglishOld(): Base("EnglishOld", "English (old)", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"like",
"just",
"love",
@@ -1678,8 +1681,8 @@ namespace Language "unseen",
"weapon",
"weary"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps(ALLOW_DUPLICATE_PREFIXES | ALLOW_SHORT_WORDS);
}
};
diff --git a/src/mnemonics/esperanto.h b/src/mnemonics/esperanto.h index a1d1a3f30..b0be235ed 100644 --- a/src/mnemonics/esperanto.h +++ b/src/mnemonics/esperanto.h @@ -58,7 +58,10 @@ namespace Language class Esperanto: public Base
{
public:
- Esperanto(): Base("Esperanto", "Esperanto", std::vector<std::string>({
+ Esperanto(): Base("Esperanto", "Esperanto", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abako",
"abdiki",
"abelo",
@@ -1685,8 +1688,8 @@ namespace Language "zorgi",
"zukino",
"zumilo",
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/french.h b/src/mnemonics/french.h index 7eaf45650..48ec46f78 100644 --- a/src/mnemonics/french.h +++ b/src/mnemonics/french.h @@ -49,7 +49,10 @@ namespace Language class French: public Base { public: - French(): Base("Français", "French", std::vector<std::string>({ + French(): Base("Français", "French", {}, 4) + { + static constexpr const char * const words[NWORDS] = + { "abandon", "abattre", "aboi", @@ -1676,8 +1679,8 @@ namespace Language "zinc", "zone", "zoom" - }), 4) - { + }; + set_words(words); populate_maps(); } }; diff --git a/src/mnemonics/german.h b/src/mnemonics/german.h index 8eff43523..883a173a3 100644 --- a/src/mnemonics/german.h +++ b/src/mnemonics/german.h @@ -51,7 +51,10 @@ namespace Language class German: public Base
{
public:
- German(): Base("Deutsch", "German", std::vector<std::string>({
+ German(): Base("Deutsch", "German", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"Abakus",
"Abart",
"abbilden",
@@ -1678,8 +1681,8 @@ namespace Language "Zündung",
"Zweck",
"Zyklop"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/italian.h b/src/mnemonics/italian.h index d5ecb74f4..57cdfa25e 100644 --- a/src/mnemonics/italian.h +++ b/src/mnemonics/italian.h @@ -51,7 +51,10 @@ namespace Language class Italian: public Base
{
public:
- Italian(): Base("Italiano", "Italian", std::vector<std::string>({
+ Italian(): Base("Italiano", "Italian", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abbinare",
"abbonato",
"abisso",
@@ -1678,8 +1681,8 @@ namespace Language "zolfo",
"zombie",
"zucchero"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/japanese.h b/src/mnemonics/japanese.h index f3b3e4924..5baabedf2 100644 --- a/src/mnemonics/japanese.h +++ b/src/mnemonics/japanese.h @@ -71,7 +71,10 @@ namespace Language class Japanese: public Base
{
public:
- Japanese(): Base("日本語", "Japanese", std::vector<std::string>({
+ Japanese(): Base("日本語", "Japanese", {}, 3)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"あいこくしん",
"あいさつ",
"あいだ",
@@ -1698,8 +1701,8 @@ namespace Language "ひさん",
"びじゅつかん",
"ひしょ"
- }), 3)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/language_base.h b/src/mnemonics/language_base.h index cf518ab2a..52e784cef 100644 --- a/src/mnemonics/language_base.h +++ b/src/mnemonics/language_base.h @@ -83,7 +83,10 @@ namespace Language ALLOW_SHORT_WORDS = 1<<0,
ALLOW_DUPLICATE_PREFIXES = 1<<1,
};
- const std::vector<std::string> word_list; /*!< A pointer to the array of words */
+ enum {
+ NWORDS = 1626
+ };
+ std::vector<std::string> word_list; /*!< A pointer to the array of words */
std::unordered_map<epee::wipeable_string, uint32_t> word_map; /*!< hash table to find word's index */
std::unordered_map<epee::wipeable_string, uint32_t> trimmed_word_map; /*!< hash table to find word's trimmed index */
std::string language_name; /*!< Name of language */
@@ -96,7 +99,7 @@ namespace Language {
int ii;
std::vector<std::string>::const_iterator it;
- if (word_list.size () != 1626)
+ if (word_list.size () != NWORDS)
throw std::runtime_error("Wrong word list length for " + language_name);
for (it = word_list.begin(), ii = 0; it != word_list.end(); it++, ii++)
{
@@ -138,6 +141,12 @@ namespace Language virtual ~Base()
{
}
+ void set_words(const char * const words[])
+ {
+ word_list.resize(NWORDS);
+ for (size_t i = 0; i < NWORDS; ++i)
+ word_list[i] = words[i];
+ }
/*!
* \brief Returns a pointer to the word list.
* \return A pointer to the word list.
diff --git a/src/mnemonics/lojban.h b/src/mnemonics/lojban.h index 0966a1169..5162a8ec9 100644 --- a/src/mnemonics/lojban.h +++ b/src/mnemonics/lojban.h @@ -56,7 +56,10 @@ namespace Language class Lojban: public Base
{
public:
- Lojban(): Base("Lojban", "Lojban", std::vector<std::string>({
+ Lojban(): Base("Lojban", "Lojban", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"backi",
"bacru",
"badna",
@@ -1683,8 +1686,8 @@ namespace Language "noltruti'u",
"samtci",
"snaxa'a",
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/portuguese.h b/src/mnemonics/portuguese.h index 0195389ce..af04f89c2 100644 --- a/src/mnemonics/portuguese.h +++ b/src/mnemonics/portuguese.h @@ -72,7 +72,10 @@ namespace Language class Portuguese: public Base
{
public:
- Portuguese(): Base("Português", "Portuguese", std::vector<std::string>({
+ Portuguese(): Base("Português", "Portuguese", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"abaular",
"abdominal",
"abeto",
@@ -1699,8 +1702,8 @@ namespace Language "zeloso",
"zenite",
"zumbi"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/russian.h b/src/mnemonics/russian.h index d5dd556ef..f3e70ede6 100644 --- a/src/mnemonics/russian.h +++ b/src/mnemonics/russian.h @@ -51,7 +51,10 @@ namespace Language class Russian: public Base
{
public:
- Russian(): Base("русский язык", "Russian", std::vector<std::string>({
+ Russian(): Base("русский язык", "Russian", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"абажур",
"абзац",
"абонент",
@@ -1678,8 +1681,8 @@ namespace Language "яхта",
"ячейка",
"ящик"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps();
}
};
diff --git a/src/mnemonics/spanish.h b/src/mnemonics/spanish.h index 51f38fede..4d7a896a6 100644 --- a/src/mnemonics/spanish.h +++ b/src/mnemonics/spanish.h @@ -72,7 +72,10 @@ namespace Language class Spanish: public Base
{
public:
- Spanish(): Base("Español", "Spanish", std::vector<std::string>({
+ Spanish(): Base("Español", "Spanish", {}, 4)
+ {
+ static constexpr const char * const words[NWORDS] =
+ {
"ábaco",
"abdomen",
"abeja",
@@ -1699,8 +1702,8 @@ namespace Language "risa",
"ritmo",
"rito"
- }), 4)
- {
+ };
+ set_words(words);
populate_maps(ALLOW_SHORT_WORDS);
}
};
diff --git a/src/ringct/rctTypes.h b/src/ringct/rctTypes.h index ffc4df3ed..18290637b 100644 --- a/src/ringct/rctTypes.h +++ b/src/ringct/rctTypes.h @@ -313,10 +313,10 @@ namespace rct { return false; if (type == RCTTypeBulletproof) { - ar.tag("bp"); - ar.begin_array(); uint32_t nbp = bulletproofs.size(); FIELD(nbp) + ar.tag("bp"); + ar.begin_array(); if (nbp > outputs) return false; PREPARE_CUSTOM_VECTOR_SERIALIZATION(nbp, bulletproofs); diff --git a/src/rpc/core_rpc_server.cpp b/src/rpc/core_rpc_server.cpp index db7f2dbaa..a85b94541 100644 --- a/src/rpc/core_rpc_server.cpp +++ b/src/rpc/core_rpc_server.cpp @@ -198,7 +198,7 @@ namespace cryptonote res.block_size_limit = res.block_weight_limit = m_core.get_blockchain_storage().get_current_cumulative_block_weight_limit(); res.block_size_median = res.block_weight_median = m_core.get_blockchain_storage().get_current_cumulative_block_weight_median(); res.status = CORE_RPC_STATUS_OK; - res.start_time = (uint64_t)m_core.get_start_time(); + res.start_time = m_restricted ? 0 : (uint64_t)m_core.get_start_time(); res.free_space = m_restricted ? std::numeric_limits<uint64_t>::max() : m_core.get_free_space(); res.offline = m_core.offline(); res.bootstrap_daemon_address = m_bootstrap_daemon_address; @@ -779,7 +779,13 @@ namespace cryptonote boost::thread::attributes attrs; attrs.set_stack_size(THREAD_STACK_SIZE); - if(!m_core.get_miner().start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) + cryptonote::miner &miner= m_core.get_miner(); + if (miner.is_mining()) + { + res.status = "Already mining"; + return true; + } + if(!miner.start(info.address, static_cast<size_t>(req.threads_count), attrs, req.do_background_mining, req.ignore_battery)) { res.status = "Failed, mining not started"; LOG_PRINT_L0(res.status); diff --git a/src/rpc/rpc_args.cpp b/src/rpc/rpc_args.cpp index d4044d11b..60c78480a 100644 --- a/src/rpc/rpc_args.cpp +++ b/src/rpc/rpc_args.cpp @@ -82,11 +82,17 @@ namespace cryptonote } } - if (command_line::has_arg(vm, arg.rpc_login)) + const char *env_rpc_login = nullptr; + const bool has_rpc_arg = command_line::has_arg(vm, arg.rpc_login); + const bool use_rpc_env = !has_rpc_arg && (env_rpc_login = getenv("RPC_LOGIN")) != nullptr && strlen(env_rpc_login) > 0; + boost::optional<tools::login> login{}; + if (has_rpc_arg || use_rpc_env) { - config.login = tools::login::parse(command_line::get_arg(vm, arg.rpc_login), true, [](bool verify) { - return tools::password_container::prompt(verify, "RPC server password"); - }); + config.login = tools::login::parse( + has_rpc_arg ? command_line::get_arg(vm, arg.rpc_login) : std::string(env_rpc_login), true, [](bool verify) { + return tools::password_container::prompt(verify, "RPC server password"); + }); + if (!config.login) return boost::none; diff --git a/src/simplewallet/simplewallet.cpp b/src/simplewallet/simplewallet.cpp index e535b6d27..391d1f03e 100644 --- a/src/simplewallet/simplewallet.cpp +++ b/src/simplewallet/simplewallet.cpp @@ -2523,6 +2523,10 @@ simple_wallet::simple_wallet() boost::bind(&simple_wallet::import_key_images, this, _1), tr("import_key_images <file>"), tr("Import a signed key images list and verify their spent status.")); + m_cmd_binder.set_handler("hw_reconnect", + boost::bind(&simple_wallet::hw_reconnect, this, _1), + tr("hw_reconnect"), + tr("Attempts to reconnect HW wallet.")); m_cmd_binder.set_handler("export_outputs", boost::bind(&simple_wallet::export_outputs, this, _1), tr("export_outputs <file>"), @@ -2650,6 +2654,7 @@ bool simple_wallet::set_variable(const std::vector<std::string> &args) success_msg_writer() << "subaddress-lookahead = " << lookahead.first << ":" << lookahead.second; success_msg_writer() << "segregation-height = " << m_wallet->segregation_height(); success_msg_writer() << "ignore-fractional-outputs = " << m_wallet->ignore_fractional_outputs(); + success_msg_writer() << "device_name = " << m_wallet->device_name(); return true; } else @@ -3009,20 +3014,19 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse view secret key - std::string viewkey_string = input_line("View key: "); + epee::wipeable_string viewkey_string = input_secure_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key viewkey; + if (viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; } - crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); m_wallet_file=m_generate_from_view_key; @@ -3045,14 +3049,14 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { m_wallet_file = m_generate_from_spend_key; // parse spend secret key - std::string spendkey_string = input_line("Secret spend key: "); + epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: "); if (std::cin.eof()) return false; if (spendkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - if (!epee::string_tools::hex_to_pod(spendkey_string, m_recovery_key)) + if (!spendkey_string.hex_to_pod(unwrap(unwrap(m_recovery_key)))) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; @@ -3085,36 +3089,34 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse spend secret key - std::string spendkey_string = input_line("Secret spend key: "); + epee::wipeable_string spendkey_string = input_secure_line("Secret spend key: "); if (std::cin.eof()) return false; if (spendkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata spendkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key spendkey; + if (!spendkey_string.hex_to_pod(unwrap(unwrap(spendkey)))) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; } - crypto::secret_key spendkey = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); // parse view secret key - std::string viewkey_string = input_line("Secret view key: "); + epee::wipeable_string viewkey_string = input_secure_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) { fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key viewkey; + if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse view key secret key"); return false; } - crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); m_wallet_file=m_generate_from_keys; @@ -3190,7 +3192,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) } // parse secret view key - std::string viewkey_string = input_line("Secret view key: "); + epee::wipeable_string viewkey_string = input_secure_line("Secret view key: "); if (std::cin.eof()) return false; if (viewkey_string.empty()) @@ -3198,13 +3200,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - cryptonote::blobdata viewkey_data; - if(!epee::string_tools::parse_hexstr_to_binbuff(viewkey_string, viewkey_data) || viewkey_data.size() != sizeof(crypto::secret_key)) + crypto::secret_key viewkey; + if(!viewkey_string.hex_to_pod(unwrap(unwrap(viewkey)))) { fail_msg_writer() << tr("failed to parse secret view key"); return false; } - crypto::secret_key viewkey = *reinterpret_cast<const crypto::secret_key*>(viewkey_data.data()); // check that the view key matches the given address crypto::public_key pkey; @@ -3225,12 +3226,12 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) if(multisig_m == multisig_n) { std::vector<crypto::secret_key> multisig_secret_spendkeys(multisig_n); - std::string spendkey_string; + epee::wipeable_string spendkey_string; cryptonote::blobdata spendkey_data; // get N secret spend keys from user for(unsigned int i=0; i<multisig_n; ++i) { - spendkey_string = input_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+1) % multisig_m).str().c_str())); + spendkey_string = input_secure_line(tr((boost::format(tr("Secret spend key (%u of %u):")) % (i+1) % multisig_m).str().c_str())); if (std::cin.eof()) return false; if (spendkey_string.empty()) @@ -3238,12 +3239,11 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) fail_msg_writer() << tr("No data supplied, cancelled"); return false; } - if(!epee::string_tools::parse_hexstr_to_binbuff(spendkey_string, spendkey_data) || spendkey_data.size() != sizeof(crypto::secret_key)) + if(!spendkey_string.hex_to_pod(unwrap(unwrap(multisig_secret_spendkeys[i])))) { fail_msg_writer() << tr("failed to parse spend key secret key"); return false; } - multisig_secret_spendkeys[i] = *reinterpret_cast<const crypto::secret_key*>(spendkey_data.data()); } // sum the spend keys together to get the master spend key @@ -3295,7 +3295,7 @@ bool simple_wallet::init(const boost::program_options::variables_map& vm) { m_wallet_file = m_generate_from_device; // create wallet - auto r = new_wallet(vm, "Ledger"); + auto r = new_wallet(vm); CHECK_AND_ASSERT_MES(r, false, tr("account creation failed")); password = *r; // if no block_height is specified, assume its a new account and start it "now" @@ -3703,8 +3703,8 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr } //---------------------------------------------------------------------------------------------------- -boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm, - const std::string &device_name) { +boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::program_options::variables_map& vm) +{ auto rc = tools::wallet2::make_new(vm, false, password_prompter); m_wallet = std::move(rc.first); if (!m_wallet) @@ -3723,10 +3723,11 @@ boost::optional<epee::wipeable_string> simple_wallet::new_wallet(const boost::pr if (m_restore_height) m_wallet->set_refresh_from_block_height(m_restore_height); + auto device_desc = tools::wallet2::device_name_option(vm); try { bool create_address_file = command_line::get_arg(vm, arg_create_address_file); - m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_name, create_address_file); + m_wallet->restore(m_wallet_file, std::move(rc.second).password(), device_desc.empty() ? "Ledger" : device_desc, create_address_file); message_writer(console_color_white, true) << tr("Generated new wallet on hw device: ") << m_wallet->get_account().get_public_address_str(m_wallet->nettype()); } @@ -4083,7 +4084,7 @@ bool simple_wallet::set_daemon(const std::vector<std::string>& args) daemon_url = args[0]; } LOCK_IDLE_SCOPE(); - m_wallet->init(false, daemon_url); + m_wallet->init(daemon_url); if (args.size() == 2) { @@ -7745,6 +7746,31 @@ bool simple_wallet::import_key_images(const std::vector<std::string> &args) return true; } //---------------------------------------------------------------------------------------------------- +bool simple_wallet::hw_reconnect(const std::vector<std::string> &args) +{ + if (!m_wallet->key_on_device()) + { + fail_msg_writer() << tr("command only supported by HW wallet"); + return true; + } + + LOCK_IDLE_SCOPE(); + try + { + bool r = m_wallet->reconnect_device(); + if (!r){ + fail_msg_writer() << tr("Failed to reconnect device"); + } + } + catch (const std::exception &e) + { + fail_msg_writer() << tr("Failed to reconnect device: ") << tr(e.what()); + return true; + } + + return true; +} +//---------------------------------------------------------------------------------------------------- bool simple_wallet::export_outputs(const std::vector<std::string> &args) { if (m_wallet->key_on_device()) diff --git a/src/simplewallet/simplewallet.h b/src/simplewallet/simplewallet.h index bfbe633ac..d50e4ce04 100644 --- a/src/simplewallet/simplewallet.h +++ b/src/simplewallet/simplewallet.h @@ -98,7 +98,7 @@ namespace cryptonote const boost::optional<crypto::secret_key>& spendkey, const crypto::secret_key& viewkey); boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const epee::wipeable_string &multisig_keys, const std::string &old_language); - boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm, const std::string& device_name); + boost::optional<epee::wipeable_string> new_wallet(const boost::program_options::variables_map& vm); bool open_wallet(const boost::program_options::variables_map& vm); bool close_wallet(); @@ -201,6 +201,7 @@ namespace cryptonote bool verify(const std::vector<std::string> &args); bool export_key_images(const std::vector<std::string> &args); bool import_key_images(const std::vector<std::string> &args); + bool hw_reconnect(const std::vector<std::string> &args); bool export_outputs(const std::vector<std::string> &args); bool import_outputs(const std::vector<std::string> &args); bool show_transfer(const std::vector<std::string> &args); diff --git a/src/wallet/CMakeLists.txt b/src/wallet/CMakeLists.txt index a16f4fe19..be10b9f62 100644 --- a/src/wallet/CMakeLists.txt +++ b/src/wallet/CMakeLists.txt @@ -90,6 +90,8 @@ target_link_libraries(wallet_rpc_server cncrypto common version + daemonizer + ${EPEE_READLINE} ${Boost_CHRONO_LIBRARY} ${Boost_PROGRAM_OPTIONS_LIBRARY} ${Boost_FILESYSTEM_LIBRARY} diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index f64a44243..463185284 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -376,7 +376,7 @@ WalletImpl::WalletImpl(NetworkType nettype, uint64_t kdf_rounds) , m_rebuildWalletCache(false) , m_is_connected(false) { - m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds); + m_wallet = new tools::wallet2(static_cast<cryptonote::network_type>(nettype), kdf_rounds, true); m_history = new TransactionHistoryImpl(this); m_wallet2Callback = new Wallet2CallbackImpl(this); m_wallet->callback(m_wallet2Callback); @@ -1244,6 +1244,20 @@ size_t WalletImpl::importMultisigImages(const vector<string>& images) { return 0; } +bool WalletImpl::hasMultisigPartialKeyImages() const { + try { + clearStatus(); + checkMultisigWalletReady(m_wallet); + + return m_wallet->has_multisig_partial_key_images(); + } catch (const exception& e) { + LOG_ERROR("Error on checking for partial multisig key images: ") << e.what(); + setStatusError(string(tr("Failed to check for partial multisig key images: ")) + e.what()); + } + + return false; +} + PendingTransaction* WalletImpl::restoreMultisigTransaction(const string& signData) { try { clearStatus(); @@ -2033,7 +2047,7 @@ bool WalletImpl::isNewWallet() const bool WalletImpl::doInit(const string &daemon_address, uint64_t upper_transaction_size_limit, bool ssl) { // claim RPC so there's no in-memory encryption for now - if (!m_wallet->init(true, daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) + if (!m_wallet->init(daemon_address, m_daemon_login, upper_transaction_size_limit, ssl)) return false; // in case new wallet, this will force fast-refresh (pulling hashes instead of blocks) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index 209040157..2cbfa30d0 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -139,6 +139,7 @@ public: bool finalizeMultisig(const std::vector<std::string>& extraMultisigInfo) override; bool exportMultisigImages(std::string& images) override; size_t importMultisigImages(const std::vector<std::string>& images) override; + bool hasMultisigPartialKeyImages() const override; PendingTransaction* restoreMultisigTransaction(const std::string& signData) override; PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index 594fcc6f1..933916076 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -720,6 +720,11 @@ struct Wallet * @return number of imported images */ virtual size_t importMultisigImages(const std::vector<std::string>& images) = 0; + /** + * @brief hasMultisigPartialKeyImages - checks if wallet needs to import multisig key images from other participants + * @return true if there are partial key images + */ + virtual bool hasMultisigPartialKeyImages() const = 0; /** * @brief restoreMultisigTransaction creates PendingTransaction from signData diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b266821d6..6502463e2 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -161,6 +161,7 @@ struct options { } }; const command_line::arg_descriptor<uint64_t> kdf_rounds = {"kdf-rounds", tools::wallet2::tr("Number of rounds for the key derivation function"), 1}; + const command_line::arg_descriptor<std::string> hw_device = {"hw-device", tools::wallet2::tr("HW device to use"), ""}; }; void do_prepare_file_names(const std::string& file_path, std::string& keys_file, std::string& wallet_file) @@ -211,6 +212,7 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl auto daemon_address = command_line::get_arg(vm, opts.daemon_address); auto daemon_host = command_line::get_arg(vm, opts.daemon_host); auto daemon_port = command_line::get_arg(vm, opts.daemon_port); + auto device_name = command_line::get_arg(vm, opts.hw_device); THROW_WALLET_EXCEPTION_IF(!daemon_address.empty() && !daemon_host.empty() && 0 != daemon_port, tools::error::wallet_internal_error, tools::wallet2::tr("can't specify daemon host or port more than once")); @@ -261,10 +263,11 @@ std::unique_ptr<tools::wallet2> make_basic(const boost::program_options::variabl catch (const std::exception &e) { } } - std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds)); - wallet->init(unattended, std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); + std::unique_ptr<tools::wallet2> wallet(new tools::wallet2(nettype, kdf_rounds, unattended)); + wallet->init(std::move(daemon_address), std::move(login), 0, false, *trusted_daemon); boost::filesystem::path ringdb_path = command_line::get_arg(vm, opts.shared_ringdb_dir); wallet->set_ring_database(ringdb_path.string()); + wallet->device_name(device_name); return wallet; } @@ -748,7 +751,7 @@ wallet_keys_unlocker::~wallet_keys_unlocker() w.encrypt_keys(key); } -wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): +wallet2::wallet2(network_type nettype, uint64_t kdf_rounds, bool unattended): m_multisig_rescan_info(NULL), m_multisig_rescan_k(NULL), m_run(true), @@ -796,7 +799,7 @@ wallet2::wallet2(network_type nettype, uint64_t kdf_rounds): m_ringdb(), m_last_block_reward(0), m_encrypt_keys_after_refresh(boost::none), - m_unattended(false) + m_unattended(unattended) { } @@ -814,6 +817,11 @@ bool wallet2::has_stagenet_option(const boost::program_options::variables_map& v return command_line::get_arg(vm, options().stagenet); } +std::string wallet2::device_name_option(const boost::program_options::variables_map& vm) +{ + return command_line::get_arg(vm, options().hw_device); +} + void wallet2::init_options(boost::program_options::options_description& desc_params) { const options opts{}; @@ -829,6 +837,7 @@ void wallet2::init_options(boost::program_options::options_description& desc_par command_line::add_arg(desc_params, opts.stagenet); command_line::add_arg(desc_params, opts.shared_ringdb_dir); command_line::add_arg(desc_params, opts.kdf_rounds); + command_line::add_arg(desc_params, opts.hw_device); } std::unique_ptr<wallet2> wallet2::make_from_json(const boost::program_options::variables_map& vm, bool unattended, const std::string& json_file, const std::function<boost::optional<tools::password_container>(const char *, bool)> &password_prompter) @@ -872,9 +881,8 @@ std::unique_ptr<wallet2> wallet2::make_dummy(const boost::program_options::varia } //---------------------------------------------------------------------------------------------------- -bool wallet2::init(bool unattended, std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) +bool wallet2::init(std::string daemon_address, boost::optional<epee::net_utils::http::login> daemon_login, uint64_t upper_transaction_weight_limit, bool ssl, bool trusted_daemon) { - m_unattended = unattended; m_checkpoints.init_default_checkpoints(m_nettype); if(m_http_client.is_connected()) m_http_client.disconnect(); @@ -984,6 +992,27 @@ bool wallet2::get_multisig_seed(epee::wipeable_string& seed, const epee::wipeabl return true; } //---------------------------------------------------------------------------------------------------- +bool wallet2::reconnect_device() +{ + bool r = true; + hw::device &hwdev = hw::get_device(m_device_name); + hwdev.set_name(m_device_name); + r = hwdev.init(); + if (!r){ + LOG_PRINT_L2("Could not init device"); + return false; + } + + r = hwdev.connect(); + if (!r){ + LOG_PRINT_L2("Could not connect to the device"); + return false; + } + + m_account.set_device(hwdev); + return true; +} +//---------------------------------------------------------------------------------------------------- /*! * \brief Gets the seed language */ @@ -2980,6 +3009,9 @@ bool wallet2::store_keys(const std::string& keys_file_name, const epee::wipeable value2.SetUint(1); json.AddMember("encrypted_secret_keys", value2, json.GetAllocator()); + value.SetString(m_device_name.c_str(), m_device_name.size()); + json.AddMember("device_name", value, json.GetAllocator()); + // Serialize the JSON object rapidjson::StringBuffer buffer; rapidjson::Writer<rapidjson::StringBuffer> writer(buffer); @@ -3088,6 +3120,7 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_ignore_fractional_outputs = true; m_subaddress_lookahead_major = SUBADDRESS_LOOKAHEAD_MAJOR; m_subaddress_lookahead_minor = SUBADDRESS_LOOKAHEAD_MINOR; + m_device_name = ""; m_key_on_device = false; encrypted_secret_keys = false; } @@ -3219,8 +3252,15 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ m_subaddress_lookahead_major = field_subaddress_lookahead_major; GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, subaddress_lookahead_minor, uint32_t, Uint, false, SUBADDRESS_LOOKAHEAD_MINOR); m_subaddress_lookahead_minor = field_subaddress_lookahead_minor; + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, encrypted_secret_keys, uint32_t, Uint, false, false); encrypted_secret_keys = field_encrypted_secret_keys; + + GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, device_name, std::string, String, false, std::string()); + if (m_device_name.empty() && field_device_name_found) + { + m_device_name = field_device_name; + } } else { @@ -3231,7 +3271,8 @@ bool wallet2::load_keys(const std::string& keys_file_name, const epee::wipeable_ r = epee::serialization::load_t_from_binary(m_account, account_data); if (r && m_key_on_device) { LOG_PRINT_L0("Account on device. Initing device..."); - hw::device &hwdev = hw::get_device("Ledger"); + hw::device &hwdev = hw::get_device(m_device_name); + hwdev.set_name(m_device_name); hwdev.init(); hwdev.connect(); m_account.set_device(hwdev); @@ -3673,13 +3714,18 @@ void wallet2::restore(const std::string& wallet_, const epee::wipeable_string& p THROW_WALLET_EXCEPTION_IF(boost::filesystem::exists(m_keys_file, ignored_ec), error::file_exists, m_keys_file); } m_key_on_device = true; - m_account.create_from_device(device_name); + + auto &hwdev = hw::get_device(device_name); + hwdev.set_name(device_name); + + m_account.create_from_device(hwdev); m_account_public_address = m_account.get_keys().m_account_address; m_watch_only = false; m_multisig = false; m_multisig_threshold = 0; m_multisig_signers.clear(); setup_keys(password); + m_device_name = device_name; create_keys_file(wallet_, false, password, m_nettype != MAINNET || create_address_file); if (m_subaddress_lookahead_major == SUBADDRESS_LOOKAHEAD_MAJOR && m_subaddress_lookahead_minor == SUBADDRESS_LOOKAHEAD_MINOR) @@ -10784,7 +10830,12 @@ size_t wallet2::import_multisig(std::vector<cryptonote::blobdata> blobs) refresh(false); } - catch (...) {} + catch (...) + { + m_multisig_rescan_info = NULL; + m_multisig_rescan_k = NULL; + throw; + } m_multisig_rescan_info = NULL; m_multisig_rescan_k = NULL; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 402066b50..c30ca1d85 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -172,6 +172,7 @@ namespace tools static bool has_testnet_option(const boost::program_options::variables_map& vm); static bool has_stagenet_option(const boost::program_options::variables_map& vm); + static std::string device_name_option(const boost::program_options::variables_map& vm); static void init_options(boost::program_options::options_description& desc_params); //! Uses stdin and stdout. Returns a wallet2 if no errors. @@ -189,7 +190,7 @@ namespace tools static bool verify_password(const std::string& keys_file_name, const epee::wipeable_string& password, bool no_spend_key, hw::device &hwdev, uint64_t kdf_rounds); - wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1); + wallet2(cryptonote::network_type nettype = cryptonote::MAINNET, uint64_t kdf_rounds = 1, bool unattended = false); ~wallet2(); struct multisig_info @@ -638,7 +639,7 @@ namespace tools bool explicit_refresh_from_block_height() const {return m_explicit_refresh_from_block_height;} bool deinit(); - bool init(bool unatteded, std::string daemon_address = "http://localhost:8080", + bool init(std::string daemon_address = "http://localhost:8080", boost::optional<epee::net_utils::http::login> daemon_login = boost::none, uint64_t upper_transaction_weight_limit = 0, bool ssl = false, bool trusted_daemon = false); void stop() { m_run.store(false, std::memory_order_relaxed); } @@ -709,6 +710,7 @@ namespace tools bool has_unknown_key_images() const; bool get_multisig_seed(epee::wipeable_string& seed, const epee::wipeable_string &passphrase = std::string(), bool raw = true) const; bool key_on_device() const { return m_key_on_device; } + bool reconnect_device(); // locked & unlocked balance of given or current subaddress account uint64_t balance(uint32_t subaddr_index_major) const; @@ -938,6 +940,8 @@ namespace tools void ignore_fractional_outputs(bool value) { m_ignore_fractional_outputs = value; } bool confirm_non_default_ring_size() const { return m_confirm_non_default_ring_size; } void confirm_non_default_ring_size(bool always) { m_confirm_non_default_ring_size = always; } + const std::string & device_name() const { return m_device_name; } + void device_name(const std::string & device_name) { m_device_name = device_name; } bool get_tx_key(const crypto::hash &txid, crypto::secret_key &tx_key, std::vector<crypto::secret_key> &additional_tx_keys) const; void set_tx_key(const crypto::hash &txid, const crypto::secret_key &tx_key, const std::vector<crypto::secret_key> &additional_tx_keys); @@ -1319,6 +1323,7 @@ namespace tools NodeRPCProxy m_node_rpc_proxy; std::unordered_set<crypto::hash> m_scanned_pool_txs[2]; size_t m_subaddress_lookahead_major, m_subaddress_lookahead_minor; + std::string m_device_name; // Light wallet bool m_light_wallet; /* sends view key to daemon for scanning */ diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index ee86587bc..86b46b173 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -51,6 +51,7 @@ using namespace epee; #include "mnemonics/electrum-words.h" #include "rpc/rpc_args.h" #include "rpc/core_rpc_server_commands_defs.h" +#include "daemonizer/daemonizer.h" #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -759,10 +760,10 @@ namespace tools { if (get_tx_key) { - std::string s = epee::string_tools::pod_to_hex(ptx.tx_key); + epee::wipeable_string s = epee::to_hex::wipeable_string(ptx.tx_key); for (const crypto::secret_key& additional_tx_key : ptx.additional_tx_keys) - s += epee::string_tools::pod_to_hex(additional_tx_key); - fill(tx_key, s); + s += epee::to_hex::wipeable_string(additional_tx_key); + fill(tx_key, std::string(s.data(), s.size())); } // Compute amount leaving wallet in tx. By convention dests does not include change outputs fill(amount, total_amount(ptx)); @@ -1569,11 +1570,13 @@ namespace tools } else if(req.key_type.compare("view_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_view_secret_key); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_view_secret_key); + res.key = std::string(key.data(), key.size()); } else if(req.key_type.compare("spend_key") == 0) { - res.key = string_tools::pod_to_hex(m_wallet->get_account().get_keys().m_spend_secret_key); + epee::wipeable_string key = epee::to_hex::wipeable_string(m_wallet->get_account().get_keys().m_spend_secret_key); + res.key = std::string(key.data(), key.size()); } else { @@ -1799,11 +1802,11 @@ namespace tools return false; } - std::ostringstream oss; - oss << epee::string_tools::pod_to_hex(tx_key); + epee::wipeable_string s; + s += epee::to_hex::wipeable_string(tx_key); for (size_t i = 0; i < additional_tx_keys.size(); ++i) - oss << epee::string_tools::pod_to_hex(additional_tx_keys[i]); - res.tx_key = oss.str(); + s += epee::to_hex::wipeable_string(additional_tx_keys[i]); + res.tx_key = std::string(s.data(), s.size()); return true; } //------------------------------------------------------------------------------------------------------------------------------ @@ -1819,26 +1822,33 @@ namespace tools return false; } - std::string tx_key_str = req.tx_key; + epee::wipeable_string tx_key_str = req.tx_key; + if (tx_key_str.size() < 64 || tx_key_str.size() % 64) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; + er.message = "Tx key has invalid format"; + return false; + } + const char *data = tx_key_str.data(); crypto::secret_key tx_key; - if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), tx_key)) + if (!epee::wipeable_string(data, 64).hex_to_pod(unwrap(unwrap(tx_key)))) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; er.message = "Tx key has invalid format"; return false; } - tx_key_str = tx_key_str.substr(64); + size_t offset = 64; std::vector<crypto::secret_key> additional_tx_keys; - while (!tx_key_str.empty()) + while (offset < tx_key_str.size()) { additional_tx_keys.resize(additional_tx_keys.size() + 1); - if (!epee::string_tools::hex_to_pod(tx_key_str.substr(0, 64), additional_tx_keys.back())) + if (!epee::wipeable_string(data + offset, 64).hex_to_pod(unwrap(unwrap(additional_tx_keys.back())))) { er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY; er.message = "Tx key has invalid format"; return false; } - tx_key_str = tx_key_str.substr(64); + offset += 64; } cryptonote::address_parse_info info; @@ -3265,12 +3275,172 @@ namespace tools //------------------------------------------------------------------------------------------------------------------------------ } +class t_daemon +{ +private: + const boost::program_options::variables_map& vm; + +public: + t_daemon(boost::program_options::variables_map const & _vm) + : vm(_vm) + { + } + + bool run() + { + std::unique_ptr<tools::wallet2> wal; + try + { + const bool testnet = tools::wallet2::has_testnet_option(vm); + const bool stagenet = tools::wallet2::has_stagenet_option(vm); + if (testnet && stagenet) + { + MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); + return false; + } + + const auto arg_wallet_file = wallet_args::arg_wallet_file(); + const auto arg_from_json = wallet_args::arg_generate_from_json(); + + const auto wallet_file = command_line::get_arg(vm, arg_wallet_file); + const auto from_json = command_line::get_arg(vm, arg_from_json); + const auto wallet_dir = command_line::get_arg(vm, arg_wallet_dir); + const auto prompt_for_password = command_line::get_arg(vm, arg_prompt_for_password); + const auto password_prompt = prompt_for_password ? password_prompter : nullptr; + + if(!wallet_file.empty() && !from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); + return false; + } + + if (!wallet_dir.empty()) + { + wal = NULL; + goto just_dir; + } + + if (wallet_file.empty() && from_json.empty()) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); + return false; + } + + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); + if(!wallet_file.empty()) + { + wal = tools::wallet2::make_from_file(vm, true, wallet_file, password_prompt).first; + } + else + { + try + { + wal = tools::wallet2::make_from_json(vm, true, from_json, password_prompt); + } + catch (const std::exception &e) + { + MERROR("Error creating wallet: " << e.what()); + return false; + } + } + if (!wal) + { + return false; + } + + bool quit = false; + tools::signal_handler::install([&wal, &quit](int) { + assert(wal); + quit = true; + wal->stop(); + }); + + wal->refresh(wal->is_trusted_daemon()); + // if we ^C during potentially length load/refresh, there's no server loop yet + if (quit) + { + MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); + wal->store(); + MINFO(tools::wallet_rpc_server::tr("Successfully saved")); + return false; + } + MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); + return false; + } + just_dir: + tools::wallet_rpc_server wrpc; + if (wal) wrpc.set_wallet(wal.release()); + bool r = wrpc.init(&vm); + CHECK_AND_ASSERT_MES(r, false, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); + tools::signal_handler::install([&wrpc](int) { + wrpc.send_stop_signal(); + }); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); + try + { + wrpc.run(); + } + catch (const std::exception &e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); + return false; + } + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); + try + { + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); + wrpc.stop(); + LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); + } + catch (const std::exception& e) + { + LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); + return false; + } + return true; + } +}; + +class t_executor final +{ +public: + static std::string const NAME; + + std::string const & name() + { + return NAME; + } + + t_daemon create_daemon(boost::program_options::variables_map const & vm) + { + return t_daemon(vm); + } + + bool run_non_interactive(boost::program_options::variables_map const & vm) + { + return t_daemon(vm).run(); + } + + bool run_interactive(boost::program_options::variables_map const & vm) + { + return t_daemon(vm).run(); + } +}; + +std::string const t_executor::NAME = "Wallet RPC Daemon"; + int main(int argc, char** argv) { namespace po = boost::program_options; const auto arg_wallet_file = wallet_args::arg_wallet_file(); const auto arg_from_json = wallet_args::arg_generate_from_json(); + po::options_description hidden_options("Hidden"); + po::options_description desc_params(wallet_args::tr("Wallet options")); tools::wallet2::init_options(desc_params); command_line::add_arg(desc_params, arg_rpc_bind_port); @@ -3282,6 +3452,8 @@ int main(int argc, char** argv) { command_line::add_arg(desc_params, arg_wallet_dir); command_line::add_arg(desc_params, arg_prompt_for_password); + daemonizer::init_options(hidden_options, desc_params); + boost::optional<po::variables_map> vm; bool should_terminate = false; std::tie(vm, should_terminate) = wallet_args::main( @@ -3303,115 +3475,5 @@ int main(int argc, char** argv) { return 0; } - std::unique_ptr<tools::wallet2> wal; - try - { - const bool testnet = tools::wallet2::has_testnet_option(*vm); - const bool stagenet = tools::wallet2::has_stagenet_option(*vm); - if (testnet && stagenet) - { - MERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --testnet and --stagenet")); - return 1; - } - - const auto wallet_file = command_line::get_arg(*vm, arg_wallet_file); - const auto from_json = command_line::get_arg(*vm, arg_from_json); - const auto wallet_dir = command_line::get_arg(*vm, arg_wallet_dir); - const auto prompt_for_password = command_line::get_arg(*vm, arg_prompt_for_password); - const auto password_prompt = prompt_for_password ? password_prompter : nullptr; - - if(!wallet_file.empty() && !from_json.empty()) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Can't specify more than one of --wallet-file and --generate-from-json")); - return 1; - } - - if (!wallet_dir.empty()) - { - wal = NULL; - goto just_dir; - } - - if (wallet_file.empty() && from_json.empty()) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Must specify --wallet-file or --generate-from-json or --wallet-dir")); - return 1; - } - - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Loading wallet...")); - if(!wallet_file.empty()) - { - wal = tools::wallet2::make_from_file(*vm, true, wallet_file, password_prompt).first; - } - else - { - try - { - wal = tools::wallet2::make_from_json(*vm, true, from_json, password_prompt); - } - catch (const std::exception &e) - { - MERROR("Error creating wallet: " << e.what()); - return 1; - } - } - if (!wal) - { - return 1; - } - - bool quit = false; - tools::signal_handler::install([&wal, &quit](int) { - assert(wal); - quit = true; - wal->stop(); - }); - - wal->refresh(wal->is_trusted_daemon()); - // if we ^C during potentially length load/refresh, there's no server loop yet - if (quit) - { - MINFO(tools::wallet_rpc_server::tr("Saving wallet...")); - wal->store(); - MINFO(tools::wallet_rpc_server::tr("Successfully saved")); - return 1; - } - MINFO(tools::wallet_rpc_server::tr("Successfully loaded")); - } - catch (const std::exception& e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Wallet initialization failed: ") << e.what()); - return 1; - } -just_dir: - tools::wallet_rpc_server wrpc; - if (wal) wrpc.set_wallet(wal.release()); - bool r = wrpc.init(&(vm.get())); - CHECK_AND_ASSERT_MES(r, 1, tools::wallet_rpc_server::tr("Failed to initialize wallet RPC server")); - tools::signal_handler::install([&wrpc](int) { - wrpc.send_stop_signal(); - }); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Starting wallet RPC server")); - try - { - wrpc.run(); - } - catch (const std::exception &e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to run wallet: ") << e.what()); - return 1; - } - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Stopped wallet RPC server")); - try - { - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Saving wallet...")); - wrpc.stop(); - LOG_PRINT_L0(tools::wallet_rpc_server::tr("Successfully saved")); - } - catch (const std::exception& e) - { - LOG_ERROR(tools::wallet_rpc_server::tr("Failed to save wallet: ") << e.what()); - return 1; - } - return 0; + return daemonizer::daemonize(argc, const_cast<const char**>(argv), t_executor{}, *vm) ? 0 : 1; } |