Class: Eth::OpenSsl

Inherits:
Object
  • Object
show all
Extended by:
FFI::Library
Defined in:
lib/eth/open_ssl.rb

Constant Summary collapse

NID_secp256k1 =
714
POINT_CONVERSION_COMPRESSED =
2
POINT_CONVERSION_UNCOMPRESSED =
4
VERSION_1_1_0_NUM =

OpenSSL 1.1.0 version as a numerical version value as defined in: www.openssl.org/docs/man1.1.0/man3/OpenSSL_version.html

0x10100000
OPENSSL_INIT_ENGINE_RDRAND =
0x00000200
OPENSSL_INIT_ENGINE_DYNAMIC =
0x00000400
OPENSSL_INIT_ENGINE_CRYPTODEV =
0x00001000
OPENSSL_INIT_ENGINE_CAPI =
0x00002000
OPENSSL_INIT_ENGINE_PADLOCK =
0x00004000
OPENSSL_INIT_ENGINE_ALL_BUILTIN =
(
  OPENSSL_INIT_ENGINE_RDRAND |
  OPENSSL_INIT_ENGINE_DYNAMIC |
  OPENSSL_INIT_ENGINE_CRYPTODEV |
  OPENSSL_INIT_ENGINE_CAPI |
  OPENSSL_INIT_ENGINE_PADLOCK
)
OPENSSL_INIT_LOAD_SSL_STRINGS =
0x00200000

Class Method Summary collapse

Class Method Details

.BN_num_bytes(ptr) ⇒ Object



108
109
110
# File 'lib/eth/open_ssl.rb', line 108

def BN_num_bytes(ptr)
  (BN_num_bits(ptr) + 7) / 8
end

.init_ffi_sslObject



225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/eth/open_ssl.rb', line 225

def init_ffi_ssl
  return if @ssl_loaded
  if version >= VERSION_1_1_0_NUM
    OPENSSL_init_ssl(
      OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_ENGINE_ALL_BUILTIN,
      nil
    )
  else
    SSL_library_init()
    ERR_load_crypto_strings()
    SSL_load_error_strings()
  end

  RAND_poll()
  @ssl_loaded = true
end

.recover_compact(hash, signature) ⇒ Object



207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/eth/open_ssl.rb', line 207

def recover_compact(hash, signature)
  return false if signature.bytesize != 65

  version = signature.unpack('C')[0]

  # Version of signature should be 27 or 28, but 0 and 1 are also possible versions
  # which can show up in Ledger hardwallet signings
  if version < 27
    version += 27
  end

  v_base = Eth.replayable_v?(version) ? Eth.replayable_chain_id : Eth.v_base

  return false if version < v_base

  recover_public_key_from_signature(hash, signature, (version - v_base), false)
end

.recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
# File 'lib/eth/open_ssl.rb', line 153

def recover_public_key_from_signature(message_hash, signature, rec_id, is_compressed)
  return nil if rec_id < 0 or signature.bytesize != 65
  init_ffi_ssl

  signature = FFI::MemoryPointer.from_string(signature)
  r = BN_bin2bn(signature[1], 32, BN_new())
  s = BN_bin2bn(signature[33], 32, BN_new())

  _n, i = 0, rec_id / 2
  eckey = EC_KEY_new_by_curve_name(NID_secp256k1)

  EC_KEY_set_conv_form(eckey, POINT_CONVERSION_COMPRESSED) if is_compressed

  group = EC_KEY_get0_group(eckey)
  order = BN_new()
  EC_GROUP_get_order(group, order, nil)
  x = BN_dup(order)
  BN_mul_word(x, i)
  BN_add(x, x, r)

  field = BN_new()
  EC_GROUP_get_curve_GFp(group, field, nil, nil, nil)

  if BN_cmp(x, field) >= 0
    bn_free_each r, s, order, x, field
    EC_KEY_free(eckey)
    return nil
  end

  big_r = EC_POINT_new(group)
  EC_POINT_set_compressed_coordinates_GFp(group, big_r, x, rec_id % 2, nil)

  big_q = EC_POINT_new(group)
  n = EC_GROUP_get_degree(group)
  e = BN_bin2bn(message_hash, message_hash.bytesize, BN_new())
  BN_rshift(e, e, 8 - (n & 7)) if 8 * message_hash.bytesize > n

  ctx = BN_CTX_new()
  zero, rr, sor, eor = BN_new(), BN_new(), BN_new(), BN_new()
  BN_set_word(zero, 0)
  BN_mod_sub(e, zero, e, order, ctx)
  BN_mod_inverse(rr, r, order, ctx)
  BN_mod_mul(sor, s, rr, order, ctx)
  BN_mod_mul(eor, e, rr, order, ctx)
  EC_POINT_mul(group, big_q, eor, big_r, sor, ctx)
  EC_KEY_set_public_key(eckey, big_q)
  BN_CTX_free(ctx)

  bn_free_each r, s, order, x, field, e, zero, rr, sor, eor
  [big_r, big_q].each{|j| EC_POINT_free(j) }

  recover_public_hex eckey
end

.sign_compact(hash, private_key, public_key_hex) ⇒ Object



112
113
114
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
144
145
146
147
148
149
150
151
# File 'lib/eth/open_ssl.rb', line 112

def sign_compact(hash, private_key, public_key_hex)
  private_key = [private_key].pack("H*") if private_key.bytesize >= 64
  pubkey_compressed = false

  init_ffi_ssl
  eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
  priv_key = BN_bin2bn(private_key, private_key.bytesize, BN_new())

  group, order, ctx = EC_KEY_get0_group(eckey), BN_new(), BN_CTX_new()
  EC_GROUP_get_order(group, order, ctx)

  pub_key = EC_POINT_new(group)
  EC_POINT_mul(group, pub_key, priv_key, nil, nil, ctx)
  EC_KEY_set_private_key(eckey, priv_key)
  EC_KEY_set_public_key(eckey, pub_key)

  signature = ECDSA_do_sign(hash, hash.bytesize, eckey)

  BN_free(order)
  BN_CTX_free(ctx)
  EC_POINT_free(pub_key)
  BN_free(priv_key)
  EC_KEY_free(eckey)

  buf, rec_id, head = FFI::MemoryPointer.new(:uint8, 32), nil, nil
  r, s = signature.get_array_of_pointer(0, 2).map{|i| BN_bn2bin(i, buf); buf.read_string(BN_num_bytes(i)).rjust(32, "\x00") }

  if signature.get_array_of_pointer(0, 2).all?{|i| BN_num_bits(i) <= 256 }
    4.times{|i|
      head = [ Eth.v_base + i ].pack("C")
      if public_key_hex == recover_public_key_from_signature(hash, [head, r, s].join, i, pubkey_compressed)
        rec_id = i; break
      end
    }
  end

  ECDSA_SIG_free(signature)

  [ head, [r,s] ].join if rec_id
end

.versionInteger

Returns the version of SSL present.

Returns:

  • (Integer)

    version number as an integer.



56
57
58
59
60
61
62
# File 'lib/eth/open_ssl.rb', line 56

def self.version
  if self.respond_to?(:OpenSSL_version_num)
    OpenSSL_version_num()
  else
    SSLeay()
  end
end