Loading...
Skip to main content

CVE-2026-40070 — Vulnerability in bsv-wallet — CVE Details

HIGH

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

Published April 9, 2026Updated May 13, 2026Source: osv

Summary

# 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 separately as finding **F8.16** in the compliance review and is closed by the same fix. ### Downstream impact Downstream reads via `list_certificates` and selective-disclosure via `prove_certificate` treat stored records as valid without re-verifying, so any forgery that slips past `acquire_certificate` is trusted permanently. ## Impact Any caller that can invoke `acquire_certificate` — via either acquisition protocol — can forge a certificate attributed to an arbitrary certifier identity key, containing arbitrary fields, and have it persisted as authentic. Applications and downstream gems that rely on the wallet's certificate store as a source of truth for identity attributes (e.g. KYC assertions, role claims, attestations) are subject to credential forgery. This is a credential-forgery primitive, not merely a spec divergence from BRC-52. ## CVSS rationale `AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N` → **8.1 (High)** - **AV:N** — network-reachable in any wallet context that exposes `acquire_certificate` to callers. - **AC:L** — low attack complexity: pass arbitrary bytes as `signature:`. - **PR:L** — low privileges: any caller authorised to invoke `acquire_certificate`. - **UI:N** — no user interaction required. - **C:H** — forged credentials via `prove_certificate` can assert attributes about the subject. - **I:H** — the wallet's credential store is polluted with attacker-controlled data. - **A:N** — availability unaffected. ## Proof of concept ```ruby client = BSV::Wallet::WalletClient.new(key, storage: BSV::Wallet::MemoryStore.new) client.acquire_certificate( type: 'age-over-18', acquisition_protocol: 'direct', certifier: claimed_trusted_pubkey_hex, serial_number: 'any-serial', revocation_outpoint: ('00' * 32) + '.0', signature: 'deadbeef' * 16, # arbitrary bytes — never verified fields: { 'verified' => 'true' }, keyring_for_subject: {} ) client.list_certificates( certifiers: [claimed_trusted_pubkey_hex], types: ['age-over-18'] ) # => returns the forged record as if it were a real certificate from that certifier ``` ## Affected versions The vulnerable direct-path code was introduced in commit `d14dd19` ("feat(wallet): implement BRC-100 identity certificate methods (Phase 5)") on 2026-03-27 20:35 UTC. The vulnerable issuance-path code was added one day later in `6a4d898` ("feat(wallet): implement certificate issuance protocol", 2026-03-28 04:38 UTC), which removed an earlier `raise UnsupportedActionError` and replaced it with an unverified HTTP POST. **`bsv-sdk`:** the v0.3.1 chore bump (`89de3a2`) was committed 28 minutes after `d14dd19`, so the direct-path bypass shipped in the **v0.3.1** tag. The v0.3.1 release raised `UnsupportedActionError` for the issuance path, so the issuance-path bypass first shipped in **v0.3.2** (`5a335de`). Every subsequent release up to and including **v0.8.1** is affected by at least one path, and every release from v0.3.2 onwards is affected by both. Combined affected range: `>= 0.3.1, < 0.8.2`. **`bsv-wallet`:** at the time both commits landed, the wallet gem was at version 0.1.1. The first wallet release containing any of the vulnerable code was **v0.1.2** (`5a335de`, 2026-03-30), which shipped both paths simultaneously. Every subsequent release up to and including **v0.3.3** is affected on both paths. Affected range: `>= 0.1.2, < 0.3.4`. ## Patches Upgrade to `bsv-sdk >= 0.8.2` **and/or** `bsv-wallet >= 0.3.4`. Both releases ship the same fix: a new module `BSV::Wallet::CertificateSignature` (`lib/bsv/wallet_interface/certificate_signature.rb`), which builds the BRC-52 canonical preimage (`type`, `serial_number`, `subject`, `certifier`, `revocation_outpoint`, lexicographically-sorted `fields`) and verifies the certifier's signature against it via `ProtoWallet#verify_signature` with protocol ID `[2, 'certificate signature']` and counterparty = the claimed certifier's public key. Both `acquire_via_direct` and `acquire_via_issuance` now call `CertificateSignature.verify!` before returning the certificate to `acquire_certificate`, so invalid certificates raise `BSV::Wallet::CertificateSignature::InvalidError` (a subclass of `InvalidSignatureError`) and are never written to storage. Consumers should upgrade whichever gem they depend on directly; they do not need both. `bsv-wallet 0.3.4` additionally tightens its dependency on `bsv-sdk` from the stale `~> 0.4` to `>= 0.8.2, < 1.0`, which forces the known-good pairing and pulls in the sibling advisory fixes (F1.3, F5.13) tracked separately. The issuance-path fix also partially closes finding **F8.16** from the same compliance review. F8.16's second aspect — switching the issuance transport from ad-hoc JSON POST to BRC-104 AuthFetch — is not addressed here and remains deferred to a future release. Fixed in sgbett/bsv-ruby-sdk#306. ## Workarounds If upgrading is not immediately possible: - Do not expose `acquire_certificate` (either acquisition protocol) to untrusted callers. - Do not invoke `acquire_certificate` with `acquisition_protocol: 'issuance'` against a certifier URL you do not fully trust, and require TLS for any such request. - Treat any record returned by `list_certificates` / `prove_certificate` as unverified and perform an out-of-band BRC-52 verification against the certifier's public key before acting on it. ## Credit Identified during the 2026-04-08 cross-SDK compliance review, tracked as findings F8.15 (direct path) and F8.16 (issuance path, partial). ## References - HLR: sgbett/bsv-ruby-sdk#305 - Fix PR: sgbett/bsv-ruby-sdk#306 - Compliance review: [`.architecture/reviews/20260408-cross-sdk-compliance-review.md`](../../.architecture/reviews/20260408-cross-sdk-compliance-review.md) - BRC-52 specification: https://brc.dev/52 - TypeScript reference: `Certificate.verify()` in `@bsv/sdk`

