bitcoinrb 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 544382f715d87eb79e185e310c827b2349c0af75c1bd89887a653510e226cd1f
4
- data.tar.gz: c807d0965da7c06f71b248d5a543ccfff2375d702a6ab4db70698c4be245720d
3
+ metadata.gz: 900806837ee6cd6b011a50d6a58c37ea233079e8b86a594c6a5031c338f9a490
4
+ data.tar.gz: 34b437fee384d630d9b7f1a47faa8175692d9ebe21408d4720828140461b152c
5
5
  SHA512:
6
- metadata.gz: 22ea36535fb045c35d28809f87c60f4451e2d37801e7a231772068c268d6e58cdeb086456ffc649091a5a28a82815af77f6d35ef286a7a101e53fa3f97e55a9a
7
- data.tar.gz: 7ca7b637b77bdde57d2af3faf056184397b1d87fd98dd5c46933dfb40e716d5bf8ef56890691771b97d8e66dde1c9684cb1d03e9f74f99d4f8b5bbaeb6a37e2c
6
+ metadata.gz: 87bcfdb59403593475f90b49f9f67299bee3bfd8cfae7089080e95ab8e1ffddcdc21ebbb72f8ca49f1caea9d8df2ea5ca613eb97b698ddab3fb3d3c617611165
7
+ data.tar.gz: 55dcbb9ce3f10a5dd822e98605eb00fa74f3034a8852cdcd714b4dff5cd347cbdc55bffd49ae2f74cc4aeaad2bf86d01d635e7146207ccb6557f55ae0849ff0e
@@ -19,7 +19,7 @@ jobs:
19
19
  runs-on: ubuntu-latest
20
20
  strategy:
21
21
  matrix:
22
- ruby-version: ['3.0', '3.1', '3.2']
22
+ ruby-version: ['3.0', '3.1', '3.2', '3.3']
23
23
 
24
24
  steps:
25
25
  - uses: actions/checkout@v2
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-3.2.0
1
+ ruby-3.3.0
data/README.md CHANGED
@@ -105,6 +105,15 @@ Bitcoin.chain_params = :signet
105
105
 
106
106
  This parameter is described in https://github.com/chaintope/bitcoinrb/blob/master/lib/bitcoin/chainparams/signet.yml.
107
107
 
