GitHub Security Advisory: GHSA-hc36-c89j-5f4j
Package: bsv-wallet (RUBYGEMS)
Severity: HIGH (CVSS 8.1)
CWE: CWE-347: Improper Verification of Cryptographic Signature
Vulnerable versions: >= 0.1.2, < 0.3.4
Patched in: 0.3.4

Summary: bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)

Details:
# Unverified certifier signatures persisted by `acquire_certificate`

## Affected packages

Both `bsv-sdk` and `bsv-wallet` are published from the [sgbett/bsv-ruby-sdk](https://github.com/sgbett/bsv-ruby-sdk) repository. The vulnerable code lives in `lib/bsv/wallet_interface/wallet_client.rb`, which is **physically shipped inside both gems** (the `bsv-wallet.gemspec` `files` list bundles the entire `lib/bsv/wallet_interface/` tree). Consumers of either gem are independently vulnerable; the two packages are versioned separately, so each has its own affected range.

| Package | Affected | Patched |
| --- | --- | --- |
| `bsv-sdk` | `>= 0.3.1, < 0.8.2` | `0.8.2` |
| `bsv-wallet` | `>= 0.1.2, < 0.3.4` | `0.3.4` |

## Summary

`BSV::Wallet::WalletClient#acquire_certificate` persists certificate records to storage **without verifying the certifier's signature** over the certificate contents. Both acquisition paths are affected:

- `acquisition_protocol: 'direct'` — the caller supplies all certificate fields (including `signature:`) and the record is written to storage verbatim.
- `acquisition_protocol: 'issuance'` — the client POSTs to a certifier URL and writes whatever signature the response body contains, also without verification.

An attacker who can reach either API (or who controls a certifier endpoint targeted by the issuance path) can forge identity certificates that subsequently appear authentic to `list_certificates` and `prove_certificate`.

## Details

BRC-52 requires a certificate's `signature` field to be verified against the claimed certifier's public key over a canonical hashing of `(type, subject, serialNumber, revocationOutpoint, fields)` before the certificate is trusted. The reference TypeScript SDK enforces this in `Certificate.verify()`.

### Direct path

The Ruby implementation's `acquire_via_direct` path (`lib/bsv/wallet_interface/wallet_client.rb`) constructs the certificate record directly from caller-supplied fields:

```ruby
def acquire_via_direct(args)
  {
    type: args[:type],
    subject: @key_deriver.identity_key,
    serial_number: args[:serial_number],
    certifier: args[:certifier],
    revocation_outpoint: args[:revocation_outpoint],
    signature: args[:signature],
    fields: args[:fields],
    keyring: args[:keyring_for_subject]
  }
end
```

The returned record is then written to the storage adapter by `acquire_certificate`. No verification of `args[:signature]` against `args[:certifier]`'s public key occurs at any point in this path.

### Issuance path

`acquire_via_issuance` POSTs to a certifier-supplied URL and parses the response body into a certificate record, which is then written to storage without verifying the returned signature. A hostile or compromised certifier endpoint — or anyone able to redirect/MITM the plain HTTP request — can therefore return an arbitrary `signature` value for any subject and have it stored as authentic. This is the same class of bypass as the direct path; it was tracked separ