Class: Bitcoin::Wallet::MasterKey
- Inherits:
-
Object
- Object
- Bitcoin::Wallet::MasterKey
- Extended by:
- Util
- Includes:
- HexConverter, KeyPath, Util
- Defined in:
- lib/bitcoin/wallet/master_key.rb
Overview
HD Wallet master seed
Constant Summary collapse
- NOT_ENCRYPTED =
The seed is not encrypted.
0- ENCRYPTED_V1 =
AES-256-CBC with a key derived by PBKDF2-HMAC-SHA1. Only supported to load a wallet which was already encrypted, #encrypt always writes ENCRYPTED_V2.
1- ENCRYPTED_V2 =
AES-256-GCM with a key derived by PBKDF2-HMAC-SHA512.
2- ENCRYPTION_VERSIONS =
[ENCRYPTED_V1, ENCRYPTED_V2]
- V1_KDF_ROUNDS =
2000- KDF_ROUNDS =
Rounds for PBKDF2-HMAC-SHA512, which take roughly 100 ms.
210_000- SALT_SIZE =
16- GCM_IV_SIZE =
12- GCM_TAG_SIZE =
16
Instance Attribute Summary collapse
-
#encrypted ⇒ Object
Returns the value of attribute encrypted.
-
#encryption_version ⇒ Object
readonly
The encryption version of
seed, nil unless encrypted. -
#mnemonic ⇒ Object
ephemeral data existing only at initialization.
-
#salt ⇒ Object
Returns the value of attribute salt.
-
#seed ⇒ Object
readonly
Returns the value of attribute seed.
Class Method Summary collapse
-
.generate ⇒ Object
generate new master key.
-
.parse_from_payload(payload) ⇒ Bitcoin::Wallet::MasterKey
parse master key raw data.
-
.recover_from_words(words) ⇒ Object
recover master key from mnemonic word list.
Instance Method Summary collapse
-
#decrypt(passphrase) ⇒ Object
Decrypt the seed with
passphrase. -
#derive(path) ⇒ Bitcoin::ExtKey
derive child key using derivation path.
-
#encrypt(passphrase) ⇒ Object
Encrypt the seed with
passphrase. -
#initialize(seed, salt: '', encrypted: false, mnemonic: nil, encryption_version: nil) ⇒ MasterKey
constructor
A new instance of MasterKey.
-
#key ⇒ Bitcoin::ExtKey
get master key.
-
#to_payload ⇒ Object
generate payload with following format [encryption version(not encrypted:0, v1:1, v2:2)][salt(var str)][seed(var str)].
Methods included from Util
byte_to_bit, calc_checksum, decode_base58_address, double_sha256, encode_base58_address, hash160, hkdf_sha256, hmac_sha256, pack_boolean, pack_var_int, pack_var_string, padding_zero, sha256, tagged_hash, unpack_boolean, unpack_var_int, unpack_var_int_from_io, unpack_var_string, valid_address?
Methods included from KeyPath
Methods included from HexConverter
Constructor Details
#initialize(seed, salt: '', encrypted: false, mnemonic: nil, encryption_version: nil) ⇒ MasterKey
Returns a new instance of MasterKey.
36 37 38 39 40 41 42 |
# File 'lib/bitcoin/wallet/master_key.rb', line 36 def initialize(seed, salt: '', encrypted: false, mnemonic: nil, encryption_version: nil) @mnemonic = mnemonic @seed = seed @encrypted = encrypted @encryption_version = encrypted ? (encryption_version || ENCRYPTED_V2) : nil @salt = salt end |
Instance Attribute Details
#encrypted ⇒ Object
Returns the value of attribute encrypted.
31 32 33 |
# File 'lib/bitcoin/wallet/master_key.rb', line 31 def encrypted @encrypted end |
#encryption_version ⇒ Object (readonly)
The encryption version of seed, nil unless encrypted.
33 34 35 |
# File 'lib/bitcoin/wallet/master_key.rb', line 33 def encryption_version @encryption_version end |
#mnemonic ⇒ Object
ephemeral data existing only at initialization
34 35 36 |
# File 'lib/bitcoin/wallet/master_key.rb', line 34 def mnemonic @mnemonic end |
#salt ⇒ Object
Returns the value of attribute salt.
30 31 32 |
# File 'lib/bitcoin/wallet/master_key.rb', line 30 def salt @salt end |
#seed ⇒ Object (readonly)
Returns the value of attribute seed.
29 30 31 |
# File 'lib/bitcoin/wallet/master_key.rb', line 29 def seed @seed end |
Class Method Details
.generate ⇒ Object
generate new master key.
46 47 48 49 50 |
# File 'lib/bitcoin/wallet/master_key.rb', line 46 def self.generate entropy = SecureRandom.hex(32) mnemonic = Bitcoin::Mnemonic.new('english') self.recover_from_words(mnemonic.to_mnemonic(entropy)) end |
.parse_from_payload(payload) ⇒ Bitcoin::Wallet::MasterKey
parse master key raw data
64 65 66 67 68 69 70 71 72 |
# File 'lib/bitcoin/wallet/master_key.rb', line 64 def self.parse_from_payload(payload) flag, payload = unpack_var_int(payload) raise 'encrypted flag is invalid.' unless [NOT_ENCRYPTED, *ENCRYPTION_VERSIONS].include?(flag) salt, payload = unpack_var_string(payload) salt = '' unless salt seed, payload = unpack_var_string(payload) self.new(seed.bth, salt: salt.bth, encrypted: flag != NOT_ENCRYPTED, encryption_version: flag) end |
Instance Method Details
#decrypt(passphrase) ⇒ Object
Decrypt the seed with passphrase.
117 118 119 120 121 122 123 |
# File 'lib/bitcoin/wallet/master_key.rb', line 117 def decrypt(passphrase) raise 'The wallet is not encrypted.' unless encrypted @seed = encryption_version == ENCRYPTED_V1 ? decrypt_v1(passphrase) : decrypt_v2(passphrase) @encrypted = false @encryption_version = nil @salt = '' end |
#derive(path) ⇒ Bitcoin::ExtKey
derive child key using derivation path.
90 91 92 93 94 |
# File 'lib/bitcoin/wallet/master_key.rb', line 90 def derive(path) derived_key = key parse_key_path(path).each{|num| derived_key = derived_key.derive(num)} derived_key end |
#encrypt(passphrase) ⇒ Object
Encrypt the seed with passphrase.
The stored seed is [IV(12 bytes)][auth tag(16 bytes)][ciphertext].
99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/bitcoin/wallet/master_key.rb', line 99 def encrypt(passphrase) raise 'The wallet is already encrypted.' if encrypted salt = SecureRandom.bytes(SALT_SIZE) iv = SecureRandom.bytes(GCM_IV_SIZE) enc = OpenSSL::Cipher.new('AES-256-GCM') enc.encrypt enc.key = derive_key(passphrase, salt, enc.key_len) enc.iv = iv encrypted_data = enc.update(seed) << enc.final @salt = salt.bth @seed = (iv + enc.auth_tag(GCM_TAG_SIZE) + encrypted_data).bth @encryption_version = ENCRYPTED_V2 @encrypted = true end |
#key ⇒ Bitcoin::ExtKey
get master key
83 84 85 86 |
# File 'lib/bitcoin/wallet/master_key.rb', line 83 def key raise 'seed is encrypted. please decrypt the seed.' if encrypted Bitcoin::ExtKey.generate_master(seed) end |
#to_payload ⇒ Object
generate payload with following format [encryption version(not encrypted:0, v1:1, v2:2)][salt(var str)][seed(var str)]
76 77 78 79 |
# File 'lib/bitcoin/wallet/master_key.rb', line 76 def to_payload flg = encrypted ? encryption_version : NOT_ENCRYPTED pack_var_int(flg) << [salt, seed].map{|v|pack_var_string(v.htb)}.join end |