Module: BitcoinCigs

Extended by:
CryptoHelper
Defined in:
lib/bitcoin_cigs.rb,
lib/bitcoin_cigs/error.rb,
lib/bitcoin_cigs/point.rb,
lib/bitcoin_cigs/ec_key.rb,
lib/bitcoin_cigs/base_58.rb,
lib/bitcoin_cigs/version.rb,
lib/bitcoin_cigs/curve_fp.rb,
lib/bitcoin_cigs/signature.rb,
lib/bitcoin_cigs/public_key.rb,
lib/bitcoin_cigs/compact_int.rb,
lib/bitcoin_cigs/private_key.rb,
lib/bitcoin_cigs/crypto_helper.rb

Defined Under Namespace

Modules: CryptoHelper Classes: Base58, CompactInt, CurveFp, EcKey, Error, Point, PrivateKey, PublicKey, Signature

Constant Summary collapse

PRIVATE_KEY_PREFIX =
{
  :mainnet => 0x80,
  :testnet => 0xEF
}
NETWORK_VERSION =
{
  :mainnet => 0x00,
  :testnet => 0x6F
}
P =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F
R =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141
B =
0x0000000000000000000000000000000000000000000000000000000000000007
A =
0x0000000000000000000000000000000000000000000000000000000000000000
Gx =
0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798
Gy =
0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8
CURVE_SECP256K1 =
::BitcoinCigs::CurveFp.new(P, A, B)
GENERATOR_SECP256K1 =
::BitcoinCigs::Point.new(CURVE_SECP256K1, Gx, Gy, R)
VERSION =
"0.0.7"

Class Method Summary collapse

Methods included from CryptoHelper

decode58, decode64, decode_hex, encode58, encode64, inverse_mod, leftmost_bit, ripemd160, sha256, sqrt_mod, str_to_num

Class Method Details

.convert_wallet_format_to_bytes!(input, network) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/bitcoin_cigs.rb', line 145

def convert_wallet_format_to_bytes!(input, network)
  bytes = if is_wallet_import_format?(input, network)
    decode_wallet_import_format(input, network)
  elsif is_compressed_wallet_import_format?(input, network)
    decode_compressed_wallet_import_format(input, network)
  elsif is_mini_format?(input)
    sha256(input)
  elsif is_hex_format?(input)
    decode_hex(input)
  elsif is_base_64_format?(input)
    decode64(input)
  else
    raise ::BitcoinCigs::Error.new("Unknown Wallet Format")
  end
  
  bytes
end

.get_signature_address(signature, message, options = {:network => :mainnet}) ⇒ Object



50
51
52
53
54
55
56
# File 'lib/bitcoin_cigs.rb', line 50

def get_signature_address(signature, message, options = {:network => :mainnet})
  begin
    get_signature_address!(signature, message, options)
  rescue ::BitcoinCigs::Error
    false
  end 
end

.get_signature_address!(signature, message, options = {:network => :mainnet}) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/bitcoin_cigs.rb', line 58

def get_signature_address!(signature, message, options = {:network => :mainnet})

  message = calculate_hash(format_message_to_sign(message))

  curve = CURVE_SECP256K1
  g = GENERATOR_SECP256K1
  a, b, p = curve.a, curve.b, curve.p
  
  order = g.order
  
  sig = decode64(signature)
  raise ::BitcoinCigs::Error.new("Bad signature length") if sig.size != 65
  raise ::BitcoinCigs::Error.new("Bad characters in signature") if signature != encode64(sig)
  
  hb = sig[0].ord
  r, s = [sig[1...33], sig[33...65]].collect { |s| str_to_num(s) }
  
  
  raise ::BitcoinCigs::Error.new("Bad signature first byte") if hb < 27 || hb >= 35
  
  compressed = false
  if hb >= 31
    compressed = true
    hb -= 4
  end
  
  recid = hb - 27
  x = (r + (recid / 2) * order) % p
  y2 = ((x ** 3 % p) + a * x + b) % p
  yomy = sqrt_mod(y2, p)
  if (yomy - recid) % 2 == 0
    y = yomy
  else
    y = p - yomy
  end
  
  r_point = ::BitcoinCigs::Point.new(curve, x, y, order)
  e = str_to_num(message)
  minus_e = -e % order
  
  inv_r = inverse_mod(r, order)
  q = (r_point * s + g * minus_e) * inv_r
  

  public_key = ::BitcoinCigs::PublicKey.new(g, q, compressed)
  
  public_key_to_bc_address(public_key.ser(), NETWORK_VERSION[options[:network]])
end

.sign_message(wallet_key, message, options = {:network => :mainnet}) ⇒ Object



107
108
109
110
111
112
113
# File 'lib/bitcoin_cigs.rb', line 107

def sign_message(wallet_key, message, options = {:network => :mainnet})
  begin
    sign_message!(wallet_key, message, options)
  rescue ::BitcoinCigs::Error
    nil
  end
end

.sign_message!(wallet_key, message, options = {:network => :mainnet}) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/bitcoin_cigs.rb', line 115

def sign_message!(wallet_key, message, options = {:network => :mainnet})
  private_key = convert_wallet_format_to_bytes!(wallet_key, options[:network])
  
  msg_hash = sha256(sha256(format_message_to_sign(message)))
  
  ec_key = ::BitcoinCigs::EcKey.new(str_to_num(private_key))
  private_key = ec_key.private_key
  public_key = ec_key.public_key
  addr = public_key_to_bc_address(get_pub_key(ec_key, ec_key.public_key.compressed), NETWORK_VERSION[options[:network]])
  
  sig = private_key.sign(msg_hash, random_k)
  raise ::BitcoinCigs::Error.new("Unable to sign message") unless public_key.verify(msg_hash, sig)
  
  4.times do |i|
    hb = 27 + i
    
    sign = "#{hb.chr}#{sig.ser}"
    sign_64 = encode64(sign)
    
    begin
      verify_message!(addr, sign_64, message, options)
      return sign_64
    rescue ::BitcoinCigs::Error
      next
    end
  end
  
  raise ::BitcoinCigs::Error, "Unable to construct recoverable key"
end

.verify_message(address, signature, message, options = {:network => :mainnet}) ⇒ Object



28
29
30
31
32
33
34
35
# File 'lib/bitcoin_cigs.rb', line 28

def verify_message(address, signature, message, options = {:network => :mainnet})
  begin
    verify_message!(address, signature, message, options)
    true
  rescue ::BitcoinCigs::Error
    false
  end
end

.verify_message!(address, signature, message, options = {:network => :mainnet}) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/bitcoin_cigs.rb', line 37

def verify_message!(address, signature, message, options = {:network => :mainnet})

  decoded_address = decode58(address)
  raise ::BitcoinCigs::Error.new("Incorrect address or message for signature.") if decoded_address.nil?
  # network_version = str_to_num(decoded_address) >> (8 * 24)

  addr = get_signature_address!(signature, message, options)

  raise ::BitcoinCigs::Error.new("Incorrect address or message for signature.") if address != addr
  
  nil
end