skeleton_key 0.1.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.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +542 -0
  3. data/bin/console +8 -0
  4. data/bin/lint +10 -0
  5. data/bin/setup +21 -0
  6. data/lib/skeleton_key/chains/bitcoin/account.rb +101 -0
  7. data/lib/skeleton_key/chains/bitcoin/account_derivation.rb +127 -0
  8. data/lib/skeleton_key/chains/bitcoin/support/outputs.rb +77 -0
  9. data/lib/skeleton_key/chains/bitcoin/support/paths.rb +34 -0
  10. data/lib/skeleton_key/chains/bitcoin/support/versioning.rb +87 -0
  11. data/lib/skeleton_key/chains/bitcoin/support.rb +48 -0
  12. data/lib/skeleton_key/chains/ethereum/account.rb +191 -0
  13. data/lib/skeleton_key/chains/ethereum/support.rb +143 -0
  14. data/lib/skeleton_key/chains/solana/account.rb +117 -0
  15. data/lib/skeleton_key/chains/solana/support.rb +27 -0
  16. data/lib/skeleton_key/codecs/base58.rb +64 -0
  17. data/lib/skeleton_key/codecs/base58_check.rb +42 -0
  18. data/lib/skeleton_key/codecs/bech32.rb +182 -0
  19. data/lib/skeleton_key/constants.rb +68 -0
  20. data/lib/skeleton_key/core/entropy.rb +37 -0
  21. data/lib/skeleton_key/derivation/bip32.rb +182 -0
  22. data/lib/skeleton_key/derivation/path.rb +112 -0
  23. data/lib/skeleton_key/derivation/slip10.rb +89 -0
  24. data/lib/skeleton_key/errors.rb +158 -0
  25. data/lib/skeleton_key/keyring.rb +63 -0
  26. data/lib/skeleton_key/recovery/bip39.rb +212 -0
  27. data/lib/skeleton_key/recovery/bip39_english.txt +2048 -0
  28. data/lib/skeleton_key/recovery/slip39.rb +220 -0
  29. data/lib/skeleton_key/recovery/slip39_support/bit_packing.rb +37 -0
  30. data/lib/skeleton_key/recovery/slip39_support/checksum.rb +53 -0
  31. data/lib/skeleton_key/recovery/slip39_support/cipher.rb +81 -0
  32. data/lib/skeleton_key/recovery/slip39_support/decoder.rb +109 -0
  33. data/lib/skeleton_key/recovery/slip39_support/encoder.rb +48 -0
  34. data/lib/skeleton_key/recovery/slip39_support/generated_set.rb +39 -0
  35. data/lib/skeleton_key/recovery/slip39_support/generator.rb +156 -0
  36. data/lib/skeleton_key/recovery/slip39_support/interpolation.rb +71 -0
  37. data/lib/skeleton_key/recovery/slip39_support/protocol.rb +34 -0
  38. data/lib/skeleton_key/recovery/slip39_support/secret_recovery.rb +74 -0
  39. data/lib/skeleton_key/recovery/slip39_support/share.rb +50 -0
  40. data/lib/skeleton_key/recovery/slip39_wordlist.txt +1024 -0
  41. data/lib/skeleton_key/seed.rb +127 -0
  42. data/lib/skeleton_key/skeleton_key.code-workspace +11 -0
  43. data/lib/skeleton_key/utils/encoding.rb +134 -0
  44. data/lib/skeleton_key/utils/hashing.rb +238 -0
  45. data/lib/skeleton_key/version.rb +8 -0
  46. data/lib/skeleton_key.rb +66 -0
  47. metadata +107 -0