108
+ ## Test
109
+
110
+ This library can use the [libsecp256k1](https://github.com/bitcoin-core/secp256k1/) dynamic library.
111
+ Therefore, some tests require this library. In a Linux environment, `spec/lib/libsecp256k1.so` is already located,
112
+ so there is no need to do anything. If you want to test in another environment,
113
+ please set the library path in the environment variable `TEST_LIBSECP256K1_PATH`.
114
+
115
+ The libsecp256k1 library currently tested for operation with this library is `v0.4.0`.
116
+
108
117
  ## Contributing
109
118
 
110
119
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/bitcoinrb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
data/bitcoinrb.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |spec|
20
20
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
21
  spec.require_paths = ["lib"]
22
22
 
23
- spec.add_runtime_dependency 'ecdsa_ext', '~> 0.5.0'
23
+ spec.add_runtime_dependency 'ecdsa_ext', '~> 0.5.1'
24
24
  spec.add_runtime_dependency 'eventmachine'
25
25
  spec.add_runtime_dependency 'murmurhash3', '~> 0.1.7'
26
26
  spec.add_runtime_dependency 'bech32', '>= 1.3.0'
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
32
32
  spec.add_runtime_dependency 'iniparse'
33
33
  spec.add_runtime_dependency 'siphash'
34
34
  spec.add_runtime_dependency 'json_pure', '>= 2.3.1'
35
- spec.add_runtime_dependency 'bip-schnorr', '>= 0.5.0'
35
+ spec.add_runtime_dependency 'bip-schnorr', '>= 0.7.0'
36
36
  spec.add_runtime_dependency 'base32', '>= 0.3.4'
37
37
 
38
38
  # for options
@@ -0,0 +1,113 @@
1
+ module Bitcoin
2
+ module BIP324
3
+ # The BIP324 packet cipher, encapsulating its key derivation, stream cipher, and AEAD.
4
+ class Cipher
5
+ include Bitcoin::Util
6
+
7
+ HEADER = [1 << 7].pack('C')
8
+ HEADER_LEN = 1
9
+ LENGTH_LEN = 3
10
+ EXPANSION = LENGTH_LEN + HEADER_LEN + 16
11
+
12
+ attr_reader :key
13
+ attr_reader :our_pubkey
14
+
15
+ attr_accessor :session_id
16
+ attr_accessor :send_garbage_terminator
17
+ attr_accessor :recv_garbage_terminator
18
+ attr_accessor :send_l_cipher
19
+ attr_accessor :send_p_cipher
20
+ attr_accessor :recv_l_cipher
21
+ attr_accessor :recv_p_cipher
22
+
23
+ # Constructor
24
+ # @param [Bitcoin::Key] key Private key.
25
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] our_pubkey Ellswift public key for testing.
26
+ # @raise ArgumentError
27
+ def initialize(key, our_pubkey = nil)
28
+ raise ArgumentError, "key must be Bitcoin::Key" unless key.is_a?(Bitcoin::Key)
29
+ raise ArgumentError, "our_pubkey must be Bitcoin::BIP324::EllSwiftPubkey" if our_pubkey && !our_pubkey.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
30
+ @our_pubkey = our_pubkey ? our_pubkey : key.create_ell_pubkey
31
+ @key = key
32
+ end
33
+
34
+ # Setup when the other side's public key is received.
35
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] their_pubkey
36
+ # @param [Boolean] initiator Set true if we are the initiator establishing the v2 P2P connection.
37
+ # @param [Boolean] self_decrypt only for testing, and swaps encryption/decryption keys, so that encryption
38
+ # and decryption can be tested without knowing the other side's private key.
39
+ def setup(their_pubkey, initiator, self_decrypt = false)
40
+ salt = 'bitcoin_v2_shared_secret' + Bitcoin.chain_params.magic_head.htb
41
+ ecdh_secret = BIP324.v2_ecdh(key.priv_key, their_pubkey, our_pubkey, initiator).htb
42
+ terminator = hkdf_sha256(ecdh_secret, salt, 'garbage_terminators')
43
+ side = initiator != self_decrypt
44
+ if side
45
+ self.send_l_cipher = FSChaCha20.new(hkdf_sha256(ecdh_secret, salt, 'initiator_L'))
46
+ self.send_p_cipher = FSChaCha20Poly1305.new(hkdf_sha256(ecdh_secret, salt, 'initiator_P'))
47
+ self.recv_l_cipher = FSChaCha20.new(hkdf_sha256(ecdh_secret, salt, 'responder_L'))
48
+ self.recv_p_cipher = FSChaCha20Poly1305.new(hkdf_sha256(ecdh_secret, salt, 'responder_P'))
49
+ else
50
+ self.recv_l_cipher = FSChaCha20.new(hkdf_sha256(ecdh_secret, salt, 'initiator_L'))
51
+ self.recv_p_cipher = FSChaCha20Poly1305.new(hkdf_sha256(ecdh_secret, salt, 'initiator_P'))
52
+ self.send_l_cipher = FSChaCha20.new(hkdf_sha256(ecdh_secret, salt, 'responder_L'))
53
+ self.send_p_cipher = FSChaCha20Poly1305.new(hkdf_sha256(ecdh_secret, salt, 'responder_P'))
54
+ end
55
+ if initiator
56
+ self.send_garbage_terminator = terminator[0...16].bth
57
+ self.recv_garbage_terminator = terminator[16..-1].bth
58
+ else
59
+ self.recv_garbage_terminator = terminator[0...16].bth
60
+ self.send_garbage_terminator = terminator[16..-1].bth
61
+ end
62
+ self.session_id = hkdf_sha256(ecdh_secret, salt, 'session_id').bth
63
+ end
64
+
65
+ # Encrypt a packet. Only after setup.
66
+ # @param [String] contents Packet with binary format.
67
+ # @param [String] aad AAD
68
+ # @param [Boolean] ignore Whether contains ignore bit or not.
69
+ # @raise Bitcoin::BIP324::TooLargeContent
70
+ def encrypt(contents, aad: '', ignore: false)
71
+ raise Bitcoin::BIP324::TooLargeContent unless contents.bytesize <= (2**24 - 1)
72
+
73
+ # encrypt length
74
+ len = Array.new(3)
75
+ len[0] = contents.bytesize & 0xff
76
+ len[1] = (contents.bytesize >> 8) & 0xff
77
+ len[2] = (contents.bytesize >> 16) & 0xff
78
+ enc_plaintext_len = send_l_cipher.encrypt(len.pack('C*'))
79
+
80
+ # encrypt contents
81
+ header = ignore ? HEADER : "00".htb
82
+ plaintext = header + contents
83
+ aead_ciphertext = send_p_cipher.encrypt(aad, plaintext)
84
+ enc_plaintext_len + aead_ciphertext
85
+ end
86
+
87
+ # Decrypt a packet. Only after setup.
88
+ # @param [String] input Packet to be decrypt.
89
+ # @param [String] aad AAD
90
+ # @param [Boolean] ignore Whether contains ignore bit or not.
91
+ # @return [String] Plaintext
92
+ # @raise Bitcoin::BIP324::InvalidPaketLength
93
+ def decrypt(input, aad: '', ignore: false)
94
+ len = decrypt_length(input[0...Bitcoin::BIP324::Cipher::LENGTH_LEN])
95
+ raise Bitcoin::BIP324::InvalidPaketLength unless input.bytesize == len + EXPANSION
96
+ recv_p_cipher.decrypt(aad, input[Bitcoin::BIP324::Cipher::LENGTH_LEN..-1])
97
+ end
98
+
99
+ private
100
+
101
+ # Decrypt the length of a packet. Only after setup.
102
+ # @param [String] input Length packet with binary format.
103
+ # @return [Integer] length
104
+ # @raise Bitcoin::BIP324::InvalidPaketLength
105
+ def decrypt_length(input)
106
+ raise Bitcoin::BIP324::InvalidPaketLength unless input.bytesize == LENGTH_LEN
107
+ ret = recv_l_cipher.decrypt(input)
108
+ b0, b1, b2 = ret.unpack('CCC')
109
+ b0 + (b1 << 8) + (b2 << 16)
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,42 @@
1
+ module Bitcoin
2
+ module BIP324
3
+ # An ElligatorSwift-encoded public key.
4
+ class EllSwiftPubkey
5
+ include Schnorr::Util
6
+
7
+ SIZE = 64
8
+
9
+ attr_reader :key
10
+
11
+ # Constructor
12
+ # @param [String] key 64 bytes of key data.
13
+ # @raise Bitcoin::BIP324::InvalidEllSwiftKey If key is invalid.
14
+ def initialize(key)
15
+ @key = hex2bin(key)
16
+ raise Bitcoin::BIP324::InvalidEllSwiftKey, 'key must be 64 bytes.' unless @key.bytesize == SIZE
17
+ end
18
+
19
+ # Decode to public key.
20
+ # @return [Bitcoin::Key] Decoded public key.
21
+ def decode
22
+ if Bitcoin.secp_impl.is_a?(Bitcoin::Secp256k1::Native)
23
+ pubkey = Bitcoin.secp_impl.ellswift_decode(key)
24
+ Bitcoin::Key.new(pubkey: pubkey, key_type: Bitcoin::Key::TYPES[:compressed])
25
+ else
26
+ u = key[0...32].bth
27
+ t = key[32..-1].bth
28
+ x = BIP324.xswiftec(u, t)
29
+ Bitcoin::Key.new(pubkey: "03#{x}")
30
+ end
31
+ end
32
+
33
+ # Check whether same public key or not?
34
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] other
35
+ # @return [Boolean]
36
+ def ==(other)
37
+ return false unless other.is_a?(EllSwiftPubkey)
38
+ key == other.key
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,132 @@
1
+ module Bitcoin
2
+ module BIP324
3
+
4
+ module ChaCha20
5
+ module_function
6
+
7
+ INDICES = [
8
+ [0, 4, 8, 12], [1, 5, 9, 13], [2, 6, 10, 14], [3, 7, 11, 15],
9
+ [0, 5, 10, 15], [1, 6, 11, 12], [2, 7, 8, 13], [3, 4, 9, 14]
10
+ ]
11
+
12
+ CONSTANTS = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]
13
+
14
+ # Rotate the 32-bit value v left by bits bits.
15
+ # @param [Integer] v
16
+ # @param [Integer] bits
17
+ # @return [Integer]
18
+ def rotl32(v, bits)
19
+ raise Bitcoin::BIP324::Error, "v must be integer" unless v.is_a?(Integer)
20
+ raise Bitcoin::BIP324::Error, "bits must be integer" unless bits.is_a?(Integer)
21
+ ((v << bits) & 0xffffffff) | (v >> (32 - bits))
22
+ end
23
+
24
+ # Apply a ChaCha20 double round to 16-element state array +s+.
25
+ # @param [Array[Integer]] s
26
+ # @return
27
+ def double_round(s)
28
+ raise Bitcoin::BIP324::Error, "s must be Array" unless s.is_a?(Array)
29
+ INDICES.each do |a, b, c, d|
30
+ s[a] = (s[a] + s[b]) & 0xffffffff
31
+ s[d] = rotl32(s[d] ^ s[a], 16)
32
+ s[c] = (s[c] + s[d]) & 0xffffffff
33
+ s[b] = rotl32(s[b] ^ s[c], 12)
34
+ s[a] = (s[a] + s[b]) & 0xffffffff
35
+ s[d] = rotl32(s[d] ^ s[a], 8)
36
+ s[c] = (s[c] + s[d]) & 0xffffffff
37
+ s[b] = rotl32(s[b] ^ s[c], 7)
38
+ end
39
+ s
40
+ end
41
+
42
+ # Compute the 64-byte output of the ChaCha20 block function.
43
+ # @param [String] key 32-bytes key with binary format.
44
+ # @param [String] nonce 12-byte nonce with binary format.
45
+ # @param [Integer] count 32-bit integer counter.
46
+ # @return [String] 64-byte output.
47
+ def block(key, nonce, count)
48
+ raise Bitcoin::BIP324::Error, "key must be 32 byte string" if !key.is_a?(String) || key.bytesize != 32
49
+ raise Bitcoin::BIP324::Error, "nonce must be 12 byte string" if !nonce.is_a?(String) || nonce.bytesize != 12
50
+ raise Bitcoin::BIP324::Error, "count must be integer" unless count.is_a?(Integer)
51
+ # Initialize state
52
+ init = Array.new(16, 0)
53
+ 4.times {|i| init[i] = CONSTANTS[i]}
54
+ key = key.unpack("V*")
55
+ 8.times {|i| init[4 + i] = key[i]}
56
+ init[12] = count
57
+ nonce = nonce.unpack("V*")
58
+ 3.times {|i| init[13 + i] = nonce[i]}
59
+ # Perform 20 rounds
60
+ state = init.dup
61
+ 10.times do
62
+ state = double_round(state)
63
+ end
64
+ # Add initial values back into state.
65
+ 16.times do |i|
66
+ state[i] = (state[i] + init[i]) & 0xffffffff
67
+ end
68
+ state.pack("V16")
69
+ end
70
+ end
71
+
72
+ # Rekeying wrapper stream cipher around ChaCha20.
73
+ class FSChaCha20
74
+ attr_accessor :key
75
+ attr_reader :rekey_interval
76
+ attr_accessor :chunk_counter
77
+ attr_accessor :block_counter
78
+ attr_accessor :key_stream
79
+
80
+ def initialize(initial_key, rekey_interval = BIP324::REKEY_INTERVAL)
81
+ @block_counter = 0
82
+ @chunk_counter = 0
83
+ @key = initial_key
84
+ @rekey_interval = rekey_interval
85
+ @key_stream = ''
86
+ end
87
+
88
+ # Encrypt a chunk
89
+ # @param [String] chunk Chunk data with binary format.
90
+ # @return [String] Encrypted data with binary format.
91
+ def encrypt(chunk)
92
+ crypt(chunk)
93
+ end
94
+
95
+ # Decrypt a chunk
96
+ # @param [String] chunk Chunk data with binary format.
97
+ # @return [String] Decrypted data with binary format.
98
+ def decrypt(chunk)
99
+ crypt(chunk)
100
+ end
101
+
102
+ private
103
+
104
+ def key_stream_bytes(n_bytes)
105
+ while key_stream.bytesize < n_bytes
106
+ nonce = [0, (chunk_counter / REKEY_INTERVAL)].pack("VQ<")
107
+ self.key_stream << ChaCha20.block(key, nonce, block_counter)
108
+ self.block_counter += 1
109
+ end
110
+ ret = self.key_stream[0...n_bytes]
111
+ self.key_stream = self.key_stream[n_bytes..-1]
112
+ ret
113
+ end
114
+
115
+ # Encrypt or decrypt a chunk.
116
+ # @param [String] chunk Chunk data with binary format.
117
+ # @return [String]
118
+ def crypt(chunk)
119
+ ks = key_stream_bytes(chunk.bytesize)
120
+ ret = chunk.unpack("C*").zip(ks.unpack("C*")).map do |c, k|
121
+ c ^ k
122
+ end.pack("C*")
123
+ if (self.chunk_counter + 1) % rekey_interval == 0
124
+ self.key = key_stream_bytes(32)
125
+ self.block_counter = 0
126
+ end
127
+ self.chunk_counter += 1
128
+ ret
129
+ end
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,129 @@
1
+ module Bitcoin
2
+ module BIP324
3
+ # Class representing a running poly1305 computation.
4
+ class Poly1305
5
+
6
+ MODULUS = 2**130 - 5
7
+ TAG_LEN = 16
8
+
9
+ attr_reader :r
10
+ attr_reader :s
11
+ attr_accessor :acc
12
+
13
+ # Constructor
14
+ #
15
+ def initialize(key)
16
+ @r = key[0...16].reverse.bti & 0xffffffc0ffffffc0ffffffc0fffffff
17
+ @s = key[16..-1].reverse.bti
18
+ @acc = 0
19
+ end
20
+
21
+ # Add a message of any length. Input so far must be a multiple of 16 bytes.
22
+ # @param [String] msg A message with binary format.
23
+ # @return [Poly1305] self
24
+ def add(msg, length: nil, padding: false)
25
+ len = length ? length : msg.bytesize
26
+ ((len + 15) / 16).times do |i|
27
+ chunk = msg[(i * 16)...(i * 16 + [16, len - i * 16].min)]
28
+ val = chunk.reverse.bti + 256**(padding ? 16 : chunk.bytesize)
29
+ self.acc = r * (acc + val) % MODULUS
30
+ end
31
+ self
32
+ end
33
+
34
+ # Compute the poly1305 tag.
35
+ # @return Poly1305 tag wit binary format.
36
+ def tag
37
+ ECDSA::Format::IntegerOctetString.encode((acc + s) & 0xffffffffffffffffffffffffffffffff, TAG_LEN).reverse
38
+ end
39
+ end
40
+
41
+ # Forward-secure wrapper around AEADChaCha20Poly1305.
42
+ class FSChaCha20Poly1305
43
+ attr_accessor :aead
44
+ attr_reader :rekey_interval
45
+ attr_accessor :packet_counter
46
+ attr_accessor :key
47
+
48
+ def initialize(initial_key, rekey_interval = REKEY_INTERVAL)
49
+ @packet_counter = 0
50
+ @rekey_interval = rekey_interval
51
+ @key = initial_key
52
+ end
53
+
54
+ # Encrypt a +plaintext+ with a specified +aad+.
55
+ # @param [String] aad AAD
56
+ # @param [String] plaintext Data to be encrypted with binary format.
57
+ # @return [String] Ciphertext
58
+ def encrypt(aad, plaintext)
59
+ crypt(aad, plaintext, false)
60
+ end
61
+
62
+ # Decrypt a *ciphertext* with a specified +aad+.
63
+ # @param [String] aad AAD
64
+ # @param [String] ciphertext Data to be decrypted with binary format.
65
+ # @return [Array] [header, plaintext]
66
+ def decrypt(aad, ciphertext)
67
+ contents = crypt(aad, ciphertext, true)
68
+ [contents[0], contents[1..-1]]
69
+ end
70
+
71
+ private
72
+
73
+ # Encrypt or decrypt the specified (plain/cipher)text.
74
+ def crypt(aad, text, is_decrypt)
75
+ nonce = [packet_counter % rekey_interval, packet_counter / rekey_interval].pack("VQ<")
76
+ ret = if is_decrypt
77
+ chacha20_poly1305_decrypt(key, nonce, aad, text)
78
+ else
79
+ chacha20_poly1305_encrypt(key, nonce, aad, text)
80
+ end
81
+ if (packet_counter + 1) % rekey_interval == 0
82
+ rekey_nonce = "ffffffff".htb + nonce[4..-1]
83
+ newkey1 = chacha20_poly1305_encrypt(key, rekey_nonce, "", "00".htb * 32)[0...32]
84
+ newkey2 = ChaCha20.block(key, rekey_nonce, 1)[0...32]
85
+ raise Bitcoin::BIP324::Error, "newkey1 != newkey2" unless newkey1 == newkey2
86
+ self.key = newkey1
87
+ end
88
+ self.packet_counter += 1
89
+ ret
90
+ end
91
+
92
+ # Encrypt a plaintext using ChaCha20Poly1305.
93
+ def chacha20_poly1305_encrypt(key, nonce, aad, plaintext)
94
+ msg_len = plaintext.bytesize
95
+ ret = ((msg_len + 63) / 64).times.map do |i|
96
+ now = [64, msg_len - 64 * i].min
97
+ keystream = ChaCha20.block(key, nonce, i + 1)
98
+ now.times.map do |j|
99
+ plaintext[j + 64 * i].unpack1('C') ^ keystream[j].unpack1('C')
100
+ end
101
+ end
102
+ ret = ret.flatten.pack('C*')
103
+ poly1305 = Poly1305.new(ChaCha20.block(key, nonce, 0)[0...32])
104
+ poly1305.add(aad, padding: true).add(ret, padding: true)
105
+ poly1305.add([aad.bytesize, msg_len].pack("Q<Q<"))
106
+ ret + poly1305.tag
107
+ end
108
+
109
+ # Decrypt a ChaCha20Poly1305 ciphertext.
110
+ def chacha20_poly1305_decrypt(key, nonce, aad, ciphertext)
111
+ return nil if ciphertext.bytesize < 16
112
+ msg_len = ciphertext.bytesize - 16
113
+ poly1305 = Poly1305.new(ChaCha20.block(key, nonce, 0)[0...32])
114
+ poly1305.add(aad, padding: true)
115
+ poly1305.add(ciphertext, length: msg_len, padding: true)
116
+ poly1305.add([aad.bytesize, msg_len].pack("Q<Q<"))
117
+ return nil unless ciphertext[-16..-1] == poly1305.tag
118
+ ret = ((msg_len + 63) / 64).times.map do |i|
119
+ now = [64, msg_len - 64 * i].min
120
+ keystream = ChaCha20.block(key, nonce, i + 1)
121
+ now.times.map do |j|
122
+ ciphertext[j + 64 * i].unpack1('C') ^ keystream[j].unpack1('C')
123
+ end
124
+ end
125
+ ret.flatten.pack('C*')
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,144 @@
1
+ module Bitcoin
2
+ # BIP 324 module
3
+ # https://github.com/bitcoin/bips/blob/master/bip-0324.mediawiki
4
+ module BIP324
5
+
6
+ class Error < StandardError; end
7
+ class InvalidPaketLength < Error; end
8
+ class TooLargeContent < Error; end
9
+ class InvalidEllSwiftKey < Error; end
10
+
11
+ autoload :EllSwiftPubkey, 'bitcoin/bip324/ell_swift_pubkey'
12
+ autoload :Cipher, 'bitcoin/bip324/cipher'
13
+ autoload :FSChaCha20, 'bitcoin/bip324/fs_chacha20'
14
+ autoload :FSChaCha20Poly1305, 'bitcoin/bip324/fs_chacha_poly1305'
15
+
16
+ FIELD_SIZE = 2**256 - 2**32 - 977
17
+ FIELD = ECDSA::PrimeField.new(FIELD_SIZE)
18
+
19
+ REKEY_INTERVAL = 224 # packets
20
+
21
+ module_function
22
+
23
+ def sqrt(n)
24
+ candidate = FIELD.power(n, (FIELD.prime + 1) / 4)
25
+ return nil unless FIELD.square(candidate) == n
26
+ candidate
27
+ end
28
+
29
+ MINUS_3_SQRT = sqrt(FIELD.mod(-3))
30
+
31
+ # Decode field elements (u, t) to an X coordinate on the curve.
32
+ # @param [String] u u of ElligatorSwift encoding with hex format.
33
+ # @param [String] t t of ElligatorSwift encoding with hex format.
34
+ # @return [String] x coordinate with hex format.
35
+ def xswiftec(u, t)
36
+ u = FIELD.mod(u.hex)
37
+ t = FIELD.mod(t.hex)
38
+ u = 1 if u == 0
39
+ t = 1 if t == 0
40
+ t = FIELD.mod(2 * t) if FIELD.mod(FIELD.power(u, 3) + FIELD.power(t, 2) + 7) == 0
41
+ x = FIELD.mod(FIELD.mod(FIELD.power(u, 3) + 7 - FIELD.power(t, 2)) * FIELD.inverse(2 * t))
42
+ y = FIELD.mod((x + t) * FIELD.inverse(MINUS_3_SQRT * u))
43
+ x1 = FIELD.mod(u + 4 * FIELD.power(y, 2))
44
+ x2 = FIELD.mod(FIELD.mod(FIELD.mod(-x) * FIELD.inverse(y) - u) * FIELD.inverse(2))
45
+ x3 = FIELD.mod(FIELD.mod(x * FIELD.inverse(y) - u) * FIELD.inverse(2))
46
+ [x1, x2, x3].each do |x|
47
+ unless ECDSA::Group::Secp256k1.solve_for_y(x).empty?
48
+ return ECDSA::Format::IntegerOctetString.encode(x, 32).bth
49
+ end
50
+ end
51
+ raise ArgumentError, 'Decode failed.'
52
+ end
53
+
54
+ # Inverse map for ElligatorSwift. Given x and u, find t such that xswiftec(u, t) = x, or return nil.
55
+ # @param [String] x x coordinate with hex format
56
+ # @param [String] u u of ElligatorSwift encoding with hex format
57
+ # @param [Integer] c Case selects which of the up to 8 results to return.
58
+ # @return [String] Inverse of xswiftec(u, t) with hex format or nil.
59
+ def xswiftec_inv(x, u, c)
60
+ x = FIELD.mod(x.hex)
61
+ u = FIELD.mod(u.hex)
62
+ if c & 2 == 0
63
+ return nil unless ECDSA::Group::Secp256k1.solve_for_y(FIELD.mod(-x - u)).empty?
64
+ v = x
65
+ s = FIELD.mod(
66
+ -FIELD.mod(FIELD.power(u, 3) + 7) *
67
+ FIELD.inverse(FIELD.mod(FIELD.power(u, 2) + u * v + FIELD.power(v, 2))))
68
+ else
69
+ s = FIELD.mod(x - u)
70
+ return nil if s == 0
71
+ r = sqrt(FIELD.mod(-s * (4 * (FIELD.power(u, 3) + 7) + 3 * s * FIELD.power(u, 2))))
72
+ return nil if r.nil?
73
+ return nil if c & 1 == 1 && r == 0
74
+ v = FIELD.mod(FIELD.mod(-u + r * FIELD.inverse(s)) * FIELD.inverse(2))
75
+ end
76
+ w = sqrt(s)
77
+ return nil if w.nil?
78
+ result = if c & 5 == 0
79
+ FIELD.mod(-w * FIELD.mod(u * (1 - MINUS_3_SQRT) * FIELD.inverse(2) + v))
80
+ elsif c & 5 == 1
81
+ FIELD.mod(w * FIELD.mod(u * (1 + MINUS_3_SQRT) * FIELD.inverse(2) + v))
82
+ elsif c & 5 == 4
83
+ FIELD.mod(w * FIELD.mod(u * (1 - MINUS_3_SQRT) * FIELD.inverse(2) + v))
84
+ elsif c & 5 == 5
85
+ FIELD.mod(-w * FIELD.mod(u * (1 + MINUS_3_SQRT) * FIELD.inverse(2) + v))
86
+ else
87
+ return nil
88
+ end
89
+ ECDSA::Format::IntegerOctetString.encode(result, 32).bth
90
+ end
91
+
92
+ # Given a field element X on the curve, find (u, t) that encode them.
93
+ # @param [String] x coordinate with hex format.
94
+ # @return [String] ElligatorSwift public key with hex format.
95
+ def xelligatorswift(x)
96
+ loop do
97
+ u = SecureRandom.random_number(1..ECDSA::Group::Secp256k1.order).to_s(16)
98
+ c = Random.rand(0..8)
99
+ t = xswiftec_inv(x, u, c)
100
+ unless t.nil?
101
+ return (ECDSA::Format::IntegerOctetString.encode(u.hex, 32) +
102
+ ECDSA::Format::IntegerOctetString.encode(t.hex, 32)).bth
103
+ end
104
+ end
105
+ end
106
+
107
+ # Compute x coordinate of shared ECDH point between +ellswift_theirs+ and +priv_key+.
108
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] ellswift_theirs Their EllSwift public key.
109
+ # @param [String] priv_key Private key with hex format.
110
+ # @return [String] x coordinate of shared ECDH point with hex format.
111
+ # @raise ArgumentError
112
+ def ellswift_ecdh_xonly(ellswift_theirs, priv_key)
113
+ raise ArgumentError, "ellswift_theirs must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_theirs.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
114
+ d = priv_key.hex
115
+ x = ellswift_theirs.decode.to_point.x
116
+ field = BIP324::FIELD
117
+ y = BIP324.sqrt(field.mod(field.power(x, 3) + 7))
118
+ return nil unless y
119
+ point = ECDSA::Point.new(ECDSA::Group::Secp256k1, x, y) * d
120
+ ECDSA::Format::FieldElementOctetString.encode(point.x, point.group.field).bth
121
+ end
122
+
123
+ # Compute BIP324 shared secret.
124
+ # @param [String] priv_key Private key with hex format.
125
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] ellswift_theirs Their EllSwift public key.
126
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] ellswift_ours Our EllSwift public key.
127
+ # @param [Boolean] initiating Whether your initiator or not.
128
+ # @return [String] Shared secret with hex format.
129
+ # @raise ArgumentError
130
+ def v2_ecdh(priv_key, ellswift_theirs, ellswift_ours, initiating)
131
+ raise ArgumentError, "ellswift_theirs must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_theirs.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
132
+ raise ArgumentError, "ellswift_ours must be a Bitcoin::BIP324::EllSwiftPubkey" unless ellswift_ours.is_a?(Bitcoin::BIP324::EllSwiftPubkey)
133
+
134
+ if Bitcoin.secp_impl.is_a?(Bitcoin::Secp256k1::Native)
135
+ Bitcoin::Secp256k1::Native.ellswift_ecdh_xonly(ellswift_theirs, ellswift_ours, priv_key, initiating)
136
+ else
137
+ ecdh_point_x32 = ellswift_ecdh_xonly(ellswift_theirs, priv_key).htb
138
+ content = initiating ? ellswift_ours.key + ellswift_theirs.key + ecdh_point_x32 :
139
+ ellswift_theirs.key + ellswift_ours.key + ecdh_point_x32
140
+ Bitcoin.tagged_hash('bip324_ellswift_xonly_ecdh', content).bth
141
+ end
142
+ end
143
+ end
144
+ end
@@ -10,12 +10,6 @@ class ::ECDSA::Signature
10
10
  end
