Module: BSV::Wallet::CertificateSignature
- Defined in:
- lib/bsv/wallet_interface/certificate_signature.rb
Overview
BRC-52 identity certificate signature verification.
Certificates carry a signature from the certifier over a canonical binary serialisation of their fields (excluding the signature itself). This module builds that canonical serialisation and delegates verification to a ProtoWallet-compatible verifier.
Every field is included in the preimage in this order:
-
type(base64 → 32 bytes) -
serial_number(base64 → 32 bytes) -
subject(hex → 33-byte compressed pubkey) -
certifier(hex → 33-byte compressed pubkey) -
revocation_outpoint: txid hex (32 bytes) + output index VarInt -
fields: VarInt count, then for each field (sorted lexicographically by name): VarInt name length + UTF-8 name bytes + VarInt value length + UTF-8 value bytes
Signing uses BRC-42 key derivation with:
-
protocol ID: [2, ‘certificate signature’]
-
key ID: “#{type} #{serial_number}”
-
counterparty on sign: ‘anyone’ (default of ProtoWallet#create_signature in TS — Ruby consumers should pass it explicitly since Ruby defaults to ‘self’)
-
counterparty on verify: the certifier’s public key hex
Defined Under Namespace
Classes: InvalidError
Constant Summary collapse
- PROTOCOL_ID =
[2, 'certificate signature'].freeze
Class Method Summary collapse
-
.serialise_preimage(cert) ⇒ String
Build the BRC-52 canonical preimage for signing or verifying.
-
.verify!(cert, verifier: ProtoWallet.new('anyone')) ⇒ true
Verify a certificate’s certifier signature.
Class Method Details
.serialise_preimage(cert) ⇒ String
Build the BRC-52 canonical preimage for signing or verifying.
93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/bsv/wallet_interface/certificate_signature.rb', line 93 def serialise_preimage(cert) buf = String.new(encoding: Encoding::ASCII_8BIT) buf << decode_base64_exact(cert[:type], 32, 'type') buf << decode_base64_exact(cert[:serial_number], 32, 'serial_number') buf << decode_hex_exact(cert[:subject], 33, 'subject') buf << decode_hex_exact(cert[:certifier], 33, 'certifier') buf << encode_revocation_outpoint(cert[:revocation_outpoint]) buf << encode_fields(cert[:fields]) buf end |
.verify!(cert, verifier: ProtoWallet.new('anyone')) ⇒ true
Verify a certificate’s certifier signature.
Raises InvalidError if the signature is missing, malformed, or does not match the expected certifier.
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/bsv/wallet_interface/certificate_signature.rb', line 59 def verify!(cert, verifier: ProtoWallet.new('anyone')) signature_hex = cert[:signature] raise InvalidError, 'signature is missing' if signature_hex.nil? || signature_hex.empty? preimage = serialise_preimage(cert) sig_bytes = hex_to_bytes(signature_hex) verifier.verify_signature({ data: preimage.unpack('C*'), signature: sig_bytes, protocol_id: PROTOCOL_ID, key_id: "#{cert[:type]} #{cert[:serial_number]}", counterparty: cert[:certifier] }) true rescue InvalidSignatureError => e raise if e.is_a?(InvalidError) raise InvalidError, e. rescue ArgumentError, EncodingError => e # EncodingError covers Encoding::InvalidByteSequenceError and # Encoding::UndefinedConversionError, which `encode_fields` # raises for non-UTF-8 field names or values. Callers of # `acquire_certificate` expect `InvalidError` on bad cert input # — leaking EncodingError would break that contract. raise InvalidError, e. end |