A library to sign and verify OpenBadges
Project description
OpenBadgesLib
=============
A Python library for signing and verifying `OpenBadges`_ assertions
embedded in SVG and PNG image files. Supports both **OpenBadges 2.0**
(JWS compact serialisation) and **OpenBadges 3.0** (W3C Verifiable
Credentials / JWT-VC).
.. _OpenBadges: https://www.imsglobal.org/activity/digital-badges
Features
--------
* Sign badge images (SVG and PNG) with a JWS assertion (OB 2.0)
* Issue and verify OpenBadges 3.0 JWT-VC credentials
* Bake OB 3.0 JWT tokens into SVG and PNG badge images
* RSA 2048-bit (RS256) and ECC NIST P-256 (ES256) key support
* SHA-256 hashed recipient identity with salt (OB 2.0)
* Expiration and revocation checking
* Command-line wrapper tools included
Requirements
------------
* Python >= 3.10
* pycryptodome >= 3.20
* ecdsa >= 0.19
* pypng >= 0.20220715.0
* PyJWT[crypto] >= 2.8
Installation
------------
::
pip install openbadgeslib
All dependencies are installed automatically.
To install in development mode with the test suite::
pip install -e ".[dev]"
Quick Start
-----------
**1. Initialize a configuration directory**::
openbadges-init ./config/
**2. Generate a key pair for a badge**::
openbadges-keygenerator -c ./config/config.ini -g 1
**3. Sign a badge**::
# OpenBadges 2.0 (default)
openbadges-signer -c ./config/config.ini -b 1 -r recipient@example.com -o /tmp/
# OpenBadges 3.0
openbadges-signer -c ./config/config.ini -b 1 -r recipient@example.com -o /tmp/ -V 3
**4. Verify a signed badge**::
# OpenBadges 2.0 (default)
openbadges-verifier -i /tmp/badge_1_recipient@example.com.svg -r recipient@example.com
# OpenBadges 3.0 (supply public key directly)
openbadges-verifier -i /tmp/badge_1_recipient@example.com.svg -r recipient@example.com \
-V 3 -k ./config/keys/verify_rsa_key_1.pem
Using the library directly
--------------------------
::
from openbadgeslib.badge import Badge, BadgeImgType
from openbadgeslib.keys import KeyType
from openbadgeslib.signer import Signer
from openbadgeslib.badge import BadgeType
# Load key material
with open('sign.pem', 'rb') as f:
priv_pem = f.read()
with open('verify.pem', 'rb') as f:
pub_pem = f.read()
# Build a Badge descriptor
badge = Badge(
ini_name='my_badge',
name='My Badge',
description='Awarded for excellence',
image_type=BadgeImgType.SVG,
image=open('badge.svg', 'rb').read(),
image_url='https://example.com/badge.svg',
criteria_url='https://example.com/criteria.html',
json_url='https://example.com/badge.json',
verify_key_url='https://example.com/verify.pem',
key_type=KeyType.RSA,
privkey_pem=priv_pem,
pubkey_pem=pub_pem,
)
# Sign
signer = Signer(identity='recipient@example.com', badge_type=BadgeType.SIGNED)
signed = signer.sign_badge(badge)
signed.save_to_file('/tmp/signed_badge.svg')
OpenBadges 3.0 (JWT-VC)
-----------------------
::
from openbadgeslib.ob3 import (
Issuer, Achievement, OpenBadgeCredential,
OB3Signer, OB3Verifier,
)
# Build the credential data model
issuer = Issuer(id='https://example.com/issuer', name='Example Org')
achievement = Achievement(
id='https://example.com/achievements/python',
name='Python Developer',
description='Awarded for Python proficiency',
criteria_narrative='Must pass the Python assessment',
)
credential = OpenBadgeCredential(
issuer=issuer,
recipient_id='mailto:recipient@example.com',
achievement=achievement,
)
# Sign — returns a JWT-VC string
with open('sign.pem', 'rb') as f:
priv_pem = f.read()
signer = OB3Signer(privkey_pem=priv_pem, algorithm='RS256')
token = signer.sign(credential)
# Bake the token into a badge image
with open('badge.svg', 'rb') as f:
svg_bytes = f.read()
baked_svg = signer.sign_into_svg(credential, svg_bytes)
# Verify
with open('verify.pem', 'rb') as f:
pub_pem = f.read()
verifier = OB3Verifier(pubkey_pem=pub_pem)
extracted_token = OB3Verifier.extract_token_from_svg(baked_svg)
restored_credential = verifier.verify(extracted_token)
print('Recipient:', restored_credential.recipient_id)
Running the test suite
----------------------
::
pytest
pytest --cov=openbadgeslib # with coverage report
Documentation
-------------
Full documentation is in the ``docs/`` directory (Sphinx RST sources).
Build the HTML docs::
pip install sphinx sphinx-rtd-theme
sphinx-build -b html docs/ docs/_build/html/
Changelog
---------
**v1.1.1** (2026-06-27)
* **OB3 fixes** — recipient identifiers are normalised through one shared
helper (a DID is no longer corrupted into ``mailto:did:...``); ``verify()``
cross-checks the JWT ``iss``/``sub`` claims against the credential and
``OB3VerificationError`` now inherits from ``LibOpenBadgesException``
* **Refactor** — shared ``alg_for_key_type`` and CLI config/key helpers,
first-party code imports ``openbadgeslib.ob2`` directly, and the oversized
verify/sign functions were decomposed
* **Type hints** added on the OB2/core byte-vs-str boundaries
* **Tests & CI** — OB2 signer CLI, mail and ``read_from_file`` edges covered
(258 tests, 92 % coverage; repo is flake8-clean); a GitHub Actions workflow
lints and tests on Python 3.10-3.13
**v1.1.0** (2026-06-27)
* **Security — algorithm pinning** — verification now pins the accepted
signature algorithm to the key's type (RS*/ES*) instead of trusting the
token header, blocking cross-type confusion and any ``none``/HMAC downgrade
* **Security — hardened parsing** — SVG is parsed with ``defusedxml`` (defusing
billion-laughs DoS) and compressed PNG ``iTXt`` tokens are inflated with a
256 KB cap (defusing decompression bombs); adds a ``defusedxml`` dependency
* **Security — OB2 CLI trust** — the verifier only reports ``[+] Signature is
correct`` when a trusted key is supplied (``--local``/``--pubkey``);
otherwise it warns that the result is internally consistent only
* **Bug fixes** — base64url padding, ``get_serial_num`` on file-loaded badges,
``check_identity`` with no identity, SVG-parse error masking, and loud
failures for unknown key/image types
* **Refactor** — one ``keys.key_to_pem`` and a shared ``openbadgeslib.baking``
module remove the OB2/OB3 duplication (key conversion and SVG/PNG carrier)
* **Cleanup** — dropped ``setup.py``, stale ``dist/`` artifacts, ``MANIFEST``
and dead exceptions; OB3 documented on the landing page; SMTP feature
documented; 236 tests, 87 % line coverage
**v1.0.2** (2026-06-18)
* **Security** — OB2 verification uses the operator-supplied trusted key when
provided (a forged badge can no longer self-describe its verify key); and
``download_file`` rejects non-HTTPS URLs by default (the verify key is the
root of trust)
* **Expiration fix** — badges are now considered expired relative to the
current time, not relative to their own issue date
* **CLI fixes** — ``openbadges-publish`` now publishes every badge with its
verify key; ``openbadges-keygenerator`` honours a ``key_type`` (RSA/ECC) field
in the badge profile; SMTP ``use_ssl`` is parsed as a boolean and mail
connection errors no longer crash a successful sign
* **OB 3.0** — credentials use the W3C VC 2.0 data model
(``validFrom``/``validUntil``); ``OB3Verifier.verify()`` gained an optional
``expected_recipient`` argument and asserts the credential type; PNG token
extraction parses the iTXt chunk properly
* Logs are appended (no longer truncated on every run); expanded test coverage
of the CLI tools, publishing, mail, and revocation
**v1.0.1** (2026-04-22)
* **OpenBadges 3.0 support** — new ``openbadgeslib.ob3`` subpackage:
``OpenBadgeCredential``, ``Issuer``, ``Achievement`` data classes;
``OB3Signer`` (JWT-VC signing + SVG/PNG baking); ``OB3Verifier``
(JWT-VC verification + token extraction from SVG/PNG)
* **OpenBadges 2.0 subpackage** — OB2 implementation moved to
``openbadgeslib.ob2``; top-level modules kept as backward-compatible
shims so existing code requires no changes
* **``--ob-version`` flag** — all four CLI tools (keygenerator, signer,
verifier, publish) accept ``-V {2,3}`` to select the specification
version (default: ``2``)
* **``openbadges-verifier --pubkey``** — new ``-k FILE`` option to supply
the PEM public key directly for OB3 verification
* Python 3.10+ compatibility: removed distutils, migrated packaging to
``pyproject.toml`` with ``setuptools.build_meta``
* Replaced abandoned ``pycrypto`` with ``pycryptodome >= 3.20``
* Replaced custom JWS engine (``3dparty/jws/``) with ``PyJWT[crypto]``
algorithm classes (RS256/384/512, ES256/384/512); old ``3dparty/``
directory removed
* Fixed TLS: removed deprecated ``PROTOCOL_TLSv1`` / ``CERT_NONE``;
``download_file`` now uses the system default TLS context
* Updated pypng API: renamed ``signature`` constant, bytes chunk tags
* Copyright year range updated to 2014-2026 across all source files
* Fixed verifier logic bug: ``check_jws_signature`` return value was
compared with ``BadgeStatus`` via identity check, always evaluating True
* Added 203 unit tests, 89% line coverage
**v0.4.2** and earlier
* See git history at https://github.com/luisgf/openbadgeslib
License
-------
The library is licensed under the `GNU Lesser General Public License v3`_
(LGPLv3). The command-line wrapper tools are licensed under the
`BSD 2-Clause`_ license.
.. _GNU Lesser General Public License v3: https://opensource.org/licenses/lgpl-3.0.html
.. _BSD 2-Clause: https://opensource.org/licenses/BSD-2-Clause
Authors
-------
* Luis González Fernández <luisgf@luisgf.es>
* Jesús Cea Avión <jcea@jcea.es>
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
openbadgeslib-1.1.1.tar.gz
(103.9 kB
view details)
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file openbadgeslib-1.1.1.tar.gz.
File metadata
- Download URL: openbadgeslib-1.1.1.tar.gz
- Upload date:
- Size: 103.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
14d1ac41a01dddc47478510a0dca77af588e600269803f58dd788184198af95d
|
|
| MD5 |
82264389893f5a44695dddedabde0c61
|
|
| BLAKE2b-256 |
edf7c4c17803789afc84ee5be925d0542d9cadc700841791bc8c56e82c11ee59
|
File details
Details for the file openbadgeslib-1.1.1-py3-none-any.whl.
File metadata
- Download URL: openbadgeslib-1.1.1-py3-none-any.whl
- Upload date:
- Size: 53.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
be3b3bfe89e6e274103597379054c600d1fdbb3d1ef4baf692e41ed90300f3b4
|
|
| MD5 |
65ac1a5ef9381048c6c780e328d187aa
|
|
| BLAKE2b-256 |
958579408a7c99ec49fc501a94be95eef1011ac77d96848cc0095958b39a5a25
|