11
11
  end
12
12
 
13
- class ::ECDSA::Point
14
- def to_hex(compression = true)
15
- ECDSA::Format::PointOctetString.encode(self, compression: compression).bth
16
- end
17
- end
18
-
19
13
  module ::ECDSA::Format::PointOctetString
20
14
 
21
15
  class << self
data/lib/bitcoin/key.rb CHANGED
@@ -146,9 +146,9 @@ module Bitcoin
146
146
  # @return [Bitcoin::Key] Recovered public key.
147
147
  def self.recover_compact(data, signature)
148
148
  rec_id = signature.unpack1('C')
149
- rec = rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE
150
- raise ArgumentError, 'Invalid signature parameter' if rec < 0 || rec > 15
151
- rec = rec & 3
149
+ rec = (rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE) & 3
150
+ raise ArgumentError, 'Invalid signature parameter' if rec < 0 || rec > 3
151
+
152
152
  compressed = (rec_id - Bitcoin::Key::COMPACT_SIG_HEADER_BYTE) & 4 != 0
153
153
  Bitcoin.secp_impl.recover_compact(data, signature, rec, compressed)
154
154
  end
@@ -344,6 +344,18 @@ module Bitcoin
344
344
  valid_pubkey? && secp256k1_module.parse_ec_pubkey?(pubkey, allow_hybrid)
