Class: Sibit::Key
- Inherits:
-
Object
- Object
- Sibit::Key
- Defined in:
- lib/sibit/key.rb
Overview
Bitcoin ECDSA key using secp256k1 curve.
Supports OpenSSL 3.0+ by constructing keys via DER encoding instead of using deprecated mutable key APIs.
- Author
Yegor Bugayenko (yegor256@gmail.com)
- Copyright
Copyright (c) 2019-2026 Yegor Bugayenko
- License
MIT
Constant Summary collapse
- MIN_PRIV =
0x01- MAX_PRIV =
0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140- SECP256K1_N =
OpenSSL::BN.new( 'FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141', 16 )
Instance Attribute Summary collapse
-
#network ⇒ Object
readonly
Returns the value of attribute network.
Class Method Summary collapse
Instance Method Summary collapse
- #base58 ⇒ Object
- #bech32 ⇒ Object
-
#initialize(privkey, network: nil) ⇒ Key
constructor
A new instance of Key.
- #priv ⇒ Object
- #pub ⇒ Object
- #sign(data) ⇒ Object
- #verify(data, sig) ⇒ Object
Constructor Details
#initialize(privkey, network: nil) ⇒ Key
Returns a new instance of Key.
46 47 48 49 50 51 52 |
# File 'lib/sibit/key.rb', line 46 def initialize(privkey, network: nil) @override = network @network = :mainnet @compressed = true @privkey = decode(privkey) @key = build(@privkey) end |
Instance Attribute Details
#network ⇒ Object (readonly)
Returns the value of attribute network.
30 31 32 |
# File 'lib/sibit/key.rb', line 30 def network @network end |
Class Method Details
.generate(network: :mainnet) ⇒ Object
32 33 34 35 36 37 38 39 40 41 42 43 44 |
# File 'lib/sibit/key.rb', line 32 def self.generate(network: :mainnet) key = OpenSSL::PKey::EC.generate('secp256k1') pvt = key.private_key raise(Sibit::Error, 'Invalid private key: zero') if pvt.zero? raise(Sibit::Error, 'Invalid private key: out of range') if pvt >= SECP256K1_N raise(Sibit::Error, 'Invalid public key: not on curve') unless key.public_key.on_curve? hex = key.private_key.to_s(16).rjust(64, '0').downcase raise(Sibit::Error, 'Invalid private key encoding') unless hex.match?(/\A[0-9a-f]{64}\z/) unless OpenSSL::BN.new(hex, 16) == pvt raise(Sibit::Error, 'Private key serialization is lossy') end new(hex, network: network) end |
Instance Method Details
#base58 ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/sibit/key.rb', line 74 def base58 hex = pub raise(Error, 'Invalid public key: not on curve') unless @key.public_key.on_curve? raise(Error, 'Invalid public key format') unless hex.match?(/\A0[23][0-9a-f]{64}\z/) versioned = "#{@network == :mainnet ? '00' : '6f'}#{hash160(hex)}" addr = Base58.new(versioned + Base58.new(versioned).check).encode mainnet = /\A1[1-9A-HJ-NP-Za-km-z]{25,34}\z/ testnet = /\A[mn][1-9A-HJ-NP-Za-km-z]{25,34}\z/ unless addr.match?(@network == :mainnet ? mainnet : testnet) raise(Error, "Invalid base58 address: #{addr}") end addr end |
#bech32 ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/sibit/key.rb', line 62 def bech32 hrp = { mainnet: 'bc', testnet: 'tb', regtest: 'bcrt' }[@network] hex = pub raise(Error, 'Invalid public key: not on curve') unless @key.public_key.on_curve? raise(Error, 'Invalid public key format') unless hex.match?(/\A0[23][0-9a-f]{64}\z/) addr = Bech32.encode(hrp, 0, hash160(hex)) unless addr.match?(/\A#{hrp}1q[a-z0-9]{38,58}\z/) raise(Error, "Invalid bech32 address: #{addr}") end addr end |
#priv ⇒ Object
54 55 56 |
# File 'lib/sibit/key.rb', line 54 def priv @privkey end |
#pub ⇒ Object
58 59 60 |
# File 'lib/sibit/key.rb', line 58 def pub @key.public_key.to_octet_string(@compressed ? :compressed : :uncompressed).unpack1('H*') end |
#sign(data) ⇒ Object
88 89 90 91 92 |
# File 'lib/sibit/key.rb', line 88 def sign(data) sig = @key.dsa_sign_asn1(data) raise(Error, 'Signature verification failed') unless verify(data, sig) sig end |
#verify(data, sig) ⇒ Object
94 95 96 97 98 |
# File 'lib/sibit/key.rb', line 94 def verify(data, sig) @key.dsa_verify_asn1(data, sig) rescue OpenSSL::PKey::PKeyError false end |