Class: Tapyrus::Key
- Inherits:
-
Object
- Object
- Tapyrus::Key
- Defined in:
- lib/tapyrus/key.rb
Overview
tapyrus key class
Constant Summary collapse
- PUBLIC_KEY_SIZE =
65
- COMPRESSED_PUBLIC_KEY_SIZE =
33
- SIGNATURE_SIZE =
72
- COMPACT_SIGNATURE_SIZE =
65
- SIG_ALGO =
%i[ecdsa schnorr]
- TYPES =
{ uncompressed: 0x00, compressed: 0x01, p2pkh: 0x10, p2wpkh: 0x11, p2wpkh_p2sh: 0x12 }
- MIN_PRIV_KEY_MOD_ORDER =
0x01
- MAX_PRIV_KEY_MOD_ORDER =
Order of secp256k1’s generator minus 1.
ECDSA::Group::Secp256k1.order - 1
Instance Attribute Summary collapse
-
#key_type ⇒ Object
Returns the value of attribute key_type.
-
#priv_key ⇒ Object
Returns the value of attribute priv_key.
-
#pubkey ⇒ Object
Returns the value of attribute pubkey.
-
#secp256k1_module ⇒ Object
readonly
Returns the value of attribute secp256k1_module.
Class Method Summary collapse
-
.compress_or_uncompress_pubkey?(pubkey) ⇒ Boolean
check
pubkey
(hex) is compress or uncompress pubkey. -
.compress_pubkey?(pubkey) ⇒ Boolean
check
pubkey
(hex) is compress pubkey. -
.from_wif(wif) ⇒ Object
import private key from wif format en.bitcoin.it/wiki/Wallet_import_format.
-
.generate(key_type = TYPES[:compressed]) ⇒ Object
generate key pair.
-
.low_signature?(sig) ⇒ Boolean
check
sig
is low. -
.valid_signature_encoding?(sig, data_sig = false) ⇒ Boolean
check
sig
is correct der encoding.
Instance Method Summary collapse
- #compressed? ⇒ Boolean
-
#fully_valid_pubkey?(allow_hybrid = false) ⇒ Boolean
fully validate whether this is a valid public key (more expensive than IsValid()).
-
#hash160 ⇒ Object
get hash160 public key.
-
#initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false) ⇒ Tapyrus::Key
constructor
initialize private key.
-
#sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa) ⇒ String
sign
data
with private key. - #to_p2pkh ⇒ Object deprecated Deprecated.
-
#to_point ⇒ ECDSA::Point
generate pubkey ec point.
-
#to_wif ⇒ Object
export private key with wif format.
-
#verify(sig, origin, algo: :ecdsa) ⇒ Boolean
verify signature using public key.
Constructor Details
#initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false) ⇒ Tapyrus::Key
initialize private key
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
# File 'lib/tapyrus/key.rb', line 32 def initialize(priv_key: nil, pubkey: nil, key_type: nil, compressed: true, allow_hybrid: false) if key_type.nil? && !compressed.nil? && pubkey.nil? warn("Use key_type parameter instead of compressed. compressed parameter removed in the future.") end if key_type @key_type = key_type compressed = @key_type != TYPES[:uncompressed] else @key_type = compressed ? TYPES[:compressed] : TYPES[:uncompressed] end @secp256k1_module = Tapyrus.secp_impl @priv_key = priv_key if @priv_key raise ArgumentError, Errors::Messages::INVALID_PRIV_KEY unless validate_private_key_range(@priv_key) raise ArgumentError, Errors::Messages::INVALID_PRIV_LENGTH unless @priv_key.htb.bytesize == 32 end if pubkey @pubkey = pubkey else @pubkey = generate_pubkey(priv_key, compressed: compressed) if priv_key end raise ArgumentError, Errors::Messages::INVALID_PUBLIC_KEY unless fully_valid_pubkey?(allow_hybrid) end |
Instance Attribute Details
#key_type ⇒ Object
Returns the value of attribute key_type.
16 17 18 |
# File 'lib/tapyrus/key.rb', line 16 def key_type @key_type end |
#priv_key ⇒ Object
Returns the value of attribute priv_key.
14 15 16 |
# File 'lib/tapyrus/key.rb', line 14 def priv_key @priv_key end |
#pubkey ⇒ Object
Returns the value of attribute pubkey.
15 16 17 |
# File 'lib/tapyrus/key.rb', line 15 def pubkey @pubkey end |
#secp256k1_module ⇒ Object (readonly)
Returns the value of attribute secp256k1_module.
17 18 19 |
# File 'lib/tapyrus/key.rb', line 17 def secp256k1_module @secp256k1_module end |
Class Method Details
.compress_or_uncompress_pubkey?(pubkey) ⇒ Boolean
check pubkey
(hex) is compress or uncompress pubkey.
154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/tapyrus/key.rb', line 154 def self.compress_or_uncompress_pubkey?(pubkey) p = pubkey.htb return false if p.bytesize < COMPRESSED_PUBLIC_KEY_SIZE case p[0] when "\x04" return false unless p.bytesize == PUBLIC_KEY_SIZE when "\x02", "\x03" return false unless p.bytesize == COMPRESSED_PUBLIC_KEY_SIZE else return false end true end |
.compress_pubkey?(pubkey) ⇒ Boolean
check pubkey
(hex) is compress pubkey.
169 170 171 172 |
# File 'lib/tapyrus/key.rb', line 169 def self.compress_pubkey?(pubkey) p = pubkey.htb p.bytesize == COMPRESSED_PUBLIC_KEY_SIZE && ["\x02", "\x03"].include?(p[0]) end |
.from_wif(wif) ⇒ Object
import private key from wif format en.bitcoin.it/wiki/Wallet_import_format
65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |
# File 'lib/tapyrus/key.rb', line 65 def self.from_wif(wif) hex = Base58.decode(wif) raise ArgumentError, "data is too short" if hex.htb.bytesize < 4 version = hex[0..1] data = hex[2...-8].htb checksum = hex[-8..-1] raise ArgumentError, "invalid version" unless version == Tapyrus.chain_params.privkey_version unless Tapyrus.calc_checksum(version + data.bth) == checksum raise ArgumentError, Errors::Messages::INVALID_CHECKSUM end key_len = data.bytesize if key_len == COMPRESSED_PUBLIC_KEY_SIZE && data[-1].unpack("C").first == 1 key_type = TYPES[:compressed] data = data[0..-2] elsif key_len == 32 key_type = TYPES[:uncompressed] else raise ArgumentError, "Wrong number of bytes for a private key, not 32 or 33" end new(priv_key: data.bth, key_type: key_type) end |
.generate(key_type = TYPES[:compressed]) ⇒ Object
generate key pair
58 59 60 61 |
# File 'lib/tapyrus/key.rb', line 58 def self.generate(key_type = TYPES[:compressed]) priv_key, pubkey = Tapyrus.secp_impl.generate_key_pair(compressed: key_type != TYPES[:uncompressed]) new(priv_key: priv_key, pubkey: pubkey, key_type: key_type) end |
.low_signature?(sig) ⇒ Boolean
check sig
is low.
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/tapyrus/key.rb', line 175 def self.low_signature?(sig) s = sig.unpack("C*") len_r = s[3] len_s = s[5 + len_r] val_s = s.slice(6 + len_r, len_s) # prettier-ignore max_mod_half_order = [ 0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x5d, 0x57, 0x6e, 0x73, 0x57, 0xa4, 0x50, 0x1d, 0xdf, 0xe9, 0x2f, 0x46, 0x68, 0x1b, 0x20, 0xa0 ] compare_big_endian(val_s, [0]) > 0 && compare_big_endian(val_s, max_mod_half_order) <= 0 end |
.valid_signature_encoding?(sig, data_sig = false) ⇒ Boolean
check sig
is correct der encoding. This function is consensus-critical since BIP66.
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'lib/tapyrus/key.rb', line 224 def self.valid_signature_encoding?(sig, data_sig = false) num_parts = data_sig ? 8 : 9 size = data_sig ? 72 : 73 return false if sig.bytesize < num_parts || sig.bytesize > size # Minimum and maximum size check s = sig.unpack("C*") return false if s[0] != 0x30 || s[1] != s.size - (data_sig ? 2 : 3) # A signature is of type 0x30 (compound). Make sure the length covers the entire signature. len_r = s[3] return false if 5 + len_r >= s.size # Make sure the length of the S element is still inside the signature. len_s = s[5 + len_r] return false unless len_r + len_s + (data_sig ? 6 : 7) == s.size #Verify that the length of the signature matches the sum of the length of the elements. return false unless s[2] == 0x02 # Check whether the R element is an integer. return false if len_r == 0 # Zero-length integers are not allowed for R. return false unless s[4] & 0x80 == 0 # Negative numbers are not allowed for R. # Null bytes at the start of R are not allowed, unless R would otherwise be interpreted as a negative number. return false if len_r > 1 && (s[4] == 0x00) && (s[5] & 0x80 == 0) return false unless s[len_r + 4] == 0x02 # Check whether the S element is an integer. return false if len_s == 0 # Zero-length integers are not allowed for S. return false unless (s[len_r + 6] & 0x80) == 0 # Negative numbers are not allowed for S. # Null bytes at the start of S are not allowed, unless S would otherwise be interpreted as a negative number. return false if len_s > 1 && (s[len_r + 6] == 0x00) && (s[len_r + 7] & 0x80 == 0) true end |
Instance Method Details
#compressed? ⇒ Boolean
141 142 143 |
# File 'lib/tapyrus/key.rb', line 141 def compressed? key_type != TYPES[:uncompressed] end |
#fully_valid_pubkey?(allow_hybrid = false) ⇒ Boolean
fully validate whether this is a valid public key (more expensive than IsValid())
261 262 263 |
# File 'lib/tapyrus/key.rb', line 261 def fully_valid_pubkey?(allow_hybrid = false) valid_pubkey? && secp256k1_module.parse_ec_pubkey?(pubkey, allow_hybrid) end |
#hash160 ⇒ Object
get hash160 public key.
131 132 133 |
# File 'lib/tapyrus/key.rb', line 131 def hash160 Tapyrus.hash160(pubkey) end |
#sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa) ⇒ String
sign data
with private key
102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/tapyrus/key.rb', line 102 def sign(data, low_r = true, extra_entropy = nil, algo: :ecdsa) raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo) case algo when :ecdsa sign_ecdsa(data, low_r, extra_entropy) when :schnorr secp256k1_module.sign_data(data, priv_key, extra_entropy, algo: algo) else false end end |
#to_p2pkh ⇒ Object
get pay to pubkey hash address
137 138 139 |
# File 'lib/tapyrus/key.rb', line 137 def to_p2pkh Tapyrus::Script.to_p2pkh(hash160).addresses.first end |
#to_point ⇒ ECDSA::Point
generate pubkey ec point
147 148 149 150 151 |
# File 'lib/tapyrus/key.rb', line 147 def to_point p = pubkey p ||= generate_pubkey(priv_key, compressed: compressed) ECDSA::Format::PointOctetString.decode(p.htb, Tapyrus::Secp256k1::GROUP) end |
#to_wif ⇒ Object
export private key with wif format
88 89 90 91 92 93 94 |
# File 'lib/tapyrus/key.rb', line 88 def to_wif version = Tapyrus.chain_params.privkey_version hex = version + priv_key hex += "01" if compressed? hex += Tapyrus.calc_checksum(hex) Base58.encode(hex) end |
#verify(sig, origin, algo: :ecdsa) ⇒ Boolean
verify signature using public key
119 120 121 122 123 124 125 126 127 128 |
# File 'lib/tapyrus/key.rb', line 119 def verify(sig, origin, algo: :ecdsa) return false unless valid_pubkey? begin raise ArgumentError, "Unsupported algorithm has been specified." unless SIG_ALGO.include?(algo) sig = ecdsa_signature_parse_der_lax(sig) if algo == :ecdsa secp256k1_module.verify_sig(origin, sig, pubkey, algo: algo) rescue Exception false end end |