345
345
  end
346
346
 
347
+ # Create an ellswift-encoded public key for this key, with specified entropy.
348
+ # @return [Bitcoin::BIP324::EllSwiftPubkey]
349
+ # @raise ArgumentError If ent32 does not 32 bytes.
350
+ def create_ell_pubkey
351
+ raise ArgumentError, "private_key required." unless priv_key
352
+ if secp256k1_module.is_a?(Bitcoin::Secp256k1::Native)
353
+ Bitcoin::BIP324::EllSwiftPubkey.new(secp256k1_module.ellswift_create(priv_key))
354
+ else
355
+ Bitcoin::BIP324::EllSwiftPubkey.new(Bitcoin::BIP324.xelligatorswift(xonly_pubkey))
356
+ end
357
+ end
358
+
347
359
  private
348
360
 
349
361
  def self.compare_big_endian(c1, c2)
@@ -50,18 +50,22 @@ module Bitcoin
50
50
  # @param [String] message The message that was signed.
51
51
  # @return [Boolean] Verification result.
52
52
  def verify_message(address, signature, message, prefix: Bitcoin.chain_params.message_magic)
53
- validate_address!(address)
53
+ addr_script = Bitcoin::Script.parse_from_addr(address)
54
54
  begin
55
55
  sig = Base64.strict_decode64(signature)
