lib/bitcoin/ffi/openssl.rb in bitcoin-ruby-0.0.19 vs lib/bitcoin/ffi/openssl.rb in bitcoin-ruby-0.0.20
- old
+ new
@@ -8,29 +8,80 @@
module OpenSSL_EC # rubocop:disable Naming/ClassAndModuleCamelCase
extend FFI::Library
if FFI::Platform.windows?
ffi_lib 'libeay32', 'ssleay32'
else
- ffi_lib ['libssl.so.1.0.0', 'ssl']
+ ffi_lib [
+ 'libssl.so.1.1.0', 'libssl.so.1.1',
+ 'libssl.so.1.0.0', 'libssl.so.10',
+ 'ssl'
+ ]
end
NID_secp256k1 = 714 # rubocop:disable Naming/ConstantName
POINT_CONVERSION_COMPRESSED = 2
POINT_CONVERSION_UNCOMPRESSED = 4
- attach_function :SSL_library_init, [], :int
- attach_function :ERR_load_crypto_strings, [], :void
- attach_function :SSL_load_error_strings, [], :void
+ # OpenSSL 1.1.0 version as a numerical version value as defined in:
+ # https://www.openssl.org/docs/man1.1.0/man3/OpenSSL_version.html
+ VERSION_1_1_0_NUM = 0x10100000
+
+ # OpenSSL 1.1.0 engine constants, taken from:
+ # https://github.com/openssl/openssl/blob/2be8c56a39b0ec2ec5af6ceaf729df154d784a43/include/openssl/crypto.h
+ 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 1.1.0 load strings constant, taken from:
+ # https://github.com/openssl/openssl/blob/c162c126be342b8cd97996346598ecf7db56130f/include/openssl/ssl.h
+ OPENSSL_INIT_LOAD_SSL_STRINGS = 0x00200000
+
+ # This is the very first function we need to use to determine what version
+ # of OpenSSL we are interacting with.
+ begin
+ attach_function :OpenSSL_version_num, [], :ulong
+ rescue FFI::NotFoundError
+ attach_function :SSLeay, [], :long
+ end
+
+ # Returns the version of SSL present.
+ #
+ # @return [Integer] version number as an integer.
+ def self.version
+ if self.respond_to?(:OpenSSL_version_num)
+ OpenSSL_version_num()
+ else
+ SSLeay()
+ end
+ end
+
+ if version >= VERSION_1_1_0_NUM
+ # Initialization procedure for the library was changed in OpenSSL 1.1.0
+ attach_function :OPENSSL_init_ssl, [:uint64, :pointer], :int
+ else
+ attach_function :SSL_library_init, [], :int
+ attach_function :ERR_load_crypto_strings, [], :void
+ attach_function :SSL_load_error_strings, [], :void
+ end
+
attach_function :RAND_poll, [], :int
attach_function :BN_CTX_free, [:pointer], :int
attach_function :BN_CTX_new, [], :pointer
attach_function :BN_add, %i[pointer pointer pointer], :int
attach_function :BN_bin2bn, %i[pointer int pointer], :pointer
attach_function :BN_bn2bin, %i[pointer pointer], :int
attach_function :BN_cmp, %i[pointer pointer], :int
- attach_function :BN_copy, %i[pointer pointer], :pointer
attach_function :BN_dup, [:pointer], :pointer
attach_function :BN_free, [:pointer], :int
attach_function :BN_mod_inverse, %i[pointer pointer pointer pointer], :pointer
attach_function :BN_mod_mul, %i[pointer pointer pointer pointer pointer], :int
attach_function :BN_mod_sub, %i[pointer pointer pointer pointer pointer], :int
@@ -49,26 +100,21 @@
attach_function :EC_KEY_new_by_curve_name, [:int], :pointer
attach_function :EC_KEY_set_conv_form, %i[pointer int], :void
attach_function :EC_KEY_set_private_key, %i[pointer pointer], :int
attach_function :EC_KEY_set_public_key, %i[pointer pointer], :int
attach_function :EC_POINT_free, [:pointer], :int
- attach_function :EC_POINT_is_at_infinity, %i[pointer pointer], :int
attach_function :EC_POINT_mul, %i[pointer pointer pointer pointer pointer pointer], :int
attach_function :EC_POINT_new, [:pointer], :pointer
attach_function :EC_POINT_set_compressed_coordinates_GFp,
%i[pointer pointer pointer int pointer], :int
- attach_function :d2i_ECPrivateKey, %i[pointer pointer long], :pointer
- attach_function :i2d_ECPrivateKey, %i[pointer pointer], :int
attach_function :i2o_ECPublicKey, %i[pointer pointer], :uint
- attach_function :EC_KEY_check_key, [:pointer], :uint
attach_function :ECDSA_do_sign, %i[pointer uint pointer], :pointer
attach_function :BN_num_bits, [:pointer], :int
attach_function :ECDSA_SIG_free, [:pointer], :void
attach_function :EC_POINT_add, %i[pointer pointer pointer pointer pointer], :int
attach_function :EC_POINT_point2hex, %i[pointer pointer int pointer], :string
attach_function :EC_POINT_hex2point, %i[pointer string pointer pointer], :pointer
- attach_function :ECDSA_SIG_new, [], :pointer
attach_function :d2i_ECDSA_SIG, %i[pointer pointer long], :pointer
attach_function :i2d_ECDSA_SIG, %i[pointer pointer], :int
attach_function :OPENSSL_free, :CRYPTO_free, [:pointer], :void
def self.BN_num_bytes(ptr) # rubocop:disable Naming/MethodName
@@ -80,74 +126,23 @@
# keypair = Bitcoin.generate_key; Bitcoin::OpenSSL_EC.regenerate_key(keypair.first) == keypair
def self.regenerate_key(private_key)
private_key = [private_key].pack('H*') if private_key.bytesize >= (32 * 2)
private_key_hex = private_key.unpack('H*')[0]
- # private_key = FFI::MemoryPointer.new(:uint8, private_key.bytesize)
- # .put_bytes(0, private_key, 0, private_key.bytesize)
- private_key = FFI::MemoryPointer.from_string(private_key)
+ group = OpenSSL::PKey::EC::Group.new('secp256k1')
+ key = OpenSSL::PKey::EC.new(group)
+ key.private_key = OpenSSL::BN.new(private_key_hex, 16)
+ key.public_key = group.generator.mul(key.private_key)
- init_ffi_ssl
- eckey = EC_KEY_new_by_curve_name(NID_secp256k1)
- # priv_key = BN_bin2bn(private_key, private_key.size, BN_new())
- priv_key = BN_bin2bn(private_key, private_key.size - 1, BN_new())
-
- group = EC_KEY_get0_group(eckey)
- order = BN_new()
- ctx = 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)
-
- BN_free(order)
- BN_CTX_free(ctx)
- EC_POINT_free(pub_key)
- BN_free(priv_key)
-
- length = i2d_ECPrivateKey(eckey, nil)
- buf = FFI::MemoryPointer.new(:uint8, length)
- ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
- priv_hex = if i2d_ECPrivateKey(eckey, ptr) == length
- size = buf.get_array_of_uint8(8, 1)[0]
- buf.get_array_of_uint8(9, size).pack('C*').rjust(32, "\x00").unpack('H*')[0]
- # der_to_private_key( ptr.read_pointer.read_string(length).unpack("H*")[0] )
- end
-
+ priv_hex = key.private_key.to_bn.to_s(16).downcase.rjust(64, '0')
if priv_hex != private_key_hex
raise 'regenerated wrong private_key, raise here before generating a faulty public_key too!'
end
- length = i2o_ECPublicKey(eckey, nil)
- buf = FFI::MemoryPointer.new(:uint8, length)
- ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
- pub_hex = buf.read_string(length).unpack('H*')[0] if i2o_ECPublicKey(eckey, ptr) == length
-
- EC_KEY_free(eckey)
-
- [priv_hex, pub_hex]
+ [priv_hex, key.public_key.to_bn.to_s(16).downcase]
end
- # extract private key from uncompressed DER format
- def self.der_to_private_key(der_hex)
- init_ffi_ssl
- # k = EC_KEY_new_by_curve_name(NID_secp256k1)
- # kp = FFI::MemoryPointer.new(:pointer).put_pointer(0, eckey)
-
- buf = FFI::MemoryPointer.from_string([der_hex].pack('H*'))
- ptr = FFI::MemoryPointer.new(:pointer).put_pointer(0, buf)
-
- # ec_key = d2i_ECPrivateKey(kp, ptr, buf.size-1)
- ec_key = d2i_ECPrivateKey(nil, ptr, buf.size - 1)
- return nil if ec_key.null?
- bn = EC_KEY_get0_private_key(ec_key)
- BN_bn2bin(bn, buf)
- buf.read_string(32).unpack('H*')[0]
- end
-
# Given the components of a signature and a selector value, recover and
# return the public key that generated the signature according to the
# algorithm in SEC1v2 section 4.1.6.
#
# rec_id is an index from 0 to 3 that indicates which of the 4 possible
@@ -393,12 +388,21 @@
end
def self.init_ffi_ssl
@ssl_loaded ||= false
return if @ssl_loaded
- SSL_library_init()
- ERR_load_crypto_strings()
- SSL_load_error_strings()
+
+ 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
end
end