bitcoinrb 0.2.9 → 0.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 +4 -4
- data/.ruby-version +1 -1
- data/.travis.yml +3 -2
- data/README.md +7 -6
- data/bitcoinrb.gemspec +4 -4
- data/exe/bitcoinrbd +5 -0
- data/lib/bitcoin.rb +33 -1
- data/lib/bitcoin/bip85_entropy.rb +111 -0
- data/lib/bitcoin/block_header.rb +2 -0
- data/lib/bitcoin/chain_params.rb +0 -8
- data/lib/bitcoin/chainparams/regtest.yml +1 -1
- data/lib/bitcoin/chainparams/testnet.yml +1 -1
- data/lib/bitcoin/constants.rb +3 -10
- data/lib/bitcoin/descriptor.rb +147 -0
- data/lib/bitcoin/ext.rb +5 -0
- data/lib/bitcoin/ext/json_parser.rb +46 -0
- data/lib/bitcoin/ext_key.rb +19 -4
- data/lib/bitcoin/key.rb +9 -5
- data/lib/bitcoin/key_path.rb +12 -5
- data/lib/bitcoin/message.rb +7 -0
- data/lib/bitcoin/message/base.rb +1 -0
- data/lib/bitcoin/message/cf_parser.rb +16 -0
- data/lib/bitcoin/message/cfcheckpt.rb +36 -0
- data/lib/bitcoin/message/cfheaders.rb +40 -0
- data/lib/bitcoin/message/cfilter.rb +35 -0
- data/lib/bitcoin/message/get_cfcheckpt.rb +29 -0
- data/lib/bitcoin/message/get_cfheaders.rb +24 -0
- data/lib/bitcoin/message/get_cfilters.rb +25 -0
- data/lib/bitcoin/message/network_addr.rb +31 -12
- data/lib/bitcoin/message/version.rb +14 -22
- data/lib/bitcoin/mnemonic.rb +5 -5
- data/lib/bitcoin/network/peer.rb +12 -11
- data/lib/bitcoin/network/peer_discovery.rb +3 -1
- data/lib/bitcoin/node/cli.rb +14 -10
- data/lib/bitcoin/node/spv.rb +1 -1
- data/lib/bitcoin/out_point.rb +14 -7
- data/lib/bitcoin/payment_code.rb +92 -0
- data/lib/bitcoin/psbt.rb +3 -1
- data/lib/bitcoin/psbt/input.rb +7 -16
- data/lib/bitcoin/psbt/tx.rb +18 -12
- data/lib/bitcoin/rpc/bitcoin_core_client.rb +22 -12
- data/lib/bitcoin/rpc/request_handler.rb +3 -3
- data/lib/bitcoin/script/script.rb +18 -10
- data/lib/bitcoin/script/script_interpreter.rb +3 -5
- data/lib/bitcoin/secp256k1.rb +1 -0
- data/lib/bitcoin/secp256k1/rfc6979.rb +43 -0
- data/lib/bitcoin/secp256k1/ruby.rb +4 -35
- data/lib/bitcoin/slip39.rb +93 -0
- data/lib/bitcoin/slip39/share.rb +122 -0
- data/lib/bitcoin/slip39/sss.rb +245 -0
- data/lib/bitcoin/slip39/wordlist/english.txt +1024 -0
- data/lib/bitcoin/store.rb +2 -1
- data/lib/bitcoin/store/chain_entry.rb +1 -0
- data/lib/bitcoin/store/db/level_db.rb +2 -2
- data/lib/bitcoin/store/utxo_db.rb +226 -0
- data/lib/bitcoin/tx.rb +6 -10
- data/lib/bitcoin/tx_in.rb +4 -5
- data/lib/bitcoin/util.rb +29 -1
- data/lib/bitcoin/version.rb +1 -1
- data/lib/bitcoin/wallet.rb +1 -0
- data/lib/bitcoin/wallet/account.rb +1 -0
- data/lib/bitcoin/wallet/base.rb +3 -3
- data/lib/bitcoin/wallet/db.rb +1 -1
- data/lib/bitcoin/wallet/master_key.rb +1 -0
- data/lib/bitcoin/wallet/utxo.rb +37 -0
- metadata +45 -26
@@ -0,0 +1,122 @@
|
|
1
|
+
module Bitcoin
|
2
|
+
module SLIP39
|
3
|
+
|
4
|
+
# Share of Shamir's Secret Sharing Scheme
|
5
|
+
class Share
|
6
|
+
|
7
|
+
attr_accessor :id # 15 bits, Integer
|
8
|
+
attr_accessor :iteration_exp # 5 bits, Integer
|
9
|
+
attr_accessor :group_index # 4 bits, Integer
|
10
|
+
attr_accessor :group_threshold # 4 bits, Integer
|
11
|
+
attr_accessor :group_count # 4 bits, Integer
|
12
|
+
attr_accessor :member_index # 4 bits, Integer
|
13
|
+
attr_accessor :member_threshold # 4 bits, Integer
|
14
|
+
attr_accessor :value # 8n bits, hex string.
|
15
|
+
attr_accessor :checksum # 30 bits, Integer
|
16
|
+
|
17
|
+
# Recover Share from the mnemonic words
|
18
|
+
# @param [Array{String}] words the mnemonic words
|
19
|
+
# @return [Bitcoin::SLIP39::Share] a share
|
20
|
+
def self.from_words(words)
|
21
|
+
raise ArgumentError, 'Mnemonics should be an array of strings' unless words.is_a?(Array)
|
22
|
+
indices = words.map do |word|
|
23
|
+
index = Bitcoin::SLIP39::WORDS.index(word.downcase)
|
24
|
+
raise IndexError, 'word not found in words list.' unless index
|
25
|
+
index
|
26
|
+
end
|
27
|
+
|
28
|
+
raise ArgumentError, 'Invalid mnemonic length.' if indices.size < MIN_MNEMONIC_LENGTH_WORDS
|
29
|
+
raise ArgumentError, 'Invalid mnemonic checksum.' unless verify_rs1024_checksum(indices)
|
30
|
+
|
31
|
+
padding_length = (RADIX_BITS * (indices.size - METADATA_LENGTH_WORDS)) % 16
|
32
|
+
raise ArgumentError, 'Invalid mnemonic length.' if padding_length > 8
|
33
|
+
data = indices.map{|i|i.to_s(2).rjust(10, '0')}.join
|
34
|
+
|
35
|
+
s = self.new
|
36
|
+
s.id = data[0...ID_LENGTH_BITS].to_i(2)
|
37
|
+
s.iteration_exp = data[ID_LENGTH_BITS...(ID_LENGTH_BITS + ITERATION_EXP_LENGTH_BITS)].to_i(2)
|
38
|
+
s.group_index = data[20...24].to_i(2)
|
39
|
+
s.group_threshold = data[24...28].to_i(2) + 1
|
40
|
+
s.group_count = data[28...32].to_i(2) + 1
|
41
|
+
raise ArgumentError, "Invalid mnemonic. Group threshold(#{s.group_threshold}) cannot be greater than group count(#{s.group_count})." if s.group_threshold > s.group_count
|
42
|
+
s.member_index = data[32...36].to_i(2)
|
43
|
+
s.member_threshold = data[36...40].to_i(2) + 1
|
44
|
+
value_length = data.length - 70
|
45
|
+
start_index = 40 + padding_length
|
46
|
+
end_index = start_index + value_length - padding_length
|
47
|
+
padding_value = data[40...(40 + padding_length)]
|
48
|
+
raise ArgumentError, "Invalid mnemonic. padding must only zero." unless padding_value.to_i(2) == 0
|
49
|
+
s.value = data[start_index...end_index].to_i(2).to_even_length_hex
|
50
|
+
s.checksum = data[(40 + value_length)..-1].to_i(2)
|
51
|
+
s
|
52
|
+
end
|
53
|
+
|
54
|
+
# Generate mnemonic words
|
55
|
+
# @return [Array[String]] array of mnemonic word.
|
56
|
+
def to_words
|
57
|
+
indices = build_word_indices
|
58
|
+
indices.map{|index| Bitcoin::SLIP39::WORDS[index]}
|
59
|
+
end
|
60
|
+
|
61
|
+
# Calculate checksum using current fields
|
62
|
+
# @return [Integer] checksum
|
63
|
+
def calculate_checksum
|
64
|
+
indices = build_word_indices(false)
|
65
|
+
create_rs1024_checksum(indices).map{|i|i.to_bits(10)}.join.to_i(2)
|
66
|
+
end
|
67
|
+
|
68
|
+
def self.rs1024_polymod(values)
|
69
|
+
gen = [0xe0e040, 0x1c1c080, 0x3838100, 0x7070200, 0xe0e0009, 0x1c0c2412, 0x38086c24, 0x3090fc48, 0x21b1f890, 0x3f3f120]
|
70
|
+
chk = 1
|
71
|
+
values.each do |v|
|
72
|
+
b = (chk >> 20)
|
73
|
+
chk = (chk & 0xfffff) << 10 ^ v
|
74
|
+
10.times do |i|
|
75
|
+
chk ^= (((b >> i) & 1 == 1) ? gen[i] : 0)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
chk
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
# Create word indices from this share.
|
84
|
+
# @param [Boolean] include_checksum whether include checksum when creating indices.
|
85
|
+
# @param [Array[Integer]] the array of index
|
86
|
+
def build_word_indices(include_checksum = true)
|
87
|
+
s = id.to_bits(ID_LENGTH_BITS)
|
88
|
+
s << iteration_exp.to_bits(ITERATION_EXP_LENGTH_BITS)
|
89
|
+
s << group_index.to_bits(4)
|
90
|
+
s << (group_threshold - 1).to_bits(4)
|
91
|
+
s << (group_count - 1).to_bits(4)
|
92
|
+
raise StandardError, "Group threshold(#{group_threshold}) cannot be greater than group count(#{group_count})." if group_threshold > group_count
|
93
|
+
s << member_index.to_bits(4)
|
94
|
+
s << (member_threshold - 1).to_bits(4)
|
95
|
+
value_length = value.to_i(16).bit_length
|
96
|
+
padding_length = RADIX_BITS - (value_length % RADIX_BITS)
|
97
|
+
s << value.to_i(16).to_bits(value_length + padding_length)
|
98
|
+
s << checksum.to_bits(30) if include_checksum
|
99
|
+
s.chars.each_slice(10).map{|index| index.join.to_i(2)}
|
100
|
+
end
|
101
|
+
|
102
|
+
# Verify RS1024 checksum
|
103
|
+
# @param [Array[Integer] data the array of mnemonic word index
|
104
|
+
# @return [Boolean] verify result
|
105
|
+
def self.verify_rs1024_checksum(data)
|
106
|
+
rs1024_polymod(CUSTOMIZATION_STRING + data) == 1
|
107
|
+
end
|
108
|
+
|
109
|
+
# Create RS1024 checksum
|
110
|
+
# @param [Array[Integer] data the array of mnemonic word index without checksum
|
111
|
+
# @return [Array[Integer]] the array of checksum integer
|
112
|
+
def create_rs1024_checksum(data)
|
113
|
+
values = CUSTOMIZATION_STRING + data + Array.new(CHECKSUM_LENGTH_WORDS, 0)
|
114
|
+
polymod = Bitcoin::SLIP39::Share.rs1024_polymod(values) ^ 1
|
115
|
+
CHECKSUM_LENGTH_WORDS.times.to_a.reverse.map {|i|(polymod >> (10 * i)) & 1023 }
|
116
|
+
end
|
117
|
+
|
118
|
+
private_class_method :verify_rs1024_checksum
|
119
|
+
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,245 @@
|
|
1
|
+
require 'securerandom'
|
2
|
+
|
3
|
+
module Bitcoin
|
4
|
+
module SLIP39
|
5
|
+
|
6
|
+
# Shamir's Secret Sharing
|
7
|
+
class SSS
|
8
|
+
|
9
|
+
include Bitcoin::Util
|
10
|
+
extend Bitcoin::Util
|
11
|
+
|
12
|
+
# Create SSS shares.
|
13
|
+
#
|
14
|
+
# [Usage]
|
15
|
+
# 4 groups shares.
|
16
|
+
# = two for Alice
|
17
|
+
# = one for friends(required 3 of her 5 friends) and
|
18
|
+
# = one for family members(required 2 of her 6 family)
|
19
|
+
#
|
20
|
+
# Two of these group shares are required to reconstruct the master secret.
|
21
|
+
# groups = [1, 1], [1, 1], [3, 5], [2, 6]
|
22
|
+
#
|
23
|
+
# group_shares = Bitcoin::SLIP39::SSS.setup_shares(group_threshold: 2, groups: groups, secret: 'secret with hex format', passphrase: 'xxx')
|
24
|
+
# return 4 group array of Bitcoin::SLIP39::Share
|
25
|
+
#
|
26
|
+
# Get each share word
|
27
|
+
# groups[0][1].to_words
|
28
|
+
# => ["shadow", "pistol", "academic", "always", "adequate", "wildlife", "fancy", "gross", "oasis", "cylinder", "mustang", "wrist", "rescue", "view", "short", "owner", "flip", "making", "coding", "armed"]
|
29
|
+
#
|
30
|
+
# @param [Array[Array[Integer, Integer]]] groups
|
31
|
+
# @param [Integer] group_threshold threshold number of group shares required to reconstruct the master secret.
|
32
|
+
# @param [Integer] exp Iteration exponent. default is 0.
|
33
|
+
# @param [String] secret master secret with hex format.
|
34
|
+
# @param [String] passphrase the passphrase used for encryption/decryption.
|
35
|
+
# @return [Array[Array[Bitcoin::SLIP39::Share]]] array of group shares.
|
36
|
+
def self.setup_shares(groups: [], group_threshold: nil, exp: 0, secret: nil, passphrase: '')
|
37
|
+
raise ArgumentError, 'Groups is empty.' if groups.empty?
|
38
|
+
raise ArgumentError, 'Group threshold must be greater than 0.' if group_threshold.nil? || group_threshold < 1
|
39
|
+
raise ArgumentError, 'Master secret does not specified.' unless secret
|
40
|
+
raise ArgumentError, "The length of the master secret (#{secret.htb.bytesize} bytes) must be at least #{MIN_STRENGTH_BITS / 8} bytes." if (secret.htb.bytesize * 8) < MIN_STRENGTH_BITS
|
41
|
+
raise ArgumentError, 'The length of the master secret in bytes must be an even number.' unless secret.bytesize.even?
|
42
|
+
raise ArgumentError, 'The passphrase must contain only printable ASCII characters (code points 32-126).' unless passphrase.ascii_only?
|
43
|
+
raise ArgumentError, "The requested group threshold (#{group_threshold}) must not exceed the number of groups (#{groups.length})." if group_threshold > groups.length
|
44
|
+
groups.each do |threshold, count|
|
45
|
+
raise ArgumentError, 'Group threshold must be greater than 0.' if threshold.nil? || threshold < 1
|
46
|
+
raise ArgumentError, "The requested member threshold (#{threshold}) must not exceed the number of share (#{count})." if threshold > count
|
47
|
+
raise ArgumentError, "Creating multiple member shares with member threshold 1 is not allowed. Use 1-of-1 member sharing instead." if threshold == 1 && count > 1
|
48
|
+
end
|
49
|
+
|
50
|
+
id = SecureRandom.random_number(32767) # 32767 is max number for 15 bits.
|
51
|
+
ems = encrypt(secret, passphrase, exp, id)
|
52
|
+
|
53
|
+
group_shares = split_secret(group_threshold, groups.length, ems)
|
54
|
+
|
55
|
+
shares = group_shares.map.with_index do |s, i|
|
56
|
+
group_index, group_share = s[0], s[1]
|
57
|
+
member_threshold, member_count = groups[i][0], groups[i][1]
|
58
|
+
shares = split_secret(member_threshold, member_count, group_share)
|
59
|
+
shares.map do |member_index, member_share|
|
60
|
+
share = Bitcoin::SLIP39::Share.new
|
61
|
+
share.id = id
|
62
|
+
share.iteration_exp = exp
|
63
|
+
share.group_index = group_index
|
64
|
+
share.group_threshold = group_threshold
|
65
|
+
share.group_count = groups.length
|
66
|
+
share.member_index = member_index
|
67
|
+
share.member_threshold = member_threshold
|
68
|
+
share.value = member_share
|
69
|
+
share.checksum = share.calculate_checksum
|
70
|
+
share
|
71
|
+
end
|
72
|
+
end
|
73
|
+
shares
|
74
|
+
end
|
75
|
+
|
76
|
+
# recovery master secret form shares.
|
77
|
+
#
|
78
|
+
# [Usage]
|
79
|
+
# shares: An array of shares required for recovery.
|
80
|
+
# master_secret = Bitcoin::SLIP39::SSS.recover_secret(shares, passphrase: 'xxx')
|
81
|
+
#
|
82
|
+
# @param [Array[Bitcoin::SLIP30::Share]] shares an array of shares.
|
83
|
+
# @param [String] passphrase the passphrase using decrypt master secret.
|
84
|
+
# @return [String] a master secret.
|
85
|
+
def self.recover_secret(shares, passphrase: '')
|
86
|
+
raise ArgumentError, 'share is empty.' if shares.nil? || shares.empty?
|
87
|
+
groups = {}
|
88
|
+
id = shares[0].id
|
89
|
+
exp = shares[0].iteration_exp
|
90
|
+
group_threshold = shares.first.group_threshold
|
91
|
+
group_count = shares.first.group_count
|
92
|
+
|
93
|
+
shares.each do |share|
|
94
|
+
raise ArgumentError, 'Invalid set of shares. All shares must have the same id.' unless id == share.id
|
95
|
+
raise ArgumentError, 'Invalid set of shares. All shares must have the same group threshold.' unless group_threshold == share.group_threshold
|
96
|
+
raise ArgumentError, 'Invalid set of shares. All shares must have the same group count.' unless group_count == share.group_count
|
97
|
+
raise ArgumentError, 'Invalid set of shares. All Shares must have the same iteration exponent.' unless exp == share.iteration_exp
|
98
|
+
groups[share.group_index] ||= []
|
99
|
+
groups[share.group_index] << share
|
100
|
+
end
|
101
|
+
|
102
|
+
group_shares = {}
|
103
|
+
groups.each do |group_index, shares|
|
104
|
+
member_threshold = shares.first.member_threshold
|
105
|
+
raise ArgumentError, "Wrong number of mnemonics. Threshold is #{member_threshold}, but share count is #{shares.length}" if shares.length < member_threshold
|
106
|
+
if shares.length == 1 && member_threshold == 1
|
107
|
+
group_shares[group_index] = shares.first.value
|
108
|
+
else
|
109
|
+
value_length = shares.first.value.length
|
110
|
+
x_coordinates = []
|
111
|
+
shares.each do |share|
|
112
|
+
raise ArgumentError, 'Invalid set of shares. All shares in a group must have the same member threshold.' unless member_threshold == share.member_threshold
|
113
|
+
raise ArgumentError, 'Invalid set of shares. All share values must have the same length.' unless value_length == share.value.length
|
114
|
+
x_coordinates << share.member_index
|
115
|
+
end
|
116
|
+
x_coordinates.uniq!
|
117
|
+
raise ArgumentError, 'Invalid set of shares. Share indices must be unique.' unless x_coordinates.size == shares.size
|
118
|
+
interpolate_shares = shares.map{|s|[s.member_index, s.value]}
|
119
|
+
|
120
|
+
secret = interpolate(interpolate_shares, SECRET_INDEX)
|
121
|
+
digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb
|
122
|
+
digest, random_value = digest_value[0...DIGEST_LENGTH_BYTES].bth, digest_value[DIGEST_LENGTH_BYTES..-1].bth
|
123
|
+
recover_digest = create_digest(secret, random_value)
|
124
|
+
raise ArgumentError, 'Invalid digest of the shared secret.' unless digest == recover_digest
|
125
|
+
|
126
|
+
group_shares[group_index] = secret
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
return decrypt(group_shares.values.first, passphrase, exp, id) if group_threshold == 1
|
131
|
+
|
132
|
+
raise ArgumentError, "Wrong number of mnemonics. Group threshold is #{group_threshold}, but share count is #{group_shares.length}" if group_shares.length < group_threshold
|
133
|
+
|
134
|
+
interpolate_shares = group_shares.map{|k, v|[k, v]}
|
135
|
+
secret = interpolate(interpolate_shares, SECRET_INDEX)
|
136
|
+
digest_value = interpolate(interpolate_shares, DIGEST_INDEX).htb
|
137
|
+
digest, random_value = digest_value[0...DIGEST_LENGTH_BYTES].bth, digest_value[DIGEST_LENGTH_BYTES..-1].bth
|
138
|
+
recover_digest = create_digest(secret, random_value)
|
139
|
+
raise ArgumentError, 'Invalid digest of the shared secret.' unless digest == recover_digest
|
140
|
+
|
141
|
+
decrypt(secret, passphrase, exp, id)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
# Calculate f(x) from given shamir shares.
|
147
|
+
# @param [Array[index, value]] shares the array of shamir shares.
|
148
|
+
# @param [Integer] x the x coordinate of the result.
|
149
|
+
# @return [String] f(x) value with hex format.
|
150
|
+
def self.interpolate(shares, x)
|
151
|
+
s = shares.find{|s|s[0] == x}
|
152
|
+
return s[1] if s
|
153
|
+
|
154
|
+
log_prod = shares.sum{|s|LOG_TABLE[s[0] ^ x]}
|
155
|
+
|
156
|
+
result = ('00' * shares.first[1].length).htb
|
157
|
+
shares.each do |share|
|
158
|
+
log_basis_eval = (log_prod - LOG_TABLE[share[0] ^ x] - shares.sum{|s|LOG_TABLE[share[0] ^ s[0]]}) % 255
|
159
|
+
result = share[1].htb.bytes.each.map.with_index do |v, i|
|
160
|
+
(result[i].bti ^ (v == 0 ? 0 : (EXP_TABLE[(LOG_TABLE[v] + log_basis_eval) % 255]))).itb
|
161
|
+
end.join
|
162
|
+
end
|
163
|
+
result.bth
|
164
|
+
end
|
165
|
+
|
166
|
+
# Decrypt encrypted master secret using passphrase.
|
167
|
+
# @param [String] ems an encrypted master secret with hex format.
|
168
|
+
# @param [String] passphrase the passphrase when using encrypt master secret with binary format.
|
169
|
+
# @param [Integer] exp iteration exponent
|
170
|
+
# @param [Integer] id identifier
|
171
|
+
def self.decrypt(ems, passphrase, exp, id)
|
172
|
+
l, r = ems[0...(ems.length / 2)].htb, ems[(ems.length / 2)..-1].htb
|
173
|
+
salt = get_salt(id)
|
174
|
+
e = (Bitcoin::SLIP39::BASE_ITERATION_COUNT << exp) / Bitcoin::SLIP39::ROUND_COUNT
|
175
|
+
Bitcoin::SLIP39::ROUND_COUNT.times.to_a.reverse.each do |i|
|
176
|
+
f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
|
177
|
+
l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize)
|
178
|
+
end
|
179
|
+
(r + l).bth
|
180
|
+
end
|
181
|
+
|
182
|
+
# Encrypt master secret using passphrase
|
183
|
+
# @param [String] secret master secret with hex format.
|
184
|
+
# @param [String] passphrase the passphrase when using encrypt master secret with binary format.
|
185
|
+
# @param [Integer] exp iteration exponent
|
186
|
+
# @param [Integer] id identifier
|
187
|
+
# @return [String] encrypted master secret with hex format.
|
188
|
+
def self.encrypt(secret, passphrase, exp, id)
|
189
|
+
s = secret.htb
|
190
|
+
l, r = s[0...(s.bytesize / 2)], s[(s.bytesize / 2)..-1]
|
191
|
+
salt = get_salt(id)
|
192
|
+
e = (Bitcoin::SLIP39::BASE_ITERATION_COUNT << exp) / Bitcoin::SLIP39::ROUND_COUNT
|
193
|
+
Bitcoin::SLIP39::ROUND_COUNT.times.to_a.each do |i|
|
194
|
+
f = OpenSSL::PKCS5.pbkdf2_hmac((i.itb + passphrase), salt + r, e, r.bytesize, 'sha256')
|
195
|
+
l, r = padding_zero(r, r.bytesize), padding_zero((l.bti ^ f.bti).itb, r.bytesize)
|
196
|
+
end
|
197
|
+
(r + l).bth
|
198
|
+
end
|
199
|
+
|
200
|
+
# Create digest of the shared secret.
|
201
|
+
# @param [String] secret the shared secret with hex format.
|
202
|
+
# @param [String] random value (n-4 bytes) with hex format.
|
203
|
+
# @return [String] digest value(4 bytes) with hex format.
|
204
|
+
def self.create_digest(secret, random)
|
205
|
+
h = Bitcoin.hmac_sha256(random.htb, secret.htb)
|
206
|
+
h[0...4].bth
|
207
|
+
end
|
208
|
+
|
209
|
+
# get salt using encryption/decryption form id.
|
210
|
+
# @param [Integer] id id
|
211
|
+
# @return [String] salt with binary format.
|
212
|
+
def self.get_salt(id)
|
213
|
+
(Bitcoin::SLIP39::CUSTOMIZATION_STRING.pack('c*') + id.itb)
|
214
|
+
end
|
215
|
+
|
216
|
+
# Split the share into +count+ with threshold +threshold+.
|
217
|
+
# @param [Integer] threshold the threshold.
|
218
|
+
# @param [Integer] count split count.
|
219
|
+
# @param [Integer] secret the secret to be split.
|
220
|
+
# @return [Array[Integer, String]] the array of split secret.
|
221
|
+
def self.split_secret(threshold, count, secret)
|
222
|
+
raise ArgumentError, "The requested threshold (#{threshold}) must be a positive integer." if threshold < 1
|
223
|
+
raise ArgumentError, "The requested threshold (#{threshold}) must not exceed the number of shares (#{count})." if threshold > count
|
224
|
+
raise ArgumentError, "The requested number of shares (#{count}) must not exceed #{MAX_SHARE_COUNT}." if count > MAX_SHARE_COUNT
|
225
|
+
|
226
|
+
return count.times.map{|i|[i, secret]} if threshold == 1 # if the threshold is 1, digest of the share is not used.
|
227
|
+
|
228
|
+
random_share_count = threshold - 2
|
229
|
+
|
230
|
+
shares = random_share_count.times.map{|i|[i, SecureRandom.hex(secret.htb.bytesize)]}
|
231
|
+
random_part = SecureRandom.hex(secret.htb.bytesize - DIGEST_LENGTH_BYTES)
|
232
|
+
digest = create_digest(secret, random_part)
|
233
|
+
|
234
|
+
base_shares = shares + [[DIGEST_INDEX, digest + random_part], [SECRET_INDEX, secret]]
|
235
|
+
|
236
|
+
(random_share_count...count).each { |i| shares << [i, interpolate(base_shares, i)]}
|
237
|
+
|
238
|
+
shares
|
239
|
+
end
|
240
|
+
|
241
|
+
private_class_method :split_secret, :get_salt, :interpolate, :encrypt, :decrypt, :create_digest
|
242
|
+
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,1024 @@
|
|
1
|
+
academic
|
2
|
+
acid
|
3
|
+
acne
|
4
|
+
acquire
|
5
|
+
acrobat
|
6
|
+
activity
|
7
|
+
actress
|
8
|
+
adapt
|
9
|
+
adequate
|
10
|
+
adjust
|
11
|
+
admit
|
12
|
+
adorn
|
13
|
+
adult
|
14
|
+
advance
|
15
|
+
advocate
|
16
|
+
afraid
|
17
|
+
again
|
18
|
+
agency
|
19
|
+
agree
|
20
|
+
aide
|
21
|
+
aircraft
|
22
|
+
airline
|
23
|
+
airport
|
24
|
+
ajar
|
25
|
+
alarm
|
26
|
+
album
|
27
|
+
alcohol
|
28
|
+
alien
|
29
|
+
alive
|
30
|
+
alpha
|
31
|
+
already
|
32
|
+
alto
|
33
|
+
aluminum
|
34
|
+
always
|
35
|
+
amazing
|
36
|
+
ambition
|
37
|
+
amount
|
38
|
+
amuse
|
39
|
+
analysis
|
40
|
+
anatomy
|
41
|
+
ancestor
|
42
|
+
ancient
|
43
|
+
angel
|
44
|
+
angry
|
45
|
+
animal
|
46
|
+
answer
|
47
|
+
antenna
|
48
|
+
anxiety
|
49
|
+
apart
|
50
|
+
aquatic
|
51
|
+
arcade
|
52
|
+
arena
|
53
|
+
argue
|
54
|
+
armed
|
55
|
+
artist
|
56
|
+
artwork
|
57
|
+
aspect
|
58
|
+
auction
|
59
|
+
august
|
60
|
+
aunt
|
61
|
+
average
|
62
|
+
aviation
|
63
|
+
avoid
|
64
|
+
award
|
65
|
+
away
|
66
|
+
axis
|
67
|
+
axle
|
68
|
+
beam
|
69
|
+
beard
|
70
|
+
beaver
|
71
|
+
become
|
72
|
+
bedroom
|
73
|
+
behavior
|
74
|
+
being
|
75
|
+
believe
|
76
|
+
belong
|
77
|
+
benefit
|
78
|
+
best
|
79
|
+
beyond
|
80
|
+
bike
|
81
|
+
biology
|
82
|
+
birthday
|
83
|
+
bishop
|
84
|
+
black
|
85
|
+
blanket
|
86
|
+
blessing
|
87
|
+
blimp
|
88
|
+
blind
|
89
|
+
blue
|
90
|
+
body
|
91
|
+
bolt
|
92
|
+
boring
|
93
|
+
born
|
94
|
+
both
|
95
|
+
boundary
|
96
|
+
bracelet
|
97
|
+
branch
|
98
|
+
brave
|
99
|
+
breathe
|
100
|
+
briefing
|
101
|
+
broken
|
102
|
+
brother
|
103
|
+
browser
|
104
|
+
bucket
|
105
|
+
budget
|
106
|
+
building
|
107
|
+
bulb
|
108
|
+
bulge
|
109
|
+
bumpy
|
110
|
+
bundle
|
111
|
+
burden
|
112
|
+
burning
|
113
|
+
busy
|
114
|
+
buyer
|
115
|
+
cage
|
116
|
+
calcium
|
117
|
+
camera
|
118
|
+
campus
|
119
|
+
canyon
|
120
|
+
capacity
|
121
|
+
capital
|
122
|
+
capture
|
123
|
+
carbon
|
124
|
+
cards
|
125
|
+
careful
|
126
|
+
cargo
|
127
|
+
carpet
|
128
|
+
carve
|
129
|
+
category
|
130
|
+
cause
|
131
|
+
ceiling
|
132
|
+
center
|
133
|
+
ceramic
|
134
|
+
champion
|
135
|
+
change
|
136
|
+
charity
|
137
|
+
check
|
138
|
+
chemical
|
139
|
+
chest
|
140
|
+
chew
|
141
|
+
chubby
|
142
|
+
cinema
|
143
|
+
civil
|
144
|
+
class
|
145
|
+
clay
|
146
|
+
cleanup
|
147
|
+
client
|
148
|
+
climate
|
149
|
+
clinic
|
150
|
+
clock
|
151
|
+
clogs
|
152
|
+
closet
|
153
|
+
clothes
|
154
|
+
club
|
155
|
+
cluster
|
156
|
+
coal
|
157
|
+
coastal
|
158
|
+
coding
|
159
|
+
column
|
160
|
+
company
|
161
|
+
corner
|
162
|
+
costume
|
163
|
+
counter
|
164
|
+
course
|
165
|
+
cover
|
166
|
+
cowboy
|
167
|
+
cradle
|
168
|
+
craft
|
169
|
+
crazy
|
170
|
+
credit
|
171
|
+
cricket
|
172
|
+
criminal
|
173
|
+
crisis
|
174
|
+
critical
|
175
|
+
crowd
|
176
|
+
crucial
|
177
|
+
crunch
|
178
|
+
crush
|
179
|
+
crystal
|
180
|
+
cubic
|
181
|
+
cultural
|
182
|
+
curious
|
183
|
+
curly
|
184
|
+
custody
|
185
|
+
cylinder
|
186
|
+
daisy
|
187
|
+
damage
|
188
|
+
dance
|
189
|
+
darkness
|
190
|
+
database
|
191
|
+
daughter
|
192
|
+
deadline
|
193
|
+
deal
|
194
|
+
debris
|
195
|
+
debut
|
196
|
+
decent
|
197
|
+
decision
|
198
|
+
declare
|
199
|
+
decorate
|
200
|
+
decrease
|
201
|
+
deliver
|
202
|
+
demand
|
203
|
+
density
|
204
|
+
deny
|
205
|
+
depart
|
206
|
+
depend
|
207
|
+
depict
|
208
|
+
deploy
|
209
|
+
describe
|
210
|
+
desert
|
211
|
+
desire
|
212
|
+
desktop
|
213
|
+
destroy
|
214
|
+
detailed
|
215
|
+
detect
|
216
|
+
device
|
217
|
+
devote
|
218
|
+
diagnose
|
219
|
+
dictate
|
220
|
+
diet
|
221
|
+
dilemma
|
222
|
+
diminish
|
223
|
+
dining
|
224
|
+
diploma
|
225
|
+
disaster
|
226
|
+
discuss
|
227
|
+
disease
|
228
|
+
dish
|
229
|
+
dismiss
|
230
|
+
display
|
231
|
+
distance
|
232
|
+
dive
|
233
|
+
divorce
|
234
|
+
document
|
235
|
+
domain
|
236
|
+
domestic
|
237
|
+
dominant
|
238
|
+
dough
|
239
|
+
downtown
|
240
|
+
dragon
|
241
|
+
dramatic
|
242
|
+
dream
|
243
|
+
dress
|
244
|
+
drift
|
245
|
+
drink
|
246
|
+
drove
|
247
|
+
drug
|
248
|
+
dryer
|
249
|
+
duckling
|
250
|
+
duke
|
251
|
+
duration
|
252
|
+
dwarf
|
253
|
+
dynamic
|
254
|
+
early
|
255
|
+
earth
|
256
|
+
easel
|
257
|
+
easy
|
258
|
+
echo
|
259
|
+
eclipse
|
260
|
+
ecology
|
261
|
+
edge
|
262
|
+
editor
|
263
|
+
educate
|
264
|
+
either
|
265
|
+
elbow
|
266
|
+
elder
|
267
|
+
election
|
268
|
+
elegant
|
269
|
+
element
|
270
|
+
elephant
|
271
|
+
elevator
|
272
|
+
elite
|
273
|
+
else
|
274
|
+
email
|
275
|
+
emerald
|
276
|
+
emission
|
277
|
+
emperor
|
278
|
+
emphasis
|
279
|
+
employer
|
280
|
+
empty
|
281
|
+
ending
|
282
|
+
endless
|
283
|
+
endorse
|
284
|
+
enemy
|
285
|
+
energy
|
286
|
+
enforce
|
287
|
+
engage
|
288
|
+
enjoy
|
289
|
+
enlarge
|
290
|
+
entrance
|
291
|
+
envelope
|
292
|
+
envy
|
293
|
+
epidemic
|
294
|
+
episode
|
295
|
+
equation
|
296
|
+
equip
|
297
|
+
eraser
|
298
|
+
erode
|
299
|
+
escape
|
300
|
+
estate
|
301
|
+
estimate
|
302
|
+
evaluate
|
303
|
+
evening
|
304
|
+
evidence
|
305
|
+
evil
|
306
|
+
evoke
|
307
|
+
exact
|
308
|
+
example
|
309
|
+
exceed
|
310
|
+
exchange
|
311
|
+
exclude
|
312
|
+
excuse
|
313
|
+
execute
|
314
|
+
exercise
|
315
|
+
exhaust
|
316
|
+
exotic
|
317
|
+
expand
|
318
|
+
expect
|
319
|
+
explain
|
320
|
+
express
|
321
|
+
extend
|
322
|
+
extra
|
323
|
+
eyebrow
|
324
|
+
facility
|
325
|
+
fact
|
326
|
+
failure
|
327
|
+
faint
|
328
|
+
fake
|
329
|
+
false
|
330
|
+
family
|
331
|
+
famous
|
332
|
+
fancy
|
333
|
+
fangs
|
334
|
+
fantasy
|
335
|
+
fatal
|
336
|
+
fatigue
|
337
|
+
favorite
|
338
|
+
fawn
|
339
|
+
fiber
|
340
|
+
fiction
|
341
|
+
filter
|
342
|
+
finance
|
343
|
+
findings
|
344
|
+
finger
|
345
|
+
firefly
|
346
|
+
firm
|
347
|
+
fiscal
|
348
|
+
fishing
|
349
|
+
fitness
|
350
|
+
flame
|
351
|
+
flash
|
352
|
+
flavor
|
353
|
+
flea
|
354
|
+
flexible
|
355
|
+
flip
|
356
|
+
float
|
357
|
+
floral
|
358
|
+
fluff
|
359
|
+
focus
|
360
|
+
forbid
|
361
|
+
force
|
362
|
+
forecast
|
363
|
+
forget
|
364
|
+
formal
|
365
|
+
fortune
|
366
|
+
forward
|
367
|
+
founder
|
368
|
+
fraction
|
369
|
+
fragment
|
370
|
+
frequent
|
371
|
+
freshman
|
372
|
+
friar
|
373
|
+
fridge
|
374
|
+
friendly
|
375
|
+
frost
|
376
|
+
froth
|
377
|
+
frozen
|
378
|
+
fumes
|
379
|
+
funding
|
380
|
+
furl
|
381
|
+
fused
|
382
|
+
galaxy
|
383
|
+
game
|
384
|
+
garbage
|
385
|
+
garden
|
386
|
+
garlic
|
387
|
+
gasoline
|
388
|
+
gather
|
389
|
+
general
|
390
|
+
genius
|
391
|
+
genre
|
392
|
+
genuine
|
393
|
+
geology
|
394
|
+
gesture
|
395
|
+
glad
|
396
|
+
glance
|
397
|
+
glasses
|
398
|
+
glen
|
399
|
+
glimpse
|
400
|
+
goat
|
401
|
+
golden
|
402
|
+
graduate
|
403
|
+
grant
|
404
|
+
grasp
|
405
|
+
gravity
|
406
|
+
gray
|
407
|
+
greatest
|
408
|
+
grief
|
409
|
+
grill
|
410
|
+
grin
|
411
|
+
grocery
|
412
|
+
gross
|
413
|
+
group
|
414
|
+
grownup
|
415
|
+
grumpy
|
416
|
+
guard
|
417
|
+
guest
|
418
|
+
guilt
|
419
|
+
guitar
|
420
|
+
gums
|
421
|
+
hairy
|
422
|
+
hamster
|
423
|
+
hand
|
424
|
+
hanger
|
425
|
+
harvest
|
426
|
+
have
|
427
|
+
havoc
|
428
|
+
hawk
|
429
|
+
hazard
|
430
|
+
headset
|
431
|
+
health
|
432
|
+
hearing
|
433
|
+
heat
|
434
|
+
helpful
|
435
|
+
herald
|
436
|
+
herd
|
437
|
+
hesitate
|
438
|
+
hobo
|
439
|
+
holiday
|
440
|
+
holy
|
441
|
+
home
|
442
|
+
hormone
|
443
|
+
hospital
|
444
|
+
hour
|
445
|
+
huge
|
446
|
+
human
|
447
|
+
humidity
|
448
|
+
hunting
|
449
|
+
husband
|
450
|
+
hush
|
451
|
+
husky
|
452
|
+
hybrid
|
453
|
+
idea
|
454
|
+
identify
|
455
|
+
idle
|
456
|
+
image
|
457
|
+
impact
|
458
|
+
imply
|
459
|
+
improve
|
460
|
+
impulse
|
461
|
+
include
|
462
|
+
income
|
463
|
+
increase
|
464
|
+
index
|
465
|
+
indicate
|
466
|
+
industry
|
467
|
+
infant
|
468
|
+
inform
|
469
|
+
inherit
|
470
|
+
injury
|
471
|
+
inmate
|
472
|
+
insect
|
473
|
+
inside
|
474
|
+
install
|
475
|
+
intend
|
476
|
+
intimate
|
477
|
+
invasion
|
478
|
+
involve
|
479
|
+
iris
|
480
|
+
island
|
481
|
+
isolate
|
482
|
+
item
|
483
|
+
ivory
|
484
|
+
jacket
|
485
|
+
jerky
|
486
|
+
jewelry
|
487
|
+
join
|
488
|
+
judicial
|
489
|
+
juice
|
490
|
+
jump
|
491
|
+
junction
|
492
|
+
junior
|
493
|
+
junk
|
494
|
+
jury
|
495
|
+
justice
|
496
|
+
kernel
|
497
|
+
keyboard
|
498
|
+
kidney
|
499
|
+
kind
|
500
|
+
kitchen
|
501
|
+
knife
|
502
|
+
knit
|
503
|
+
laden
|
504
|
+
ladle
|
505
|
+
ladybug
|
506
|
+
lair
|
507
|
+
lamp
|
508
|
+
language
|
509
|
+
large
|
510
|
+
laser
|
511
|
+
laundry
|
512
|
+
lawsuit
|
513
|
+
leader
|
514
|
+
leaf
|
515
|
+
learn
|
516
|
+
leaves
|
517
|
+
lecture
|
518
|
+
legal
|
519
|
+
legend
|
520
|
+
legs
|
521
|
+
lend
|
522
|
+
length
|
523
|
+
level
|
524
|
+
liberty
|
525
|
+
library
|
526
|
+
license
|
527
|
+
lift
|
528
|
+
likely
|
529
|
+
lilac
|
530
|
+
lily
|
531
|
+
lips
|
532
|
+
liquid
|
533
|
+
listen
|
534
|
+
literary
|
535
|
+
living
|
536
|
+
lizard
|
537
|
+
loan
|
538
|
+
lobe
|
539
|
+
location
|
540
|
+
losing
|
541
|
+
loud
|
542
|
+
loyalty
|
543
|
+
luck
|
544
|
+
lunar
|
545
|
+
lunch
|
546
|
+
lungs
|
547
|
+
luxury
|
548
|
+
lying
|
549
|
+
lyrics
|
550
|
+
machine
|
551
|
+
magazine
|
552
|
+
maiden
|
553
|
+
mailman
|
554
|
+
main
|
555
|
+
makeup
|
556
|
+
making
|
557
|
+
mama
|
558
|
+
manager
|
559
|
+
mandate
|
560
|
+
mansion
|
561
|
+
manual
|
562
|
+
marathon
|
563
|
+
march
|
564
|
+
market
|
565
|
+
marvel
|
566
|
+
mason
|
567
|
+
material
|
568
|
+
math
|
569
|
+
maximum
|
570
|
+
mayor
|
571
|
+
meaning
|
572
|
+
medal
|
573
|
+
medical
|
574
|
+
member
|
575
|
+
memory
|
576
|
+
mental
|
577
|
+
merchant
|
578
|
+
merit
|
579
|
+
method
|
580
|
+
metric
|
581
|
+
midst
|
582
|
+
mild
|
583
|
+
military
|
584
|
+
mineral
|
585
|
+
minister
|
586
|
+
miracle
|
587
|
+
mixed
|
588
|
+
mixture
|
589
|
+
mobile
|
590
|
+
modern
|
591
|
+
modify
|
592
|
+
moisture
|
593
|
+
moment
|
594
|
+
morning
|
595
|
+
mortgage
|
596
|
+
mother
|
597
|
+
mountain
|
598
|
+
mouse
|
599
|
+
move
|
600
|
+
much
|
601
|
+
mule
|
602
|
+
multiple
|
603
|
+
muscle
|
604
|
+
museum
|
605
|
+
music
|
606
|
+
mustang
|
607
|
+
nail
|
608
|
+
national
|
609
|
+
necklace
|
610
|
+
negative
|
611
|
+
nervous
|
612
|
+
network
|
613
|
+
news
|
614
|
+
nuclear
|
615
|
+
numb
|
616
|
+
numerous
|
617
|
+
nylon
|
618
|
+
oasis
|
619
|
+
obesity
|
620
|
+
object
|
621
|
+
observe
|
622
|
+
obtain
|
623
|
+
ocean
|
624
|
+
often
|
625
|
+
olympic
|
626
|
+
omit
|
627
|
+
oral
|
628
|
+
orange
|
629
|
+
orbit
|
630
|
+
order
|
631
|
+
ordinary
|
632
|
+
organize
|
633
|
+
ounce
|
634
|
+
oven
|
635
|
+
overall
|
636
|
+
owner
|
637
|
+
paces
|
638
|
+
pacific
|
639
|
+
package
|
640
|
+
paid
|
641
|
+
painting
|
642
|
+
pajamas
|
643
|
+
pancake
|
644
|
+
pants
|
645
|
+
papa
|
646
|
+
paper
|
647
|
+
parcel
|
648
|
+
parking
|
649
|
+
party
|
650
|
+
patent
|
651
|
+
patrol
|
652
|
+
payment
|
653
|
+
payroll
|
654
|
+
peaceful
|
655
|
+
peanut
|
656
|
+
peasant
|
657
|
+
pecan
|
658
|
+
penalty
|
659
|
+
pencil
|
660
|
+
percent
|
661
|
+
perfect
|
662
|
+
permit
|
663
|
+
petition
|
664
|
+
phantom
|
665
|
+
pharmacy
|
666
|
+
photo
|
667
|
+
phrase
|
668
|
+
physics
|
669
|
+
pickup
|
670
|
+
picture
|
671
|
+
piece
|
672
|
+
pile
|
673
|
+
pink
|
674
|
+
pipeline
|
675
|
+
pistol
|
676
|
+
pitch
|
677
|
+
plains
|
678
|
+
plan
|
679
|
+
plastic
|
680
|
+
platform
|
681
|
+
playoff
|
682
|
+
pleasure
|
683
|
+
plot
|
684
|
+
plunge
|
685
|
+
practice
|
686
|
+
prayer
|
687
|
+
preach
|
688
|
+
predator
|
689
|
+
pregnant
|
690
|
+
premium
|
691
|
+
prepare
|
692
|
+
presence
|
693
|
+
prevent
|
694
|
+
priest
|
695
|
+
primary
|
696
|
+
priority
|
697
|
+
prisoner
|
698
|
+
privacy
|
699
|
+
prize
|
700
|
+
problem
|
701
|
+
process
|
702
|
+
profile
|
703
|
+
program
|
704
|
+
promise
|
705
|
+
prospect
|
706
|
+
provide
|
707
|
+
prune
|
708
|
+
public
|
709
|
+
pulse
|
710
|
+
pumps
|
711
|
+
punish
|
712
|
+
puny
|
713
|
+
pupal
|
714
|
+
purchase
|
715
|
+
purple
|
716
|
+
python
|
717
|
+
quantity
|
718
|
+
quarter
|
719
|
+
quick
|
720
|
+
quiet
|
721
|
+
race
|
722
|
+
racism
|
723
|
+
radar
|
724
|
+
railroad
|
725
|
+
rainbow
|
726
|
+
raisin
|
727
|
+
random
|
728
|
+
ranked
|
729
|
+
rapids
|
730
|
+
raspy
|
731
|
+
reaction
|
732
|
+
realize
|
733
|
+
rebound
|
734
|
+
rebuild
|
735
|
+
recall
|
736
|
+
receiver
|
737
|
+
recover
|
738
|
+
regret
|
739
|
+
regular
|
740
|
+
reject
|
741
|
+
relate
|
742
|
+
remember
|
743
|
+
remind
|
744
|
+
remove
|
745
|
+
render
|
746
|
+
repair
|
747
|
+
repeat
|
748
|
+
replace
|
749
|
+
require
|
750
|
+
rescue
|
751
|
+
research
|
752
|
+
resident
|
753
|
+
response
|
754
|
+
result
|
755
|
+
retailer
|
756
|
+
retreat
|
757
|
+
reunion
|
758
|
+
revenue
|
759
|
+
review
|
760
|
+
reward
|
761
|
+
rhyme
|
762
|
+
rhythm
|
763
|
+
rich
|
764
|
+
rival
|
765
|
+
river
|
766
|
+
robin
|
767
|
+
rocky
|
768
|
+
romantic
|
769
|
+
romp
|
770
|
+
roster
|
771
|
+
round
|
772
|
+
royal
|
773
|
+
ruin
|
774
|
+
ruler
|
775
|
+
rumor
|
776
|
+
sack
|
777
|
+
safari
|
778
|
+
salary
|
779
|
+
salon
|
780
|
+
salt
|
781
|
+
satisfy
|
782
|
+
satoshi
|
783
|
+
saver
|
784
|
+
says
|
785
|
+
scandal
|
786
|
+
scared
|
787
|
+
scatter
|
788
|
+
scene
|
789
|
+
scholar
|
790
|
+
science
|
791
|
+
scout
|
792
|
+
scramble
|
793
|
+
screw
|
794
|
+
script
|
795
|
+
scroll
|
796
|
+
seafood
|
797
|
+
season
|
798
|
+
secret
|
799
|
+
security
|
800
|
+
segment
|
801
|
+
senior
|
802
|
+
shadow
|
803
|
+
shaft
|
804
|
+
shame
|
805
|
+
shaped
|
806
|
+
sharp
|
807
|
+
shelter
|
808
|
+
sheriff
|
809
|
+
short
|
810
|
+
should
|
811
|
+
shrimp
|
812
|
+
sidewalk
|
813
|
+
silent
|
814
|
+
silver
|
815
|
+
similar
|
816
|
+
simple
|
817
|
+
single
|
818
|
+
sister
|
819
|
+
skin
|
820
|
+
skunk
|
821
|
+
slap
|
822
|
+
slavery
|
823
|
+
sled
|
824
|
+
slice
|
825
|
+
slim
|
826
|
+
slow
|
827
|
+
slush
|
828
|
+
smart
|
829
|
+
smear
|
830
|
+
smell
|
831
|
+
smirk
|
832
|
+
smith
|
833
|
+
smoking
|
834
|
+
smug
|
835
|
+
snake
|
836
|
+
snapshot
|
837
|
+
sniff
|
838
|
+
society
|
839
|
+
software
|
840
|
+
soldier
|
841
|
+
solution
|
842
|
+
soul
|
843
|
+
source
|
844
|
+
space
|
845
|
+
spark
|
846
|
+
speak
|
847
|
+
species
|
848
|
+
spelling
|
849
|
+
spend
|
850
|
+
spew
|
851
|
+
spider
|
852
|
+
spill
|
853
|
+
spine
|
854
|
+
spirit
|
855
|
+
spit
|
856
|
+
spray
|
857
|
+
sprinkle
|
858
|
+
square
|
859
|
+
squeeze
|
860
|
+
stadium
|
861
|
+
staff
|
862
|
+
standard
|
863
|
+
starting
|
864
|
+
station
|
865
|
+
stay
|
866
|
+
steady
|
867
|
+
step
|
868
|
+
stick
|
869
|
+
stilt
|
870
|
+
story
|
871
|
+
strategy
|
872
|
+
strike
|
873
|
+
style
|
874
|
+
subject
|
875
|
+
submit
|
876
|
+
sugar
|
877
|
+
suitable
|
878
|
+
sunlight
|
879
|
+
superior
|
880
|
+
surface
|
881
|
+
surprise
|
882
|
+
survive
|
883
|
+
sweater
|
884
|
+
swimming
|
885
|
+
swing
|
886
|
+
switch
|
887
|
+
symbolic
|
888
|
+
sympathy
|
889
|
+
syndrome
|
890
|
+
system
|
891
|
+
tackle
|
892
|
+
tactics
|
893
|
+
tadpole
|
894
|
+
talent
|
895
|
+
task
|
896
|
+
taste
|
897
|
+
taught
|
898
|
+
taxi
|
899
|
+
teacher
|
900
|
+
teammate
|
901
|
+
teaspoon
|
902
|
+
temple
|
903
|
+
tenant
|
904
|
+
tendency
|
905
|
+
tension
|
906
|
+
terminal
|
907
|
+
testify
|
908
|
+
texture
|
909
|
+
thank
|
910
|
+
that
|
911
|
+
theater
|
912
|
+
theory
|
913
|
+
therapy
|
914
|
+
thorn
|
915
|
+
threaten
|
916
|
+
thumb
|
917
|
+
thunder
|
918
|
+
ticket
|
919
|
+
tidy
|
920
|
+
timber
|
921
|
+
timely
|
922
|
+
ting
|
923
|
+
tofu
|
924
|
+
together
|
925
|
+
tolerate
|
926
|
+
total
|
927
|
+
toxic
|
928
|
+
tracks
|
929
|
+
traffic
|
930
|
+
training
|
931
|
+
transfer
|
932
|
+
trash
|
933
|
+
traveler
|
934
|
+
treat
|
935
|
+
trend
|
936
|
+
trial
|
937
|
+
tricycle
|
938
|
+
trip
|
939
|
+
triumph
|
940
|
+
trouble
|
941
|
+
true
|
942
|
+
trust
|
943
|
+
twice
|
944
|
+
twin
|
945
|
+
type
|
946
|
+
typical
|
947
|
+
ugly
|
948
|
+
ultimate
|
949
|
+
umbrella
|
950
|
+
uncover
|
951
|
+
undergo
|
952
|
+
unfair
|
953
|
+
unfold
|
954
|
+
unhappy
|
955
|
+
union
|
956
|
+
universe
|
957
|
+
unkind
|
958
|
+
unknown
|
959
|
+
unusual
|
960
|
+
unwrap
|
961
|
+
upgrade
|
962
|
+
upstairs
|
963
|
+
username
|
964
|
+
usher
|
965
|
+
usual
|
966
|
+
valid
|
967
|
+
valuable
|
968
|
+
vampire
|
969
|
+
vanish
|
970
|
+
various
|
971
|
+
vegan
|
972
|
+
velvet
|
973
|
+
venture
|
974
|
+
verdict
|
975
|
+
verify
|
976
|
+
very
|
977
|
+
veteran
|
978
|
+
vexed
|
979
|
+
victim
|
980
|
+
video
|
981
|
+
view
|
982
|
+
vintage
|
983
|
+
violence
|
984
|
+
viral
|
985
|
+
visitor
|
986
|
+
visual
|
987
|
+
vitamins
|
988
|
+
vocal
|
989
|
+
voice
|
990
|
+
volume
|
991
|
+
voter
|
992
|
+
voting
|
993
|
+
walnut
|
994
|
+
warmth
|
995
|
+
warn
|
996
|
+
watch
|
997
|
+
wavy
|
998
|
+
wealthy
|
999
|
+
weapon
|
1000
|
+
webcam
|
1001
|
+
welcome
|
1002
|
+
welfare
|
1003
|
+
western
|
1004
|
+
width
|
1005
|
+
wildlife
|
1006
|
+
window
|
1007
|
+
wine
|
1008
|
+
wireless
|
1009
|
+
wisdom
|
1010
|
+
withdraw
|
1011
|
+
wits
|
1012
|
+
wolf
|
1013
|
+
woman
|
1014
|
+
work
|
1015
|
+
worthy
|
1016
|
+
wrap
|
1017
|
+
wrist
|
1018
|
+
writing
|
1019
|
+
wrote
|
1020
|
+
year
|
1021
|
+
yelp
|
1022
|
+
yield
|
1023
|
+
yoga
|
1024
|
+
zero
|