56
56
  rescue ArgumentError
57
57
  raise ArgumentError, 'Invalid signature'
58
58
  end
59
- begin
60
- # Legacy verification
61
- pubkey = Bitcoin::Key.recover_compact(message_hash(message, prefix: prefix, legacy: true), sig)
62
- return false unless pubkey
63
- pubkey.to_p2pkh == address
64
- rescue ArgumentError
59
+ if addr_script.p2pkh?
60
+ begin
61
+ # Legacy verification
62
+ pubkey = Bitcoin::Key.recover_compact(message_hash(message, prefix: prefix, legacy: true), sig)
63
+ return false unless pubkey
64
+ pubkey.to_p2pkh == address
65
+ rescue RuntimeError
66
+ return false
67
+ end
68
+ elsif addr_script.witness_program?
65
69
  # BIP322 verification
66
70
  tx = to_sign_tx(message_hash(message, prefix: prefix, legacy: false), address)
67
71
  tx.in[0].script_witness = Bitcoin::ScriptWitness.parse_from_payload(sig)
@@ -69,6 +73,8 @@ module Bitcoin
69
73
  tx_out = Bitcoin::TxOut.new(script_pubkey: script_pubkey)
70
74
  interpreter = Bitcoin::ScriptInterpreter.new(checker: Bitcoin::TxChecker.new(tx: tx, input_index: 0, prevouts: [tx_out]))