Advisory Timeline

  • First published April 9, 2026 (186 days ago).
  • Last revised May 13, 2026 — 34 days after initial publication.

Remediation

Upgrade to the fixed version using your package manager.

Bundler
Update bsv-wallet to 0.3.4 or later
gem install bsv-wallet -v 0.3.4
Bundler
Update bsv-sdk to 0.8.2 or later
gem install bsv-sdk -v 0.8.2

After upgrading, run your dependency scanner again to confirm the vulnerability is resolved.

Affected Packages (2)

Affects 1 ecosystem: rubygems (2).

PackageEcosystemAffectedFixed In
bsv-wallet
rubygems
0.1.2, 0.2.0, 0.2.1, 0.2.2 (+4 more)0.3.4
bsv-sdk
rubygems
0.3.1, 0.3.2, 0.4.0, 0.5.0 (+6 more)0.8.2

Vulnerability Classification

Common Weakness Enumeration (CWE) identifiers for this vulnerability type.

  • CWE-347
    Improper Verification of Cryptographic SignatureMITRE

CVSS Score Breakdown

What the CVSS (Common Vulnerability Scoring System) 8.1 score means for each attack dimension.

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N

Frequently Asked Questions

What is CVE-2026-40070?
bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths) This vulnerability has been assigned a severity rating of HIGH (CVSS score: 8.1/10).
How do I check if my project is affected by CVE-2026-40070?
CVE-2026-40070 affects bsv-wallet and bsv-sdk. Use GeekWala's free vulnerability scanner to check your dependencies against CVE-2026-40070 and 200,000+ other known vulnerabilities.

Severity & Exploitability

CVSS Score
8.1

High exploitability or significant impact. Prioritize remediation within days.

Also Known As

GHSA-hc36-c89j-5f4j

Related CVEs

  • CVE-2026-94462
    HIGH

    Spree: Broken Access Control in `PATCH /api/v3/store/carts/:id/associate` (IDOR)

  • CVE-2026-40069
    HIGH

    bsv-sdk ARC broadcaster treats INVALID/MALFORMED/ORPHAN responses as successful broadcasts

  • CVE-2026-77601
    HIGH

    OpenC3 COSMOS: Authenticated OS command injection via the `pypi_url` setting

Check if you're affected

Scan your dependencies to see if this vulnerability affects your projects.

Scan Your Dependencies