CVE-2026-40070
bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)
BSV Ruby SDK is the Ruby SDK for the BSV blockchain. From 0.3.1 to before 0.8.2, BSV::Wallet::WalletClient#acquire_certificate persists certificate records to storage without verifying the certifier's signature over the certificate contents. In acquisition_protocol: 'direct', the caller supplies all certificate fields (including signature:) and the record is written to storage verbatim. In 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.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
8.1
bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)
BSV Ruby SDK is the Ruby SDK for the BSV blockchain. From 0.3.1 to before 0.8.2, BSV::Wallet::WalletClient#acquire_certificate persists certificate records to storage without verifying the certifier's signature over the certificate contents. In acquisition_protocol: 'direct', the caller supplies all certificate fields (including signature:) and the record is written to storage verbatim. In 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.
- Record
CVE-2026-40070- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-347
- Updated
- 2026-11-02
EUVDEUVD-2026-20996
8.1
BSV Ruby SDK is the Ruby SDK for the BSV blockchain. From 0.3.1 to before 0.8.2, BSV::Wallet::WalletClient#acquire_certificate persists certificate records to storage without verifying the certifier's signature over the certificate contents. In acquisition_protocol: 'direct', the caller supplies all certificate fields (including signature:) and the record is written to storage verbatim. In 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.
- Record
EUVD-2026-20996- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-hc36-c89j-5f4j
8.1
bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)
Unverified certifier signatures persisted by acquire_certificate
Affected packages
Both bsv-sdk and bsv-wallet are published from the 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 (includingsignature:) 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:
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…
- Record
GHSA-hc36-c89j-5f4j- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-347
- Updated
- 2026-11-02
NVD
8.1
BSV Ruby SDK is the Ruby SDK for the BSV blockchain. From 0.3.1 to before 0.8.2, BSV::Wallet::WalletClient#acquire_certificate persists certificate records to storage without verifying the certifier's signature over the certificate contents. In acquisition_protocol: 'direct', the caller supplies all certificate fields (including signature:) and the record is written to storage verbatim. In 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.
- Record
CVE-2026-40070- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-347
- Updated
- 2026-11-02
OSV
8.1
bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)
BSV Ruby SDK is the Ruby SDK for the BSV blockchain. From 0.3.1 to before 0.8.2, BSV::Wallet::WalletClient#acquire_certificate persists certificate records to storage without verifying the certifier's signature over the certificate contents. In acquisition_protocol: 'direct', the caller supplies all certificate fields (including signature:) and the record is written to storage verbatim. In 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.
- Record
CVE-2026-40070- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-347
- Updated
- 2026-11-02
OSVGHSA-hc36-c89j-5f4j
8.1
bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)
Unverified certifier signatures persisted by acquire_certificate
Affected packages
Both bsv-sdk and bsv-wallet are published from the 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 (includingsignature:) 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:
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…
- Record
GHSA-hc36-c89j-5f4j- CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-347
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
bsv-sdkRubyGems |
0.3.1 up to 0.8.2 | 0.8.2 |
bsv-walletRubyGems |
0.1.2 up to 0.3.4 | 0.3.4 |
bsv-sdkRubyGems |
0.3.1 up to 0.8.2, plus listed versions 0.3.20.4.00.5.00.6.00.6.1 Show 4 moreShow less0.6.20.7.00.8.00.8.1 |
0.8.2 |
bsv-walletRubyGems |
0.1.2 up to 0.3.4, plus listed versions 0.2.00.2.10.2.20.3.00.3.10.3.20.3.3 |
0.3.4 |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-40070 is a high vulnerability (CVSS 8.1) in Sgbett Bsv-sdk, published April 9, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.14%, the 3rd percentile. Fixed in bsv-sdk 0.8.2.
References
github.com/sgbett/bsv-ruby-sdk/security/advisories/GHSA-hc36-c89j-5f4j
github.com/sgbett/bsv-ruby-sdk/issues/305
github.com/sgbett/bsv-ruby-sdk/pull/306
github.com/sgbett/bsv-ruby-sdk/commit/4992e8a265fd914a7eeb0405c69d1ff0122a84cc
brc.dev/52
nvd.nist.gov/vuln/detail/CVE-2026-40070
Show 5 moreShow less
github.com/rubysec/ruby-advisory-db/blob/master/gems/bsv-sdk/CVE-2026-40070.yml
github.com/rubysec/ruby-advisory-db/blob/master/gems/bsv-wallet/CVE-2026-40070.yml
github.com/advisories/GHSA-hc36-c89j-5f4j
github.com/CVEProject/cvelistV5/tree/main/cves/2026/40xxx/CVE-2026-40070.json
github.com/sgbett/bsv-ruby-sdk
Left out here: the rest of 2 source descriptions. The JSON record has them.