71
75
  interpreter.verify_script(Bitcoin::Script.new, script_pubkey, tx.in[0].script_witness)
76
+ else
77
+ raise ArgumentError, "This address unsupported."
72
78
  end
73
79
  end
74
80
 
@@ -82,7 +88,6 @@ module Bitcoin
82
88
  end
83
89
 
84
90
  def validate_address!(address)
85
- raise ArgumentError, 'Invalid address' unless Bitcoin.valid_address?(address)
86
91
  script = Bitcoin::Script.parse_from_addr(address)
87
92
  raise ArgumentError, 'This address unsupported' if script.p2sh? || script.p2wsh?
88
93
  end
@@ -5,7 +5,7 @@ module Bitcoin
5
5
  module Secp256k1
6
6
 
7
7
  # binding for secp256k1 (https://github.com/bitcoin-core/secp256k1/)
8
- # commit: efad3506a8937162e8010f5839fdf3771dfcf516
8
+ # tag: v0.4.0
9
9
  # this is not included by default, to enable set shared object path to ENV['SECP256K1_LIB_PATH']
10
10
  # for linux, ENV['SECP256K1_LIB_PATH'] = '/usr/local/lib/libsecp256k1.so'
11
11
  # for mac,
@@ -50,14 +50,20 @@ module Bitcoin
50
50
  attach_function(:secp256k1_ecdsa_signature_parse_der, [:pointer, :pointer, :pointer, :size_t], :int)
