Apr 10, 2026·5 min read·2 visits
A missing signature verification check in the BRC-52 certificate acquisition API allows attackers to inject arbitrary forged identity attributes into the wallet storage, subverting downstream identity proofs.
The bsv-ruby-sdk and bsv-wallet RubyGems fail to cryptographically verify BRC-52 identity certificate signatures, allowing attackers to forge trusted identity credentials.
CVE-2026-40070 is a high-severity vulnerability affecting the bsv-ruby-sdk and bsv-wallet RubyGems. The flaw resides within the BSV::Wallet::WalletClient#acquire_certificate method, which is responsible for obtaining and persisting BRC-52 Identity Certificates. These certificates assert attributes about a subject, such as age or identity, and rely on cryptographic signatures from a trusted certifier. The vulnerability allows an attacker to bypass these trust mechanisms entirely.
The core issue is categorized as CWE-347: Improper Verification of Cryptographic Signature. Prior to the patched versions, the SDK lacked any verification of the certifier's signature before persisting the certificate data. This omission means the application blindly trusts the provided data, leading to a complete breakdown of the BRC-52 identity model.
Applications relying on these gems for selective-disclosure proofs or identity verification are exposed to credential forgery. Attackers can inject fraudulent credentials that the wallet subsequently treats as authentic. This subverts the fundamental security guarantees of the BRC-52 standard.
The BRC-52 standard mandates that identity certificates must be signed by a trusted public key, known as the certifier. This signature is computed over a canonical binary serialization of the certificate's data, which includes the type, subject identity key, serial number, and custom fields. Proper implementation requires verifying this signature against the certifier's public key before accepting the certificate.
The vulnerability exists in two distinct acquisition paths within the bsv-ruby-sdk: the direct path (acquire_via_direct) and the issuance path (acquire_via_issuance). In the direct path, the caller provides all certificate fields, including the signature, directly to the API. In the issuance path, the client fetches the certificate via an HTTP POST request to a certifier-supplied URL.
In both paths, the SDK constructed a record and persisted it directly to the wallet's storage adapter. The code executed no cryptographic verification of the signature field. Downstream methods, specifically list_certificates and prove_certificate, operate under the assumption that any record in the persistent storage was validated upon entry. This architectural disjoint allows unverified data to be utilized in secure operations.
The vulnerability was introduced in commit d14dd19 during the implementation of BRC-100 and expanded in commit 6a4d898 with the issuance protocol. The pre-patch implementation constructed the certificate hash and returned it for persistence without any signature validation step.
# Vulnerable pre-patch implementation
def acquire_via_direct(args)
cert = construct_certificate(args)
# Flaw: Certificate is returned and persisted without verification
cert
endThe patch, applied in commit 4992e8a265fd914a7eeb0405c69d1ff0122a84cc, implements the missing verification logic by introducing the BSV::Wallet::CertificateSignature module. This module enforces BRC-52 compliant verification immediately upon certificate acquisition.
# Post-patch implementation
def acquire_via_direct(args)
cert = construct_certificate(args)
# Fix: Mandatory verification before return/storage
CertificateSignature.verify!(cert)
cert
endThe new verify! method constructs the canonical preimage from the certificate fields, ensuring lexicographical sorting of custom fields. It utilizes ProtoWallet#verify_signature with a domain-separated protocol ID [2, 'certificate signature']. Furthermore, the implementation uses Base64.strict_decode64 to parse the signature, mitigating potential preimage malleability attacks that rely on whitespace injection.
Exploitation of CVE-2026-40070 requires low attack complexity and no user interaction. An attacker needs authorization to invoke the acquire_certificate API on the vulnerable wallet client. The attacker constructs a payload containing arbitrary identity attributes and provides an invalid or arbitrary signature string.
The following proof-of-concept demonstrates the direct acquisition attack vector. The attacker supplies a payload claiming an age-over-18 certification from a trusted certifier. They provide a static hex string deadbeef for the signature field, which the application fails to validate.
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,
fields: { 'verified' => 'true' },
keyring_for_subject: {}
)Because the SDK does not validate the signature, the record is stored permanently. When the application subsequently calls client.list_certificates, the SDK returns the forged record as an authentic identity credential. The attacker successfully injects a trusted attribute without possessing the certifier's private key.
The vulnerability carries a CVSS 3.1 base score of 8.1 (High), reflecting severe impacts on confidentiality and integrity. The attack vector is network-based, as the vulnerable API paths process data supplied externally or fetched from external endpoints.
The primary consequence is the total subversion of the BRC-52 identity trust model. Attackers can forge arbitrary credentials, including KYC verifications, age attestations, or administrative role assignments. These forged credentials allow attackers to assert false attributes to downstream systems relying on the wallet's proof mechanisms.
The integrity of the wallet's internal credential store is compromised, as it becomes polluted with attacker-controlled data. While there is no direct availability impact, the compromise of trust anchors fundamentally undermines the security posture of any application utilizing the bsv-ruby-sdk for identity management.
The primary remediation strategy is upgrading the affected libraries. Users must upgrade bsv-sdk to version 0.8.2 or later, and bsv-wallet to version 0.3.4 or later. The bsv-wallet 0.3.4 release enforces a tightened dependency on bsv-sdk >= 0.8.2 to ensure all related cryptographic fixes are applied uniformly.
If immediate patching is not technically feasible, administrators can implement specific workarounds. Access to the acquire_certificate API must be strictly limited to fully trusted internal systems, preventing malicious external input from reaching the vulnerable code path.
Developers can implement manual BRC-52 verification logic within the application layer. This involves validating the signature of any stored certificate before utilizing its attributes in business logic. Additionally, ensuring all certifier issuance requests are performed over TLS with strict certificate validation reduces the risk of in-transit manipulation.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
bsv-sdk sgbett | >= 0.3.1, < 0.8.2 | 0.8.2 |
bsv-wallet sgbett | >= 0.1.2, < 0.3.4 | 0.3.4 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-347 |
| CVSS Score | 8.1 |
| Attack Vector | Network |
| Impact | High Integrity, High Confidentiality |
| Exploit Maturity | Proof-of-Concept |
| KEV Listed | False |
Improper Verification of Cryptographic Signature