From 9176b83431cdedd5f4a1caa990f9ecc09a76a219 Mon Sep 17 00:00:00 2001 From: Daniel Suo Date: Sat, 13 Dec 2014 16:02:30 -0500 Subject: [PATCH] generate keys --- .gitignore | 2 +- README.md | 3 +- REQUIRE | 2 +- doc/README.md | 2 +- src/Coin.jl | 30 ++-- src/Crypto/Crypto.jl | 13 -- src/Crypto/ECDSA.jl | 7 - src/Crypto/EllipticCurves.jl | 159 ---------------------- src/Crypto/RIPEMD.jl | 207 ---------------------------- src/Crypto/SHA2.jl | 233 -------------------------------- src/Util/{Base.jl => Base58.jl} | 14 +- src/Util/Util.jl | 11 -- src/Wallet/Keys.jl | 60 ++++++++ src/Wallet/WIF.jl | 48 +++---- src/Wallet/Wallet.jl | 11 -- test/runtests.jl | 1 - 16 files changed, 107 insertions(+), 696 deletions(-) delete mode 100644 src/Crypto/Crypto.jl delete mode 100644 src/Crypto/ECDSA.jl delete mode 100644 src/Crypto/EllipticCurves.jl delete mode 100644 src/Crypto/RIPEMD.jl delete mode 100644 src/Crypto/SHA2.jl rename src/Util/{Base.jl => Base58.jl} (88%) delete mode 100644 src/Util/Util.jl create mode 100644 src/Wallet/Keys.jl delete mode 100644 src/Wallet/Wallet.jl diff --git a/.gitignore b/.gitignore index 1250a91..3e019ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ .DS_Store # Keep out code coverage files; they are generated on Travis -*.cov \ No newline at end of file +*.cov diff --git a/README.md b/README.md index bae820a..589ad64 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ First, we're going to implement a thin-client wallet. ## Vault and multisig - https://github.com/ciphrex/CoinVault +- BIP 32 HD Wallets [ref](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) ## Mining - https://github.com/ckolivas/cgminer @@ -51,4 +52,4 @@ First, we're going to implement a thin-client wallet. - [TheBlueMatt tests](https://github.com/TheBlueMatt/test-scripts) - Add to Julia pkg repo and get badge - Split off Crypto package -- Write Struct (a la Python's `struct` module) package? \ No newline at end of file +- Write Struct (a la Python's `struct` module) package? diff --git a/REQUIRE b/REQUIRE index f3e0e25..87f0245 100644 --- a/REQUIRE +++ b/REQUIRE @@ -1 +1 @@ -Coverage \ No newline at end of file +Coverage diff --git a/doc/README.md b/doc/README.md index 5e08a9e..fea1f63 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1 +1 @@ -Documentation goes here. \ No newline at end of file +Documentation goes here. diff --git a/src/Coin.jl b/src/Coin.jl index a4778aa..6826968 100644 --- a/src/Coin.jl +++ b/src/Coin.jl @@ -1,20 +1,26 @@ module Coin -############################################################################## -## -## Dependencies -## -############################################################################## +export + # Keys.jl + generate_keys, + get_public_key, + + # WIF.jl + private2wif, + wif2private, + wif_check_sum, -# TODO: move crypto into a separate package? + # Base58.jl + encode58, + decode58 ############################################################################## ## -## Exported methods and types +## Dependencies ## ############################################################################## -# export sha256 +using Crypto ############################################################################## ## @@ -22,8 +28,10 @@ module Coin ## ############################################################################## -include(joinpath("Crypto", "Crypto.jl")) -include(joinpath("Wallet", "Wallet.jl")) -include(joinpath("Util", "Util.jl")) +include(joinpath("Util", "Base58.jl")) +include(joinpath("Wallet", "Keys.jl")) +include(joinpath("Wallet", "WIF.jl")) + +Crypto.init() end # module Coin diff --git a/src/Crypto/Crypto.jl b/src/Crypto/Crypto.jl deleted file mode 100644 index a8dc2f1..0000000 --- a/src/Crypto/Crypto.jl +++ /dev/null @@ -1,13 +0,0 @@ -module Crypto - -############################################################################## -## -## Load files -## -############################################################################## - -include("SHA2.jl") -include("RIPEMD.jl") -include("ECDSA.jl") - -end \ No newline at end of file diff --git a/src/Crypto/ECDSA.jl b/src/Crypto/ECDSA.jl deleted file mode 100644 index b6fb1b7..0000000 --- a/src/Crypto/ECDSA.jl +++ /dev/null @@ -1,7 +0,0 @@ -# module ECDSA - -include("EllipticCurves.jl") - - - -# end # module ECDSA \ No newline at end of file diff --git a/src/Crypto/EllipticCurves.jl b/src/Crypto/EllipticCurves.jl deleted file mode 100644 index 9dd15d2..0000000 --- a/src/Crypto/EllipticCurves.jl +++ /dev/null @@ -1,159 +0,0 @@ -module EllipticCurves - -# NOTE: Borrowed heavily from @wwilson - -############################################################################## -## -## Exported methods and types -## -############################################################################## - -export Curve, ConcretePoint, IdealPoint - -############################################################################## -## -## Elliptic curve type definition -## -############################################################################## - -immutable Curve - a::Number - b::Number - discriminant - - function Curve(a::Number, b::Number) - d = -16 * (4 * a*a*a + 27 * b * b) - if d == 0 - error("Curve is singular.") - end - new(a, b, d) - end -end - -function show(io::IO, e::Curve) - print(io, "y^2 = x^3 + ", e.a, "x + ", e.b, "\n") -end - -function ==(e::Curve, f::Curve) - return e.a == f.a && e.b == f.b -end - -############################################################################## -## -## Point type definition -## -############################################################################## - -abstract Point - -type ConcretePoint <: Point - x::Number - y::Number - c::Curve - - function ConcretePoint(x::Number, y::Number, c::Curve) - y*y == x*x*x + c.a*x + c.b ? new(x, y, c) : error("Point is not on curve.") - end -end - -type IdealPoint <: Point - c::Curve - IdealPoint(c::Curve) = new(c) -end - -function ==(p::IdealPoint, q::IdealPoint) - return p.c == q.c -end - -function ==(p::IdealPoint, q::ConcretePoint) - return false -end - -function ==(p::ConcretePoint, q::IdealPoint) - return false -end - -function ==(p::ConcretePoint, q::ConcretePoint) - return p.x == q.x && p.y == q.y && p.c == q.c -end - -function -(p::IdealPoint) - return p -end - -function -(p::ConcretePoint) - return ConcretePoint(p.x,-p.y,p.c) -end - -function +(p1::IdealPoint, p2::IdealPoint) - @assert p1.c == p2.c - return p1 -end - -function +(p1::IdealPoint, p2::ConcretePoint) - @assert p1.c == p2.c - return p2 -end - -function +(p1::ConcretePoint, p2::IdealPoint) - @assert p1.c == p2.c - return p1 -end - -function +(p1::ConcretePoint, p2::ConcretePoint) - @assert p1.c == p2.c - if p1 == p2 - if p1.y == 0 - return IdealPoint(p1.c) - end - m = (3*p1.x*p1.x+p1.c.a) / (2*p1.y) - else - if p1.x == p2.x - return IdealPoint(p1.c) - end - m = (p2.y-p1.y) / (p2.x-p1.x) - end - - new_x = m*m - p2.x - p1.x - new_y = m*(new_x - p1.x) + p1.y - - return ConcretePoint(new_x, -new_y, p1.c) -end - -function -(p1::Point, p2::Point) - return p1 + (-p2) -end - -function *(p::IdealPoint, n::Integer) - return p -end - -function *(n::Integer, p::IdealPoint) - return p -end - -function *(p::ConcretePoint, n::Integer) - if n == 0 - return IdealPoint(p.c) - elseif n < 0 - return -p * -n - else - q::Point = p - r::Point = ( n & 1 == 1 ? p : IdealPoint(p.c)) - i = 2 - while i <= n - q = q+q - if n & i == i - r = q+r - end - i = i << 1 - end - return r - end -end - -function *(n::Integer, p::ConcretePoint) - return p*n -end - -end # module EllipticCurves \ No newline at end of file diff --git a/src/Crypto/RIPEMD.jl b/src/Crypto/RIPEMD.jl deleted file mode 100644 index f92584c..0000000 --- a/src/Crypto/RIPEMD.jl +++ /dev/null @@ -1,207 +0,0 @@ -module RIPEMD - -############################################################################## -## -## Exported methods and types -## -############################################################################## - -export ripemd160 - -############################################################################## -## -## Notes -## -############################################################################## - -# WARNING: This module is in need of some clean up - -# Pseudocode: http://homes.esat.kuleuven.be/~bosselae/ripemd/rmd160.txt -# Additional notes: https://en.bitcoin.it/wiki/RIPEMD-160 -# -# RIPEMD-160 is an iterative hash function that operates on 32-bit words. -# The round function takes as input a 5-word chaining variable and a 16-word -# message block and maps this to a new chaining variable. All operations are -# defined on 32-bit words. Padding is identical to that of MD4. - -############################################################################## -## -## Definitions -## -############################################################################## - -const BLOCK_SIZE = 64 # SHA256 operates on 64 (512 bit) blocks -const WORDS_PER_BLOCK = 16 - -# Nonlinear functions at bit level: exor, mux, -, mux, - -const f = [(x, y, z) -> x $ y $ z, - (x, y, z) -> (x & y) | (~x & z), - (x, y, z) -> (x | ~y) $ z, - (x, y, z) -> (x & z) | (y & ~z), - (x, y, z) -> x $ (y | ~z)] - -# Added constants (hexadecimal) -# NOTE: First 32 bits of the fractional parts of the square (K0)/cube (K1) -# roots of the first 4 primes, 2 through 7. -const K0 = [0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E] -const K1 = [0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000] - -# Selection of message word -# NOTE: Julia is 1-indexed -const r0 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, - 8, 5, 14, 2, 11, 7, 16, 4, 13, 1, 10, 6, 3, 15, 12, 9, - 4, 11, 15, 5, 10, 16, 9, 2, 3, 8, 1, 7, 14, 12, 6, 13, - 2, 10, 12, 11, 1, 9, 13, 5, 14, 4, 8, 16, 15, 6, 7, 3, - 5, 1, 6, 10, 8, 13, 3, 11, 15, 2, 4, 9, 12, 7, 16, 14] -const r1 = [5, 15, 8, 1, 10, 3, 12, 5, 14, 7, 16, 9, 2, 11, 4, 13, - 7, 12, 4, 8, 1, 14, 6, 11, 15, 16, 9, 13, 5, 10, 2, 3, - 16, 6, 2, 4, 8, 15, 7, 10, 12, 9, 13, 3, 11, 1, 5, 14, - 9, 7, 5, 2, 4, 12, 16, 1, 6, 13, 3, 14, 10, 8, 11, 15, - 13, 16, 11, 5, 2, 6, 9, 8, 7, 3, 14, 15, 1, 4, 10, 12] - -# Amount for rotate left (rol) -# NOTE: Julia is 1-indexed -const s0 = [11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, - 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, - 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, - 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, - 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6] -const s1 = [8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, - 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, - 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, - 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, - 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11] - -############################################################################## -## -## Initialize helper functions -## -############################################################################## - -# Hard-coding for 32-bit unsigned ints -# Technically, we could use the '>>' operator for right shift because -# we're only using unsigned ints, but we explicitly use logical right shift -ROTRIGHT(num, shift) = (num >>> shift) | (num << (32 - shift)) -ROTLEFT(num, shift) = (num << shift) | (num >>> (32 - shift)) - -############################################################################## -## -## Function definitions -## -############################################################################## - -function transform!(state, block) - A0, B0, C0, D0, E0 = state - A1, B1, C1, D1, E1 = state - - X = zeros(Uint32, WORDS_PER_BLOCK) - - for i = 1:WORDS_PER_BLOCK - X[i] = uint32(block[4 * (i - 1) + 4]) << 24 + - uint32(block[4 * (i - 1) + 3]) << 16 + - uint32(block[4 * (i - 1) + 2]) << 8 + - uint32(block[4 * (i - 1) + 1]) - end - - for j = 1:80 - # TODO: Replace with cld(j, 16) in Julia v0.4 - div, rem = divrem(j, 16) - i = div + (rem == 0 ? 0 : 1) - k = 5 - i + 1 - - T = uint32(A0 + f[i](B0, C0, D0) + X[r0[j]] + K0[i]) - T = uint32(ROTLEFT(T, s0[j]) + E0) - - A0 = E0 - E0 = D0 - D0 = ROTLEFT(C0, 10) - C0 = B0 - B0 = T - - T = uint32(A1 + f[k](B1, C1, D1) + X[r1[j]] + K1[i]) - T = uint32(ROTLEFT(T, s1[j]) + E1) - - A1 = E1 - E1 = D1 - D1 = ROTLEFT(C1, 10) - C1 = B1 - B1 = T - end - - T = uint32(state[2] + C0 + D1) - state[2] = uint32(state[3] + D0 + E1) - state[3] = uint32(state[4] + E0 + A1) - state[4] = uint32(state[5] + A0 + B1) - state[5] = uint32(state[1] + B0 + C1) - state[1] = uint32(T) - - return -end - -function ripemd160(msg::ASCIIString; is_hex=true) - - if is_hex - len = int(length(msg) / 2) - result = zeros(Uint8, len) - for i = 1:len - result[i] = uint8(parseint(msg[2 * i - 1: 2 * i],16)) - end - msg = result - else - # We only want byte array literal (i.e., character array) - msg = msg.data - end - - # Get original length and bit lengths - len = length(msg) - bitlen = len * 8 - - # Append the bit '1' to the message. - append!(msg, [0x80]) - - state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0] - - # Divide up message into blocks of BLOCK_SIZE = 512 bits - # and run through transformation - while length(msg) >= BLOCK_SIZE - transform!(state, msg[1:BLOCK_SIZE]) - msg = msg[BLOCK_SIZE + 1:end] - end - - # Get the number of characters untransformed - rem = length(msg) - - # If there are any characters remaining - if rem > 0 - - # Append k bits '0', where k is the minimum number >= 0 such that the - # resulting message length (modulo 512 in bits) is 448. - if length(msg) > BLOCK_SIZE - 8 - msg = append!(msg, zeros(Uint8, BLOCK_SIZE - rem)) - transform!(state, msg) - msg = zeros(Uint8, BLOCK_SIZE) - else - msg = append!(msg, zeros(Uint8, BLOCK_SIZE - rem)) - end - - # Append length of message (without the '1' bit or padding), in bits, as - # 64-bit big-endian integer (this will make the entire post-processed - # length a multiple of 512 bits) - msg[64] = (bitlen >>> 56) & 0xff - msg[63] = (bitlen >>> 48) & 0xff - msg[62] = (bitlen >>> 40) & 0xff - msg[61] = (bitlen >>> 32) & 0xff - msg[60] = (bitlen >>> 24) & 0xff - msg[59] = (bitlen >>> 16) & 0xff - msg[58] = (bitlen >>> 8) & 0xff - msg[57] = bitlen & 0xff - - # Process the last block - transform!(state, msg) - end - - # Assemble digest and return - return reduce((x, y) -> string(x, y), map(x -> lpad(hex(ntoh(uint32(x))), 8, "0"), state)) -end - -end # module RIPEMD \ No newline at end of file diff --git a/src/Crypto/SHA2.jl b/src/Crypto/SHA2.jl deleted file mode 100644 index 0ee12f8..0000000 --- a/src/Crypto/SHA2.jl +++ /dev/null @@ -1,233 +0,0 @@ -module SHA2 - -############################################################################## -## -## Exported methods and types -## -############################################################################## - -export sha256 - -############################################################################## -## -## Notes -## -############################################################################## - -# WARNING: This has only been tested on OS X 10.0.1 on a 64-bit machine. -# The implementation below may die on a 32-bit machine. -# WARNING: This implementation assumes string inputs - -# - All variables are 32-bit unsigned integers. From the Julia documentation -# -# http://docs.julialang.org/en/release-0.3/manual/integers-and-floating -# -point-numbers/ -# -# we see that -# -# Unsigned integers are input and output using the 0x prefix and hexadecimal -# (base 16) digits 0-9a-f (the capitalized digits A-F also work for input). -# The size of the unsigned value is determined by the number of hex digits. -# -# - Addition is calculated modulo 2^32 -# -# - For each round, there is one round constant k[i] and one entry in the -# message schedule array w[i], 0 ≤ i ≤ 63 -# -# - The compression function uses 8 working variables, a through h -# -# - Big-endian convention is used when expressing the constants in this -# pseudocode, and when parsing message block data from bytes to chunks, -# for example, the first chunk of the input message "abc" after padding is -# 0x61626380 - -############################################################################## -## -## Algorithm parameters -## -############################################################################## - -const DIGEST_SIZE = 32 # SHA256 outputs a 32 byte (256 bit) digest -const BLOCK_SIZE = 64 # SHA256 operates on 64 (512 bit) blocks -const CHUNKS_PER_BLOCK = 16 -const CHUNKS_PER_SCHEDULE = 64 - -############################################################################## -## -## Initialize array of round constants -## -############################################################################## - -# First 32 bits of the fractional parts of the cube roots of the first 64 -# primes, 2 through 311. A sample generation function below: -# -# function initial_array_of_round_constraints(n) -# fractional_cuberoot = cbrt(n) % 1 -# first_32_bits = floor(fractional_cube_root * 2^32) -# @printf "%x" first_32_bits -# return first_32_bits -# end - -const k = [0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, - 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, - 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, - 0x06ca6351, 0x14292967, 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 0xa2bfe8a1, 0xa81a664b, - 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, - 0x5b9cca4f, 0x682e6ff3, 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2] - -############################################################################## -## -## Initialize helper functions -## -############################################################################## - -# Hard-coding for 32-bit unsigned ints -# Technically, we could use the '>>' operator for right shift because -# we're only using unsigned ints, but we explicitly use logical right shift -ROTRIGHT(num, shift) = (num >>> shift) | (num << (32 - shift)) -ROTLEFT(num, shift) = (num << shift) | (num >>> (32 - shift)) - -CH(x, y, z) = z $ (x & (y $ z)) -MA(x, y, z) = ((x | y) & z) | (x & y) - -E0(x) = (ROTRIGHT(x, 2) $ ROTRIGHT(x, 13) $ ROTRIGHT(x, 22)) -E1(x) = (ROTRIGHT(x, 6) $ ROTRIGHT(x, 11) $ ROTRIGHT(x, 25)) - -S0(x) = (ROTRIGHT(x, 7) $ ROTRIGHT(x, 18) $ (x >>> 3)) -S1(x) = (ROTRIGHT(x, 17) $ ROTRIGHT(x, 19) $ (x >>> 10)) - -############################################################################## -## -## Function definitions -## -############################################################################## - -function transform!(state, block) - - # Pre-allocate the message schedule array (64 8-bit words) - m = zeros(Uint32, CHUNKS_PER_SCHEDULE) - - for i = 1:CHUNKS_PER_BLOCK - m[i] = uint32(block[4 * (i - 1) + 1]) << 24 + - uint32(block[4 * (i - 1) + 2]) << 16 + - uint32(block[4 * (i - 1) + 3]) << 8 + - uint32(block[4 * (i - 1) + 4]) - end - for i = CHUNKS_PER_BLOCK + 1:CHUNKS_PER_SCHEDULE - s0 = S0(m[i-15]) - s1 = S1(m[i-2]) - - m[i] = m[i-16] + s0 + m[i-7] + s1 - end - - a, b, c, d, e, f, g, h = state - - for i = 1:CHUNKS_PER_SCHEDULE - t1 = h + E1(e) + CH(e, f, g) + k[i] + m[i] - t2 = E0(a) + MA(a, b, c) - - h = g; - g = f; - f = e; - e = uint32(d + t1) - d = c; - c = b; - b = a; - a = uint32(t1 + t2) - end - - state[1] += a - state[2] += b - state[3] += c - state[4] += d - state[5] += e - state[6] += f - state[7] += g - state[8] += h - - return -end - -function sha256(msg::ASCIIString; is_hex=true) - - if is_hex - len = int(length(msg) / 2) - result = zeros(Uint8, len) - for i = 1:len - result[i] = uint8(parseint(msg[2 * i - 1: 2 * i],16)) - end - msg = result - else - # We only want byte array literal (i.e., character array) - msg = msg.data - end - - # Get original length and bit lengths - len = length(msg) - bitlen = len * 8 - - # Append the bit '1' to the message. - append!(msg, [0x80]) - - # First 32 bits of the fractional parts of the square roots of the first 8 - # primes, 2 through 19. A sample generation function below: - # - # function initial_hash_value(n) - # fractional_square_root = sqrt(n) % 1 - # first_32_bits = floor(fractional_square_root * 2^32) - # @printf "%x" first_32_bits - # return first_32_bits - # end - - state = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19] - - # Divide up message into blocks of BLOCK_SIZE = 512 bits - # and run through transformation - while length(msg) >= BLOCK_SIZE - transform!(state, msg[1:BLOCK_SIZE]) - msg = msg[BLOCK_SIZE + 1:end] - end - - # Get the number of characters untransformed - rem = length(msg) - - # If there are any characters remaining - if rem > 0 - - # Append k bits '0', where k is the minimum number >= 0 such that the - # resulting message length (modulo 512 in bits) is 448. - if length(msg) > BLOCK_SIZE - 8 - msg = append!(msg, zeros(Uint8, BLOCK_SIZE - rem)) - transform!(state, msg) - msg = zeros(Uint8, BLOCK_SIZE) - else - msg = append!(msg, zeros(Uint8, BLOCK_SIZE - rem)) - end - - # Append length of message (without the '1' bit or padding), in bits, as - # 64-bit big-endian integer (this will make the entire post-processed - # length a multiple of 512 bits) - msg[57] = (bitlen >>> 56) & 0xff - msg[58] = (bitlen >>> 48) & 0xff - msg[59] = (bitlen >>> 40) & 0xff - msg[60] = (bitlen >>> 32) & 0xff - msg[61] = (bitlen >>> 24) & 0xff - msg[62] = (bitlen >>> 16) & 0xff - msg[63] = (bitlen >>> 8) & 0xff - msg[64] = bitlen & 0xff - - # Process the last block - transform!(state, msg) - end - - # Assemble digest and return - return reduce((x, y) -> string(x, y), map(x -> lpad(hex(uint32(x)), 8, "0"), state)) -end - -end # module SHA256 diff --git a/src/Util/Base.jl b/src/Util/Base58.jl similarity index 88% rename from src/Util/Base.jl rename to src/Util/Base58.jl index 303d5d4..2e79caf 100644 --- a/src/Util/Base.jl +++ b/src/Util/Base58.jl @@ -1,12 +1,10 @@ -module Base - ############################################################################## ## -## Exported functions and modules +## References ## ############################################################################## -export encode, decode +# - https://github.com/bitcoin/bitcoin/blob/master/src/base58.cpp ############################################################################## ## @@ -29,7 +27,7 @@ const base58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" # but descriptive function names. # - n: integer we want to convert # - b: base we want to convert to -function encode(n, b) +function encode58(n; b = 58) # Require base to be 58 for now @assert b == 58 @@ -37,7 +35,7 @@ function encode(n, b) while n > 0 n, rem = divrem(n, b) - output = string(base58[rem + 1],output) + output = string(base58[rem + 1], output) end return output @@ -45,7 +43,7 @@ end # Decode from base 58 to integer # - n: base 58 number we want to convert -function decode(n, b) +function decode58(n; b = 58) # Require base to be 58 for now @assert b == 58 @@ -57,5 +55,3 @@ function decode(n, b) return result end - -end # module Base diff --git a/src/Util/Util.jl b/src/Util/Util.jl deleted file mode 100644 index 801a0ff..0000000 --- a/src/Util/Util.jl +++ /dev/null @@ -1,11 +0,0 @@ -module Util - -############################################################################## -## -## Load files -## -############################################################################## - -include("Base.jl") - -end # module Util \ No newline at end of file diff --git a/src/Wallet/Keys.jl b/src/Wallet/Keys.jl new file mode 100644 index 0000000..d5f9812 --- /dev/null +++ b/src/Wallet/Keys.jl @@ -0,0 +1,60 @@ +############################################################################## +## +## References +## +############################################################################## + +# - https://en.bitcoin.it/wiki/Technical_background_of_version_1_Bitcoin_addresses + +############################################################################## +## +## Definitions +## +############################################################################## + +# TODO: Compressed keys? +function generate_keys(network_id = "00", version = "1") + secret_key = join([hex(x) for x in Crypto.random(256)]) + public_key = get_public_key(secret_key, network_id = network_id, version = version) + + return (secret_key, public_key) +end + +function get_public_key(secret_key; network_id = "00", version = "1") + + # Generate corresponding public key generated with against ECDSA secp256k1 + # (65 bytes, 1 byte 0x04, 32 bytes corresponding to X coordinate, 32 bytes + # corresponding to Y coordinate) + public_key = Crypto.ec_public_key_create(secret_key) + + # Perform SHA-256 hashing on the public key + public_key = Crypto.digest("SHA256", public_key, is_hex=true) + + # Perform RIPEMD-160 hashing on the result of SHA-256 + public_key = Crypto.digest("RIPEMD160", public_key, is_hex=true) + + # Add version byte in front of RIPEMD-160 hash (0x00 for Main Network) + # Reference: https://bitcoin.org/en/developer-reference#address-conversion + public_key = string(network_id, public_key) + + # Get checksum by performing SHA256 hash twice and taking first 4 bytes + checksum = Crypto.digest("SHA256", public_key, is_hex=true) + checksum = Crypto.digest("SHA256", checksum, is_hex=true) + checksum = checksum[1:8] + + # Add the 4 checksum bytes from stage 7 at the end of extended RIPEMD-160 + # hash from stage 4. This is the 25-byte binary Bitcoin Address. + public_key = string(public_key, checksum) + + # Convert the result from a byte string into a base58 string using + # Base58Check encoding. This is the most commonly used Bitcoin Address format + # Reference: https://en.bitcoin.it/wiki/Base58Check_encoding + public_key = parseint(BigInt, public_key, 16) + public_key = encode58(public_key) + + # Append address version byte in hex + # Reference: https://en.bitcoin.it/wiki/List_of_address_prefixes + public_key = string(version, public_key) + + return public_key +end diff --git a/src/Wallet/WIF.jl b/src/Wallet/WIF.jl index 57a9bc2..6d71f8c 100644 --- a/src/Wallet/WIF.jl +++ b/src/Wallet/WIF.jl @@ -1,20 +1,10 @@ -module WIF - -############################################################################## -## -## Dependencies -## -############################################################################## - -using Coin - ############################################################################## ## -## Exported methods and types +## References ## ############################################################################## -export private2wif, wif2private, wif_checksum +# - https://en.bitcoin.it/wiki/WIF ############################################################################## ## @@ -26,44 +16,42 @@ export private2wif, wif2private, wif_checksum # Convert private key to WIF. # TODO: turn keys into objects to hold metadata -# - which_net: which network to use; 0x80 for mainnet, 0xef for testnet +# - network_id: which network to use; 0x80 for mainnet, 0xef for testnet # - compression: 01 if private key corresponds to compressed public key -function private2wif(pk::ASCIIString; which_net="80", compression="") - pk = string(which_net, pk, compression) +function private2wif(private_key; network_id = "80", compression = "") + private_key = string(network_id, private_key, compression) - hashed = Coin.Crypto.SHA2.sha256(pk) - hashed = Coin.Crypto.SHA2.sha256(hashed) + hashed = Crypto.digest("SHA256", private_key) + hashed = Crypto.digest("SHA256", hashed) checksum = hashed[1:8] - pk = string(pk, checksum) + private_key = string(private_key, checksum) - return Coin.Util.Base.encode(parseint(BigInt, pk, 16), 58) + return encode58(parseint(BigInt, private_key, 16), 58) end -function wif2private(wif::ASCIIString; is_compressed=false) - pk = hex(Coin.Util.Base.decode(wif, 58)) +function wif2private(wif; is_compressed=false) + private_key = hex(decode58(wif, 58)) # Drop the first two characters (network identifier) and last # 8 (checksum) - pk = pk[3:end-8] + private_key = private_key[3:end-8] if is_compressed - pk = pk[1:end-2] + private_key = private_key[1:end-2] end - return pk + return private_key end -function wif_checksum(wif::ASCIIString) - result = hex(Coin.Util.Base.decode(wif, 58)) +function wif_check_sum(wif) + result = hex(decode58(wif, 58)) checksum = result[end - 8 + 1:end] - result = Coin.Crypto.SHA2.sha256(result[1:end - 8]) - result = Coin.Crypto.SHA2.sha256(result) + result = Crypto.digest("SHA256", result[1:end - 8]) + result = Crypto.digest("SHA256", result) return result[1:8] == checksum end - -end # module WIF diff --git a/src/Wallet/Wallet.jl b/src/Wallet/Wallet.jl deleted file mode 100644 index d412b5f..0000000 --- a/src/Wallet/Wallet.jl +++ /dev/null @@ -1,11 +0,0 @@ -module Wallet - -############################################################################## -## -## Load files -## -############################################################################## - -include("WIF.jl") - -end # module Wallet \ No newline at end of file diff --git a/test/runtests.jl b/test/runtests.jl index 4d5d001..7eb2d97 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -59,4 +59,3 @@ base58data = parseint(BigInt, "800c28fca386c7a227600b2fe50b7cae11ec86d3bf1fbe471 # WIF checksum @test Coin.Wallet.WIF.wif_checksum("5HueCGU8rMjxEXxiPuD5BDku4MkFqeZyd4dZ1jvhTVqvbTLvyTJ") -