51
51
  attach_function(:secp256k1_ecdsa_signature_normalize, [:pointer, :pointer, :pointer], :int)
52
52
  attach_function(:secp256k1_ecdsa_verify, [:pointer, :pointer, :pointer, :pointer], :int)
53
- attach_function(:secp256k1_schnorrsig_sign, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
54
- attach_function(:secp256k1_schnorrsig_verify, [:pointer, :pointer, :pointer, :pointer], :int)
53
+ attach_function(:secp256k1_schnorrsig_sign32, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
54
+ attach_function(:secp256k1_schnorrsig_verify, [:pointer, :pointer, :pointer, :size_t, :pointer], :int)
55
55
  attach_function(:secp256k1_keypair_create, [:pointer, :pointer, :pointer], :int)
56
56
  attach_function(:secp256k1_xonly_pubkey_parse, [:pointer, :pointer, :pointer], :int)
57
57
  attach_function(:secp256k1_ecdsa_sign_recoverable, [:pointer, :pointer, :pointer, :pointer, :pointer, :pointer], :int)
58
58
  attach_function(:secp256k1_ecdsa_recoverable_signature_serialize_compact, [:pointer, :pointer, :pointer, :pointer], :int)
59
59
  attach_function(:secp256k1_ecdsa_recover, [:pointer, :pointer, :pointer, :pointer], :int)
60
60
  attach_function(:secp256k1_ecdsa_recoverable_signature_parse_compact, [:pointer, :pointer, :pointer, :int], :int)
61
+ attach_function(:secp256k1_ellswift_decode, [:pointer, :pointer, :pointer], :int)
62
+ attach_function(:secp256k1_ellswift_create, [:pointer, :pointer, :pointer, :pointer], :int)
63
+ # Define function pointer
64
+ callback(:secp256k1_ellswift_xdh_hash_function, [:pointer, :pointer, :pointer, :pointer, :pointer], :int)
65
+ attach_variable(:secp256k1_ellswift_xdh_hash_function_bip324, :secp256k1_ellswift_xdh_hash_function)
66
+ attach_function(:secp256k1_ellswift_xdh, [:pointer, :pointer, :pointer, :pointer, :pointer, :int, :pointer, :pointer], :int)
61
67
  end
62
68
 
63
69
  def with_context(flags: (SECP256K1_CONTEXT_VERIFY | SECP256K1_CONTEXT_SIGN))
@@ -224,6 +230,56 @@ module Bitcoin
224
230
  true
225
231
  end
226
232
 
233
+ # Decode ellswift public key.
234
+ # @param [String] ell_key ElligatorSwift key with binary format.
235
+ # @return [String] Decoded public key with hex format
236
+ def ellswift_decode(ell_key)
237
+ with_context do |context|
238
+ ell64 = FFI::MemoryPointer.new(:uchar, ell_key.bytesize).put_bytes(0, ell_key)
239
+ internal = FFI::MemoryPointer.new(:uchar, 64)
240
+ result = secp256k1_ellswift_decode(context, internal, ell64)
241
+ raise ArgumentError, 'Decode failed.' unless result == 1
242
+ serialize_pubkey_internal(context, internal, true)
243
+ end
244
+ end
245
+
246
+ # Compute an ElligatorSwift public key for a secret key.
247
+ # @param [String] priv_key private key with hex format
248
+ # @return [String] ElligatorSwift public key with hex format.
249
+ def ellswift_create(priv_key)
250
+ with_context(flags: SECP256K1_CONTEXT_SIGN) do |context|
251
+ ell64 = FFI::MemoryPointer.new(:uchar, 64)
252
+ seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb)
253
+ result = secp256k1_ellswift_create(context, ell64, seckey32, nil)
254
+ raise ArgumentError, 'Failed to create ElligatorSwift public key.' unless result == 1
255
+ ell64.read_string(64).bth
256
+ end
257
+ end
258
+
259
+ # Compute X coordinate of shared ECDH point between elswift pubkey and privkey.
260
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] their_ell_pubkey Their EllSwift public key.
261
+ # @param [Bitcoin::BIP324::EllSwiftPubkey] our_ell_pubkey Our EllSwift public key.
262
+ # @param [String] priv_key private key with hex format.
263
+ # @param [Boolean] initiating Whether your initiator or not.
264
+ # @return [String] x coordinate with hex format.
265
+ def ellswift_ecdh_xonly(their_ell_pubkey, our_ell_pubkey, priv_key, initiating)
266
+ with_context(flags: SECP256K1_CONTEXT_SIGN) do |context|
267
+ output = FFI::MemoryPointer.new(:uchar, 32)
268
+ our_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, our_ell_pubkey.key)
269
+ their_ell_ptr = FFI::MemoryPointer.new(:uchar, 64).put_bytes(0, their_ell_pubkey.key)
270
+ seckey32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, priv_key.htb)
271
+ hashfp = secp256k1_ellswift_xdh_hash_function_bip324
272
+ result = secp256k1_ellswift_xdh(context, output,
273
+ initiating ? our_ell_ptr : their_ell_ptr,
274
+ initiating ? their_ell_ptr : our_ell_ptr,
275
+ seckey32,
276
+ initiating ? 0 : 1,
277
+ hashfp, nil)
278
+ raise ArgumentError, "secret was invalid or hashfp returned 0" unless result == 1
279
+ output.read_string(32).bth
280
+ end
281
+ end
282
+
227
283
  private
228
284
 
229
285
  # Calculate full public key(64 bytes) from public key(32 bytes).
@@ -280,7 +336,7 @@ module Bitcoin
280
336
  signature = FFI::MemoryPointer.new(:uchar, 64)
281
337
  msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
282
338
  aux_rand = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, aux_rand) if aux_rand
