Skip to main content

Generic, HSM-friendly Verifiable Credentials core — VC-JWT proofs and DID resolution (key/web) — with an optional read-only EBSI plugin.

Project description

openvc

A small, dependency-light Verifiable Credentials core for Python: sign and verify credentials in both proof formats — VC-JWT (JOSE) and Data Integrity (eddsa-rdfc-2022) — resolve DIDs (did:key, did:web), check status-list revocation, and — via an optional plugin — verify against the EBSI trust registries. Designed so private keys can live behind an HSM/Vault and never enter the process.

It is intentionally not an Open Badges library: openvc is the generic VC machinery that a badge issuer (or an EBSI verifier, or a EUDI wallet backend) builds on. It never imports anything upward.

Why

  • VC-JWT first, HSM-friendly. Signing delegates the raw signature to a SigningKey backend, so a PKCS#11 / Vault Transit key is a drop-in — the private key never has to be in-process. ES256 signatures are the correct JOSE raw R‖S form (the classic reason a locally-produced token fails elsewhere).
  • Safe by construction. The verifier pins an algorithm allow-list (ES256, EdDSA) before any crypto runs, and reconciles the JWT envelope with the embedded credential. The did:web fetch and the EBSI client both guard against SSRF.
  • Version drift, contained. EBSI ships versioned registries whose response shapes change; every version specific lives behind one adapter, so the domain model and trust logic never see wire formats.

Layout

src/openvc/                core — knows nothing about EBSI or badges
    keys.py                Ed25519 (EdDSA) & P-256 (ES256) SigningKey backends
    multibase.py           base58btc multibase + multicodec varint
    proof/vc_jwt.py        VcJwtProofSuite: peek / verify / sign
    proof/data_integrity.py DataIntegrityProofSuite: eddsa-rdfc-2022 (needs pyld)
    proof/contexts/        bundled JSON-LD contexts + offline document loader
    did/base.py            DidDocument, resolver protocol, W3C parser, registry
    did/did_key.py         offline did:key (Ed25519, P-256)
    did/did_web.py         did:web -> https -> fetch (fetch is injected)
    fetch.py               SSRF- + DNS-rebinding-safe https JSON fetch for did:web
    status/                W3C Bitstring Status List (revocation/suspension)
src/openvc_ebsi/           optional EBSI plugin (read-only); depends on openvc only
    http.py                EbsiHttpClient: TTL cache, retries, host allow-list
    versioning.py          DID Registry / TIR version adapters + DidEbsiResolver
    trust.py               recursive TI->TAO->RootTAO trust-chain verification
    verify.py              verify_ebsi_badge: signature + trust + revocation
    models.py              Accreditation, IssuerRecord (version-agnostic domain)

Dependency rule: openvc imports nothing upward. openvc_ebsi depends on openvc, never the reverse.

Install

The PyPI distribution is openvc-core; the Python import package is openvc — so pip install openvc-core, then import openvc.

pip install openvc-core                    # core: VC-JWT, did:key, did:web, status list
pip install "openvc-core[ebsi]"            # + the EBSI registry client (httpx)
pip install "openvc-core[data-integrity]"  # + eddsa-rdfc-2022 Data Integrity (pyld)
pip install -e ".[all]"                    # everything + dev tools (from a checkout)

Quick start

Issue and verify a VC-JWT with an in-process key (swap for an HSM backend in production):

from openvc.keys import P256SigningKey
from openvc.proof.vc_jwt import VcJwtProofSuite

sk = P256SigningKey.generate(kid="did:web:issuer.example#key-1")
suite = VcJwtProofSuite()

credential = {
    "@context": ["https://www.w3.org/2018/credentials/v1"],
    "id": "urn:uuid:...",
    "type": ["VerifiableCredential"],
    "issuer": "did:web:issuer.example",
    "credentialSubject": {"id": "did:key:z6Mk..."},
}
token = suite.sign(credential, signing_key=sk)

verified = suite.verify(token, public_key_jwk=sk.public_jwk())
print(verified.issuer, verified.subject)

Resolve a did:web with the SSRF-guarded fetch, then verify against its key:

from openvc.fetch import default_did_web_resolver

resolver = default_did_web_resolver()          # https-only, blocks private ranges
doc = resolver.resolve("did:web:issuer.example")
vm = doc.key_by_kid("did:web:issuer.example#key-1")
verified = suite.verify(token, public_key_jwk=vm.public_key_jwk)

EBSI (read-only) — resolve a did:ebsi and check issuer trust:

from openvc.proof.vc_jwt import VcJwtProofSuite
from openvc_ebsi.http import for_ebsi
from openvc_ebsi.versioning import DidEbsiResolver
from openvc_ebsi.verify import verify_ebsi_badge

suite = VcJwtProofSuite()
with for_ebsi("pilot") as http:
    resolver = DidEbsiResolver(http.get_json, decode_jwt=suite.peek_claims)
    result = verify_ebsi_badge(token, resolver=resolver, proof_suite=suite,
                               expected_types=["VerifiableAttestation"])
    print(result.trusted, result.issuer)

Status

Alpha. Both proof suites (VC-JWT and eddsa-rdfc-2022 Data Integrity — the latter verified byte-for-byte against the official W3C vc-di-eddsa vector), the key backends, DID resolution (did:key, did:web, did:ebsi read), the EBSI registry client, the recursive TI→TAO→RootTAO trust chain, and W3C Bitstring Status List revocation (with per-hop delegation scoping and revocation of the accreditations themselves) are implemented and tested offline; an opt-in live EBSI smoke test runs against the pilot/conformance environments. See the roadmap for what is next (recorded golden fixtures, Token Status List, ecdsa-sd-2023, PyPI publish).

did:ebsi write/onboarding (JSON-RPC + OID4VP) is out of scope — this is a verifier/issuer library, not a node operator.

Tests

pip install -e ".[all]"
pytest                        # offline: deterministic, no network
OPENVC_EBSI_LIVE=1 pytest     # also the opt-in live EBSI smoke test

Project

License

LGPL-3.0-or-later. Copyright © 2026 Luis González Fernández. See COPYING.LESSER and COPYING.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

openvc_core-0.1.0.tar.gz (61.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

openvc_core-0.1.0-py3-none-any.whl (57.5 kB view details)

Uploaded Python 3

File details

Details for the file openvc_core-0.1.0.tar.gz.

File metadata

  • Download URL: openvc_core-0.1.0.tar.gz
  • Upload date:
  • Size: 61.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openvc_core-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9fef541e0ea29bcf01a8d03e5569fbdcbce1b41bf85e89f5c66a0e1bceba2299
MD5 ad10fc60faff39c3441a07a48c33fb88
BLAKE2b-256 d80cbcd3fe664e441734fadd366264f5171c3045cda8274afe3954d364946c7a

See more details on using hashes here.

Provenance

The following attestation bundles were made for openvc_core-0.1.0.tar.gz:

Publisher: ci.yml on luisgf/openvc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file openvc_core-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: openvc_core-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 57.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for openvc_core-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 1f6977641f4950f0c19e6b94262dfbbdd3015c30557f070ea53c79bb9b937876
MD5 fdf17b2ac6965aacafc9c17141a7d380
BLAKE2b-256 3f815b3a542e65bc3c8061f0ade8e4195a015af815c8c77bbd281e521033a472

See more details on using hashes here.

Provenance

The following attestation bundles were made for openvc_core-0.1.0-py3-none-any.whl:

Publisher: ci.yml on luisgf/openvc

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page