Class: BSV::Primitives::PublicKey
- Inherits:
-
Object
- Object
- BSV::Primitives::PublicKey
- Defined in:
- lib/bsv/primitives/public_key.rb
Overview
A secp256k1 public key for address derivation and signature verification.
Public keys are points on the secp256k1 curve. They can be serialised in compressed (33-byte) or uncompressed (65-byte) form, converted to Bitcoin addresses, and used to verify ECDSA signatures.
Constant Summary collapse
- MAINNET_PUBKEY_HASH =
Address version byte for mainnet P2PKH addresses.
"\x00".b
- TESTNET_PUBKEY_HASH =
Address version byte for testnet P2PKH addresses.
"\x6f".b
Instance Attribute Summary collapse
-
#point ⇒ OpenSSL::PKey::EC::Point
readonly
The underlying curve point.
Class Method Summary collapse
-
.from_bytes(bytes) ⇒ PublicKey
Create a public key from raw bytes (compressed or uncompressed).
-
.from_hex(hex) ⇒ PublicKey
Create a public key from a hex string.
-
.from_private_key(private_key) ⇒ PublicKey
Derive the public key from a PrivateKey.
Instance Method Summary collapse
-
#==(other) ⇒ Boolean
trueif both keys represent the same curve point. -
#address(network: :mainnet) ⇒ String
Derive a Base58Check-encoded Bitcoin address.
-
#compressed ⇒ String
Return the compressed (33-byte) encoding.
-
#derive_child(private_key, invoice_number) ⇒ PublicKey
Derive a child public key using BRC-42 key derivation.
-
#derive_shared_secret(private_key) ⇒ PublicKey
Derive an ECDH shared secret with another party’s private key.
-
#hash160 ⇒ String
Compute the Hash160 (RIPEMD-160 of SHA-256) of the compressed public key.
-
#initialize(point) ⇒ PublicKey
constructor
A new instance of PublicKey.
-
#to_hex(compressed: true) ⇒ String
Return the public key as a hex string.
-
#uncompressed ⇒ String
Return the uncompressed (65-byte) encoding.
-
#verify(hash, signature) ⇒ Boolean
Verify an ECDSA signature against a message hash.
Constructor Details
#initialize(point) ⇒ PublicKey
Returns a new instance of PublicKey.
28 29 30 31 32 33 |
# File 'lib/bsv/primitives/public_key.rb', line 28 def initialize(point) raise ArgumentError, 'point must be an EC point' unless point.is_a?(OpenSSL::PKey::EC::Point) raise ArgumentError, 'point is at infinity' if point.infinity? @point = point end |
Instance Attribute Details
#point ⇒ OpenSSL::PKey::EC::Point (readonly)
Returns the underlying curve point.
24 25 26 |
# File 'lib/bsv/primitives/public_key.rb', line 24 def point @point end |
Class Method Details
.from_bytes(bytes) ⇒ PublicKey
Create a public key from raw bytes (compressed or uncompressed).
39 40 41 42 |
# File 'lib/bsv/primitives/public_key.rb', line 39 def self.from_bytes(bytes) point = Curve.point_from_bytes(bytes) new(point) end |
.from_hex(hex) ⇒ PublicKey
Create a public key from a hex string.
48 49 50 |
# File 'lib/bsv/primitives/public_key.rb', line 48 def self.from_hex(hex) from_bytes(Hex.decode(hex, name: 'public key hex')) end |
.from_private_key(private_key) ⇒ PublicKey
Derive the public key from a BSV::Primitives::PrivateKey.
56 57 58 |
# File 'lib/bsv/primitives/public_key.rb', line 56 def self.from_private_key(private_key) private_key.public_key end |
Instance Method Details
#==(other) ⇒ Boolean
Returns true if both keys represent the same curve point.
152 153 154 |
# File 'lib/bsv/primitives/public_key.rb', line 152 def ==(other) other.is_a?(PublicKey) && compressed == other.compressed end |
#address(network: :mainnet) ⇒ String
Derive a Base58Check-encoded Bitcoin address.
97 98 99 100 |
# File 'lib/bsv/primitives/public_key.rb', line 97 def address(network: :mainnet) prefix = network == :mainnet ? MAINNET_PUBKEY_HASH : TESTNET_PUBKEY_HASH Base58.check_encode(prefix + hash160) end |
#compressed ⇒ String
Return the compressed (33-byte) encoding.
63 64 65 |
# File 'lib/bsv/primitives/public_key.rb', line 63 def compressed @point.to_octet_string(:compressed) end |
#derive_child(private_key, invoice_number) ⇒ PublicKey
Derive a child public key using BRC-42 key derivation.
Computes HMAC-SHA256(key: ECDH_shared_secret, msg: invoice_number) and adds the corresponding curve point to this public key. The result matches the public key of BSV::Primitives::PrivateKey#derive_child with the same inputs, enabling public-key-only derivation.
132 133 134 135 136 137 138 139 |
# File 'lib/bsv/primitives/public_key.rb', line 132 def derive_child(private_key, invoice_number) shared = derive_shared_secret(private_key) hmac = Digest.hmac_sha256(shared.compressed, invoice_number.encode('UTF-8')) hmac_bn = OpenSSL::BN.new(hmac.unpack1('H*'), 16) hmac_point = Curve.multiply_generator_ct(hmac_bn) child_point = Curve.add_points(@point, hmac_point) PublicKey.new(child_point) end |
#derive_shared_secret(private_key) ⇒ PublicKey
Derive an ECDH shared secret with another party’s private key.
Computes the shared point by multiplying this public key by the given private key’s scalar. The result is commutative:
alice_pub.derive_shared_secret(bob_priv) ==
bob_pub.derive_shared_secret(alice_priv)
Uses constant-time scalar multiplication to protect the private key scalar from timing side-channels.
This is the foundational primitive for BRC-42 key derivation, BRC-77/78 messaging, and ECIES encryption.
117 118 119 120 |
# File 'lib/bsv/primitives/public_key.rb', line 117 def derive_shared_secret(private_key) shared_point = Curve.multiply_point_ct(@point, private_key.bn) PublicKey.new(shared_point) end |
#hash160 ⇒ String
Compute the Hash160 (RIPEMD-160 of SHA-256) of the compressed public key.
89 90 91 |
# File 'lib/bsv/primitives/public_key.rb', line 89 def hash160 Digest.hash160(compressed) end |
#to_hex(compressed: true) ⇒ String
Return the public key as a hex string.
78 79 80 81 82 83 84 |
# File 'lib/bsv/primitives/public_key.rb', line 78 def to_hex(compressed: true) if compressed self.compressed.unpack1('H*') else uncompressed.unpack1('H*') end end |
#uncompressed ⇒ String
Return the uncompressed (65-byte) encoding.
70 71 72 |
# File 'lib/bsv/primitives/public_key.rb', line 70 def uncompressed @point.to_octet_string(:uncompressed) end |