@@ -0,0 +1,127 @@
1
+ require "securerandom"
2
+
3
+ module SkeletonKey
4
+ ##
5
+ # Canonical seed container shared by all chain derivation entry points.
6
+ #
7
+ # {Seed} is the normalization boundary between recovery inputs
8
+ # (BIP39/SLIP-0039), raw entropy-like byte material, and downstream chain
9
+ # derivation. Callers can import from several supported input forms, but once
10
+ # constructed the object always wraps validated raw bytes.
11
+ class Seed
12
+ include Utils::Encoding
13
+ extend Utils::Encoding
14
+
15
+ # Raw seed bytes after normalization and validation.
16
+ #
17
+ # @return [String]
18
+ attr_reader :bytes
19
+
20
+ # @param bytes [String] raw seed bytes
21
+ # @raise [Errors::InvalidSeedError] if the seed length is not valid
22
+ def initialize(bytes)
23
+ @bytes = bytes
24
+
25
+ raise Errors::InvalidSeedError unless Constants::SEED_LENGTHS.include?(@bytes.bytesize)
26
+ end
27
+
28
+ # Returns the hex representation of the seed
29
+ #
30
+ # @return [String] hex string
31
+ def hex
32
+ bytes_to_hex(@bytes)
33
+ end
34
+
35
+ # Returns the array of octets (integers 0-255) representation of the seed
36
+ #
37
+ # @return [Array<Integer>] array of byte values
38
+ def octets
39
+ @bytes.bytes
40
+ end
41
+
42
+ class << self
43
+ # Loads a seed from a given value
44
+ #
45
+ # Supported inputs:
46
+ # - `nil` to generate a new random seed
47
+ # - existing {Seed}
48
+ # - validated {Recovery::Bip39}
49
+ # - hex string
50
+ # - mnemonic string
51
+ # - raw byte string
52
+ # - array of octets
53
+ #
54
+ # @param value [String, Seed, Recovery::Bip39, Array<Integer>, nil]
55
+ # @return [Seed]
56
+ # @raise [Errors::InvalidSeedError] if the value cannot be normalized
57
+ def import(value)
58
+ case
59
+ when value.nil? then generate
60
+ when value.is_a?(Seed) then import_from_seed(value)
61
+ when value.is_a?(Recovery::Bip39) then import_from_mnemonic(value)
62
+ when hex_string?(value) then import_from_hex(value)
63
+ when mnemonic_string?(value) then import_from_mnemonic(value)
64
+ when byte_string?(value) then import_from_bytes(value)
65
+ when octet_array?(value) then import_from_octets(value)
66
+ else
67
+ raise Errors::InvalidSeedError
68
+ end
69
+ end
70
+
71
+ # Generates a new random 32-byte seed
72
+ #
73
+ # @return [Seed] the generated seed
74
+ def generate
75
+ new(Core::Entropy.generate(bytes: 32))
76
+ end
77
+
78
+ # Creates a new seed from a byte string
79
+ #
80
+ # @param seed [String] the byte string seed
81
+ # @return [Seed] the created Seed
82
+ def import_from_bytes(bytes)
83
+ new(bytes)
84
+ end
85
+
86
+ # Creates a new seed from a hex string
87
+ #
88
+ # @param seed_hex [String] the hex string seed
89
+ # @return [Seed] the created Seed
90
+ def import_from_hex(hex)
91
+ new(hex_to_bytes(hex))
92
+ end
93
+
94
+ # Creates a new seed from an array of octets
95
+ #
96
+ # @param seed_octets [Array<Integer>] the array of octets
97
+ # @return [Seed] the created Seed
98
+ def import_from_octets(octets)
99
+ new(octets_to_bytes(octets))
100
+ end
101
+
102
+ # Creates a new seed from another Seed
103
+ #
104
+ # @param seed [Seed] the Seed to copy
105
+ # @return [Seed] the created Seed
106
+ def import_from_seed(seed)
107
+ new(seed.bytes)
108
+ end
109
+
110
+ # Recovers a seed from a BIP39 mnemonic phrase.
111
+ #
112
+ # @param mnemonic [Recovery::Bip39, String]
113
+ # @return [Seed]
114
+ def import_from_mnemonic(mnemonic)
115
+ Recovery::Bip39.import(mnemonic).seed
116
+ end
117
+
118
+ private
119
+
120
+ def mnemonic_string?(value)
121
+ return false unless value.is_a?(String)
122
+
123
+ Constants::MNEMONIC_WORD_COUNTS.include?(value.strip.split(/\s+/).length)
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,11 @@
1
+ {
2
+ "folders": [
3
+ {
4
+ "path": "../../../slip-39-tools"
5
+ },
6
+ {
7
+ "path": "../.."
8
+ }
9
+ ],
10
+ "settings": {}
11
+ }
@@ -0,0 +1,134 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SkeletonKey
4
+ module Utils
5
+ ##
6
+ # Static helpers for encoding/decoding binary values
7
+ # into hex, base64, base58, etc.
8
+ #
9
+ # Also provides validation helpers for distinguishing
10
+ # between byte strings and hex strings.
11
+ #
12
+ # Usage:
13
+ # SkeletonKey::Utils::Encoding.hex_to_bytes("deadbeef")
14
+ # SkeletonKey::Utils::Encoding.bytes_to_hex("\xDE\xAD")
15
+ # SkeletonKey::Utils::Encoding.hex_string?("zzzz") # => false
16
+ #
17
+ module Encoding
18
+ module_function
19
+
20
+ ##
21
+ # Base58Check encoding (Bitcoin alphabet)
22
+ #
23
+ # @param [String] payload binary string to encode
24
+ # @return [String] base58check-encoded string
25
+ def base58check_encode(payload)
26
+ Codecs::Base58Check.encode(payload)
27
+ end
28
+
29
+ ##
30
+ # Convert a hex string into raw bytes.
31
+ #
32
+ # @param [String] hex a valid hex string
33
+ # @return [String] binary string of bytes
34
+ # @raise [ArgumentError] if input is not valid hex
35
+ def hex_to_bytes(hex)
36
+ raise ArgumentError, "invalid hex string" unless hex_string?(hex)
37
+ [hex].pack("H*")
38
+ end
39
+
40
+ ##
41
+ # Convert raw bytes into a hex string.
42
+ #
43
+ # @param [String] bytes binary string
44
+ # @return [String] hex string
45
+ def bytes_to_hex(bytes)
46
+ bytes.unpack1("H*")
47
+ end
48
+
49
+ ##
50
+ # Convert an array of octets (integers 0-255) into raw bytes.
51
+ #
52
+ # @param [Array<Integer>] octets array of byte values
53
+ # @return [String] binary string of bytes
54
+ def octets_to_bytes(octets)
55
+ octets.pack("C*")
56
+ end
57
+
58
+ ##
59
+ # Check if a value is valid hex.
60
+ #
61
+ # @param [String] value
62
+ # @return [Boolean]
63
+ def hex_string?(value)
64
+ value.is_a?(String) && value.match?(/\A[0-9a-fA-F]+\z/) && value.length.even?
65
+ end
66
+
67
+ ##
68
+ # Check if a value looks like a raw byte string.
69
+ #
70
+ # @param [String] value
71
+ # @return [Boolean]
72
+ def byte_string?(value)
73
+ value.is_a?(String) && value.encoding == ::Encoding::BINARY
74
+ end
75
+
76
+ ##
77
+ # Checks if a value is an array of octets (integers 0-255)
78
+ #
79
+ # @param [Object] value
80
+ # @return [Boolean]
81
+ def octet_array?(value)
82
+ value.is_a?(Array) && value.all? { |b| b.is_a?(Integer) && b.between?(0, 255) }
83
+ end
84
+
85
+ ##
86
+ # Serialize a 32-bit unsigned integer to big-endian binary.
87
+ #
88
+ # Used in BIP32 child index encoding and serialization.
89
+ #
90
+ # @param i [Integer] integer in range 0..2^32-1
91
+ # @return [String] 4-byte big-endian representation
92
+ #
93
+ # @example
94
+ # ser32(1) # => "\x00\x00\x00\x01"
95
+ # ser32(256) # => "\x00\x00\x01\x00"
96
+ def ser32(i)
97
+ [i].pack("N")
98
+ end
99
+
100
+ ##
101
+ # Serialize a 256-bit integer into a fixed-length 32-byte string.
102
+ #
103
+ # Pads with leading zeros if necessary. Used for private key
104
+ # and scalar encoding in BIP32.
105
+ #
106
+ # @param i [Integer] integer in range 0..2^256-1
107
+ # @return [String] 32-byte binary string
108
+ #
109
+ # @example
110
+ # ser256(1).bytesize # => 32
111
+ # ser256(1).unpack1("H*") # => "0000...0001" (64 hex chars)
112
+ def ser256(i)
113
+ i.to_s(16).rjust(64, "0").scan(/../).map { |b| b.hex }.pack("C*")
114
+ end
115
+
116
+ ##
117
+ # Parse a 32-byte string into a 256-bit integer.
118
+ #
119
+ # Inverse of {#ser256}. Converts a big-endian binary
120
+ # string into an Integer.
121
+ #
122
+ # @param b [String] 32-byte binary string
123
+ # @return [Integer] integer value
124
+ #
125
+ # @example
126
+ # i = 42
127
+ # bin = ser256(i)
128
+ # parse256(bin) # => 42
129
+ def parse256(b)
130
+ b.unpack1("H*").to_i(16)
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "digest"
4
+ require "openssl"
5
+
6
+ module SkeletonKey
7
+ module Utils
8
+ ##
9
+ # Collection of cryptographic hashing and HMAC utilities.
10
+ #
11
+ # This module wraps common digest primitives into clear,
12
+ # chainable methods with explicit names.
13
+ #
14
+ # These are the **building blocks** for higher-level functions
15
+ # like address encoding (Bitcoin), key derivation (BIP32/SLIP-10),
16
+ # and message authentication.
17
+ #
18
+ # @example Compute a Bitcoin address hash
19
+ # pubkey = "\x02..." # compressed secp256k1 public key
20
+ # addr_hash = SkeletonKey::Hashing.hash160(pubkey)
21
+ #
22
+ # @example Compute a BIP32 master key
23
+ # seed = SecureRandom.random_bytes(64)
24
+ # i = SkeletonKey::Hashing.hmac_sha512("Bitcoin seed", seed)
25
+ #
26
+ module Hashing
27
+ module_function
28
+ ##
29
+ # Generate n-byte checksum (first n bytes of double SHA256)
30
+ #
31
+ # @param payload [String] input byte string
32
+ # @return [String] n-byte checksum
33
+ def checksum(payload, length: 4)
34
+ double_sha256(payload)[0, length]
35
+ end
36
+
37
+ ##
38
+ # Single SHA-256 digest of data.
39
+ #
40
+ # @param data [String] input byte string
41
+ # @return [String] 32-byte binary digest
42
+ #
43
+ # @see https://en.wikipedia.org/wiki/SHA-2
44
+ def sha256(data)
45
+ Digest::SHA256.digest(data)
46
+ end
47
+
48
+ ##
49
+ # Single RIPEMD-160 digest of data.
50
+ #
51
+ # @param data [String] input byte string
52
+ # @return [String] 20-byte binary digest
53
+ #
54
+ # @see https://homes.esat.kuleuven.be/~bosselae/ripemd160/
55
+ def ripemd160(data)
56
+ Digest::RMD160.digest(data)
57
+ end
58
+
59
+ ##
60
+ # Hash160 = RIPEMD-160(SHA-256(data))
61
+ #
62
+ # Widely used in Bitcoin for address derivation from public keys.
63
+ #
64
+ # @param data [String] input byte string
65
+ # @return [String] 20-byte binary digest
66
+ #
67
+ # @example
68
+ # SkeletonKey::Hashing.hash160(pubkey)
69
+ def hash160(data)
70
+ ripemd160(sha256(data))
71
+ end
72
+
73
+ ##
74
+ # Double SHA-256 digest.
75
+ #
76
+ # Used in Bitcoin for checksums and block header hashing.
77
+ #
78
+ # @param data [String] input byte string
79
+ # @return [String] 32-byte binary digest
80
+ #
81
+ # @example
82
+ # SkeletonKey::Hashing.double_sha256("hello")
83
+ def double_sha256(data)
84
+ sha256(sha256(data))
85
+ end
86
+
87
+ ##
88
+ # HMAC-SHA512 keyed hash.
89
+ #
90
+ # Used in BIP32 master key and child key derivation.
91
+ #
92
+ # @param key [String] HMAC key
93
+ # @param data [String] message input
94
+ # @return [String] 64-byte binary HMAC digest
95
+ #
96
+ # @see https://en.wikipedia.org/wiki/HMAC
97
+ # @see https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki
98
+ #
99
+ # @example BIP32 master key
100
+ # i = SkeletonKey::Hashing.hmac_sha512("Bitcoin seed", seed)
101
+ # il, ir = i[0, 32], i[32, 32]
102
+ def hmac_sha512(key, data)
103
+ OpenSSL::HMAC.digest("SHA512", key, data)
104
+ end
105
+
106
+ def keccak256(data)
107
+ rate = 136
108
+ state = Array.new(25, 0)
109
+ offset = 0
110
+
111
+ while offset + rate <= data.bytesize
112
+ keccak_absorb_block(state, data.byteslice(offset, rate))
113
+ keccak_f1600(state)
114
+ offset += rate
115
+ end
116
+
117
+ tail = (data.byteslice(offset, data.bytesize - offset) || +"").b
118
+ tail << "\x01".b
119
+ tail << "\x00".b * (rate - tail.bytesize)
120
+ tail.setbyte(rate - 1, tail.getbyte(rate - 1) | 0x80)
121
+
122
+ keccak_absorb_block(state, tail)
123
+ keccak_f1600(state)
124
+ keccak_squeeze(state, 32)
125
+ end
126
+
127
+ def keccak_absorb_block(state, block)
128
+ (block.bytesize / 8).times do |idx|
129
+ lane_bytes = block.byteslice(idx * 8, 8).bytes
130
+ lane = lane_bytes.each_with_index.reduce(0) do |acc, (byte, byte_idx)|
131
+ acc | (byte << (8 * byte_idx))
132
+ end
133
+ state[idx] ^= lane
134
+ end
135
+ end
136
+
137
+ def keccak_squeeze(state, length)
138
+ output = +"".b
139
+ lane_index = 0
140
+
141
+ while output.bytesize < length
142
+ output << [state[lane_index]].pack("Q<")
143
+ lane_index += 1
144
+ if lane_index == 17 && output.bytesize < length
145
+ keccak_f1600(state)
146
+ lane_index = 0
147
+ end
148
+ end
149
+
150
+ output.byteslice(0, length)
151
+ end
152
+
153
+ def keccak_f1600(state)
154
+ 24.times do |round|
155
+ c = 5.times.map do |x|
156
+ state[x] ^ state[x + 5] ^ state[x + 10] ^ state[x + 15] ^ state[x + 20]
157
+ end
158
+
159
+ d = 5.times.map do |x|
160
+ c[(x - 1) % 5] ^ keccak_rotl64(c[(x + 1) % 5], 1)
161
+ end
162
+
163
+ 25.times do |idx|
164
+ state[idx] = (state[idx] ^ d[idx % 5]) & keccak_mask
165
+ end
166
+
167
+ b = Array.new(25, 0)
168
+ 5.times do |x|
169
+ 5.times do |y|
170
+ b[y + (5 * ((2 * x + 3 * y) % 5))] =
171
+ keccak_rotl64(state[x + (5 * y)], keccak_rotation_offsets[x][y])
172
+ end
173
+ end
174
+
175
+ 5.times do |x|
176
+ 5.times do |y|
177
+ idx = x + (5 * y)
178
+ state[idx] = b[idx] ^ ((~b[((x + 1) % 5) + (5 * y)]) & b[((x + 2) % 5) + (5 * y)])
179
+ state[idx] &= keccak_mask
180
+ end
181
+ end
182
+
183
+ state[0] ^= keccak_round_constants[round]
184
+ end
185
+ end
186
+
187
+ def keccak_rotl64(value, shift)
188
+ shift %= 64
189
+ return value & keccak_mask if shift.zero?
190
+
191
+ ((value << shift) | (value >> (64 - shift))) & keccak_mask
192
+ end
193
+
194
+ def keccak_mask
195
+ 0xFFFF_FFFF_FFFF_FFFF
196
+ end
197
+
198
+ def keccak_rotation_offsets
199
+ [
200
+ [0, 36, 3, 41, 18],
201
+ [1, 44, 10, 45, 2],
202
+ [62, 6, 43, 15, 61],
203
+ [28, 55, 25, 21, 56],
204
+ [27, 20, 39, 8, 14]
205
+ ]
206
+ end
207
+
208
+ def keccak_round_constants
209
+ [
210
+ 0x0000000000000001,
211
+ 0x0000000000008082,
212
+ 0x800000000000808A,
213
+ 0x8000000080008000,
214
+ 0x000000000000808B,
215
+ 0x0000000080000001,
216
+ 0x8000000080008081,
217
+ 0x8000000000008009,
218
+ 0x000000000000008A,
219
+ 0x0000000000000088,
220
+ 0x0000000080008009,
221
+ 0x000000008000000A,
222
+ 0x000000008000808B,
223
+ 0x800000000000008B,
224
+ 0x8000000000008089,
225
+ 0x8000000000008003,
226
+ 0x8000000000008002,
227
+ 0x8000000000000080,
228
+ 0x000000000000800A,
229
+ 0x800000008000000A,
230
+ 0x8000000080008081,
231
+ 0x8000000000008080,
232
+ 0x0000000080000001,
233
+ 0x8000000080008008
234
+ ]
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SkeletonKey
4
+ # Current gem version.
5
+ #
6
+ # @return [String]
7
+ VERSION = "0.1.0"
8
+ end
@@ -0,0 +1,66 @@
1
+ # Version
2
+ require_relative "skeleton_key/version"
3
+
4
+ # Main module for SkeletonKey
5
+ require_relative "skeleton_key/constants"
6
+ require_relative "skeleton_key/errors"
7
+
8
+ # Utilities
9
+ require_relative "skeleton_key/utils/hashing"
10
+ require_relative "skeleton_key/utils/encoding"
11
+ require_relative "skeleton_key/codecs/base58"
12
+ require_relative "skeleton_key/codecs/base58_check"
13
+ require_relative "skeleton_key/codecs/bech32"
14
+
15
+ # Derivation methods
16
+ require_relative "skeleton_key/derivation/path"
17
+ require_relative "skeleton_key/derivation/bip32"
18
+ require_relative "skeleton_key/derivation/slip10"
19
+
20
+ # Core functionality
21
+ require_relative "skeleton_key/core/entropy"
22
+ require_relative "skeleton_key/recovery/slip39_support/protocol"
23
+ require_relative "skeleton_key/recovery/slip39_support/share"
24
+ require_relative "skeleton_key/recovery/slip39_support/bit_packing"
25
+ require_relative "skeleton_key/recovery/slip39_support/checksum"
26
+ require_relative "skeleton_key/recovery/slip39_support/interpolation"
27
+ require_relative "skeleton_key/recovery/slip39_support/cipher"
28
+ require_relative "skeleton_key/recovery/slip39_support/encoder"
29
+ require_relative "skeleton_key/recovery/slip39_support/generated_set"
30
+ require_relative "skeleton_key/recovery/slip39_support/decoder"
31
+ require_relative "skeleton_key/recovery/slip39_support/secret_recovery"
32
+ require_relative "skeleton_key/recovery/slip39_support/generator"
33
+ require_relative "skeleton_key/recovery/slip39"
34
+
35
+ # Higher-level abstractions
36
+ require_relative "skeleton_key/recovery/bip39"
37
+ require_relative "skeleton_key/seed"
38
+ require_relative "skeleton_key/keyring"
39
+
40
+ # Canonical chain namespace
41
+ require_relative "skeleton_key/chains/bitcoin/support/versioning"
42
+ require_relative "skeleton_key/chains/bitcoin/support/paths"
43
+ require_relative "skeleton_key/chains/bitcoin/support/outputs"
44
+ require_relative "skeleton_key/chains/bitcoin/support"
45
+ require_relative "skeleton_key/chains/bitcoin/account_derivation"
46
+ require_relative "skeleton_key/chains/bitcoin/account"
47
+ require_relative "skeleton_key/chains/ethereum/support"
48
+ require_relative "skeleton_key/chains/ethereum/account"
49
+ require_relative "skeleton_key/chains/solana/support"
50
+ require_relative "skeleton_key/chains/solana/account"
51
+
52
+ module SkeletonKey
53
+ ##
54
+ # Top-level namespace for the SkeletonKey library.
55
+ #
56
+ # The file load order here reflects the repository architecture:
57
+ # - shared constants and typed errors
58
+ # - shared utilities and codecs
59
+ # - shared derivation primitives
60
+ # - recovery formats and seed normalization
61
+ # - chain-specific account implementations
62
+ class Error < StandardError; end
63
+
64
+ # Project root directory
65
+ ROOT = File.expand_path("..", __dir__)
66
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: skeleton_key
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sebastian Scholl
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: rspec
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '3.12'
19
+ type: :development
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '3.12'
26
+ description: SkeletonKey provides deterministic wallet recovery, seed normalization,
27
+ and key derivation for Bitcoin, Ethereum, and Solana.
28
+ email:
29
+ - sebscholl@gmail.com
30
+ executables:
31
+ - console
32
+ - lint
33
+ - setup
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - README.md
38
+ - bin/console
39
+ - bin/lint
40
+ - bin/setup
41
+ - lib/skeleton_key.rb
42
+ - lib/skeleton_key/chains/bitcoin/account.rb
43
+ - lib/skeleton_key/chains/bitcoin/account_derivation.rb
44
+ - lib/skeleton_key/chains/bitcoin/support.rb
45
+ - lib/skeleton_key/chains/bitcoin/support/outputs.rb
46
+ - lib/skeleton_key/chains/bitcoin/support/paths.rb
47
+ - lib/skeleton_key/chains/bitcoin/support/versioning.rb
48
+ - lib/skeleton_key/chains/ethereum/account.rb
49
+ - lib/skeleton_key/chains/ethereum/support.rb
50
+ - lib/skeleton_key/chains/solana/account.rb
51
+ - lib/skeleton_key/chains/solana/support.rb
52
+ - lib/skeleton_key/codecs/base58.rb
53
+ - lib/skeleton_key/codecs/base58_check.rb
54
+ - lib/skeleton_key/codecs/bech32.rb
55
+ - lib/skeleton_key/constants.rb
56
+ - lib/skeleton_key/core/entropy.rb
57
+ - lib/skeleton_key/derivation/bip32.rb
58
+ - lib/skeleton_key/derivation/path.rb
59
+ - lib/skeleton_key/derivation/slip10.rb
60
+ - lib/skeleton_key/errors.rb
61
+ - lib/skeleton_key/keyring.rb
62
+ - lib/skeleton_key/recovery/bip39.rb
63
+ - lib/skeleton_key/recovery/bip39_english.txt
64
+ - lib/skeleton_key/recovery/slip39.rb
65
+ - lib/skeleton_key/recovery/slip39_support/bit_packing.rb
66
+ - lib/skeleton_key/recovery/slip39_support/checksum.rb
67
+ - lib/skeleton_key/recovery/slip39_support/cipher.rb
68
+ - lib/skeleton_key/recovery/slip39_support/decoder.rb
69
+ - lib/skeleton_key/recovery/slip39_support/encoder.rb
70
+ - lib/skeleton_key/recovery/slip39_support/generated_set.rb
71
+ - lib/skeleton_key/recovery/slip39_support/generator.rb
72
+ - lib/skeleton_key/recovery/slip39_support/interpolation.rb
73
+ - lib/skeleton_key/recovery/slip39_support/protocol.rb
74
+ - lib/skeleton_key/recovery/slip39_support/secret_recovery.rb
75
+ - lib/skeleton_key/recovery/slip39_support/share.rb
76
+ - lib/skeleton_key/recovery/slip39_wordlist.txt
77
+ - lib/skeleton_key/seed.rb
78
+ - lib/skeleton_key/skeleton_key.code-workspace
79
+ - lib/skeleton_key/utils/encoding.rb
80
+ - lib/skeleton_key/utils/hashing.rb
81
+ - lib/skeleton_key/version.rb
82
+ homepage: https://github.com/sebscholl/skeleton-key
83
+ licenses:
84
+ - MIT
85
+ metadata:
86
+ homepage_uri: https://github.com/sebscholl/skeleton-key
87
+ source_code_uri: https://github.com/sebscholl/skeleton-key
88
+ documentation_uri: https://github.com/sebscholl/skeleton-key#readme
89
+ rubygems_mfa_required: 'true'
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: 3.2.0
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubygems_version: 3.6.9
105
+ specification_version: 4
106
+ summary: Deterministic wallet recovery and derivation across chains
107
+ test_files: []