283
- raise 'Failed to generate schnorr signature.' unless secp256k1_schnorrsig_sign(context, signature, msg32, keypair, nil, aux_rand) == 1
339
+ raise 'Failed to generate schnorr signature.' unless secp256k1_schnorrsig_sign32(context, signature, msg32, keypair, aux_rand) == 1
284
340
  signature.read_string(64)
285
341
  end
286
342
  end
@@ -316,7 +372,7 @@ module Bitcoin
316
372
  xonly_pubkey = FFI::MemoryPointer.new(:uchar, pubkey.bytesize).put_bytes(0, pubkey)
317
373
  signature = FFI::MemoryPointer.new(:uchar, sig.bytesize).put_bytes(0, sig)
318
374
  msg32 = FFI::MemoryPointer.new(:uchar, 32).put_bytes(0, data)
319
- result = secp256k1_schnorrsig_verify(context, signature, msg32, xonly_pubkey)
375
+ result = secp256k1_schnorrsig_verify(context, signature, msg32, 32, xonly_pubkey)
320
376
  result == 1
321
377
  end
322
378
  end
@@ -77,13 +77,30 @@ module Bitcoin
77
77
  # @param [Boolean] compressed whether compressed public key or not.
78
78
  # @return [Bitcoin::Key] Recovered public key.
79
79
  def recover_compact(data, signature, rec, compressed)
80
+ group = Bitcoin::Secp256k1::GROUP
80
81
  r = ECDSA::Format::IntegerOctetString.decode(signature[1...33])
81
82
  s = ECDSA::Format::IntegerOctetString.decode(signature[33..-1])
82
- ECDSA.recover_public_key(Bitcoin::Secp256k1::GROUP, data, ECDSA::Signature.new(r, s)).each do |p|
83
- if p.y & 1 == rec
84
- return Bitcoin::Key.from_point(p, compressed: compressed)
85
- end
83
+ return nil if r.zero?
84
+ return nil if s.zero?
85
+
86
+ digest = ECDSA.normalize_digest(data, group.bit_length)
87
+ field = ECDSA::PrimeField.new(group.order)
88
+
89
+ unless rec & 2 == 0
90
+ r = field.mod(r + group.order)
86
91
  end
92
+
93
+ is_odd = (rec & 1 == 1)
94
+ y_coordinate = group.solve_for_y(r).find{|y| is_odd ? y.odd? : y.even?}
95
+
96
+ p = group.new_point([r, y_coordinate])
97
+
98
+ inv_r = field.inverse(r)
99
+ u1 = field.mod(inv_r * digest)
100
+ u2 = field.mod(inv_r * s)
101
+ q = p * u2 + (group.new_point(u1)).negate
102
+ return nil if q.infinity?
103
+ Bitcoin::Key.from_point(q, compressed: compressed)
87
104
  end
88
105
 
89
106
  # verify signature using public key
data/lib/bitcoin/util.rb CHANGED
@@ -131,10 +131,18 @@ module Bitcoin
131
131
  double_sha256(hex.htb).bth[0..7]
132
132
  end
133
133
 
134
- DIGEST_NAME_SHA256 = 'sha256'
135
-
136
134
  def hmac_sha256(key, data)
137
- OpenSSL::HMAC.digest(DIGEST_NAME_SHA256, key, data)
135
+ Bitcoin.hmac_sha256(key, data)
136
+ end
137
+
138
+ # Run HKDF.
139
+ # @param [String] ikm The input keying material with binary format.
140
+ # @param [String] salt The salt with binary format.
141
+ # @param [String] info The context and application specific information with binary format.
142
+ # @param [Integer] length The output length in octets.
143
+ # @return [String] The result of HKDF with binary format.
144
+ def hkdf_sha256(ikm, salt, info, length = 32)
145
+ OpenSSL::KDF.hkdf(ikm, salt: salt, info: info, length: length, hash: "SHA256")
138
146
  end
139
147
 
140
148
  # check whether +addr+ is valid address.
@@ -1,3 +1,3 @@
1
1
  module Bitcoin
2
- VERSION = "1.4.0"
2
+ VERSION = "1.5.0"
3
3
  end
data/lib/bitcoin.rb CHANGED
@@ -61,6 +61,7 @@ module Bitcoin
61
61
  autoload :SigHashGenerator, 'bitcoin/sighash_generator'
62
62
  autoload :MessageSign, 'bitcoin/message_sign'
63
63
  autoload :Taproot, 'bitcoin/taproot'
64
+ autoload :BIP324, 'bitcoin/bip324'
64
65
 
65
66
  require_relative 'bitcoin/constants'
66
67
  require_relative 'bitcoin/ext/ecdsa'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bitcoinrb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - azuchi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-12-19 00:00:00.000000000 Z
11
+ date: 2024-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ecdsa_ext
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 0.5.0
19
+ version: 0.5.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 0.5.0
26
+ version: 0.5.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: eventmachine
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -184,14 +184,14 @@ dependencies:
184
184
  requirements:
185
185
  - - ">="
186
186
  - !ruby/object:Gem::Version
187
- version: 0.5.0
187
+ version: 0.7.0
188
188
  type: :runtime
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
192
  - - ">="
193
193
  - !ruby/object:Gem::Version
194
- version: 0.5.0
194
+ version: 0.7.0
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: base32
197
197
  requirement: !ruby/object:Gem::Requirement
@@ -332,6 +332,11 @@ files:
332
332
  - exe/bitcoinrbd
333
333
  - lib/bitcoin.rb
334
334
  - lib/bitcoin/base58.rb
335
+ - lib/bitcoin/bip324.rb
336
+ - lib/bitcoin/bip324/cipher.rb
337
+ - lib/bitcoin/bip324/ell_swift_pubkey.rb
338
+ - lib/bitcoin/bip324/fs_chacha20.rb
339
+ - lib/bitcoin/bip324/fs_chacha_poly1305.rb
335
340
  - lib/bitcoin/bip85_entropy.rb
336
341
  - lib/bitcoin/bit_stream.rb
337
342
  - lib/bitcoin/block.rb
@@ -496,7 +501,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
496
501
  - !ruby/object:Gem::Version
497
502
  version: '0'
498
503
  requirements: []
499
- rubygems_version: 3.4.1
504
+ rubygems_version: 3.5.3
500
505
  signing_key:
501
506
  specification_version: 4
502
507
  summary: The implementation of Bitcoin Protocol for Ruby.