bitcoinrb 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.