citesig
CiteSig is an open protocol for embedding cryptographic verification signatures inside factual claims — a trust layer for the AI era.
citesig is the reference Python implementation of CiteSig v0.1. It provides sign(), verify(), canonical serialization, and the compact form defined in the specification.
Install
pip install citesig
Requires Python 3.9 or later. Depends on cryptography for Ed25519 primitives.
Quick example
import os
from datetime import datetime, timezone
from citesig import (
sign,
verify,
ed25519,
did_key_from_ed25519_public_key,
)
# Generate a signing key (32-byte seed).
seed = os.urandom(32)
public_key = ed25519.public_key_from_seed(seed)
signer = did_key_from_ed25519_public_key(public_key)
# Sign a claim.
attestation = sign(
{
"claim": "The Great Barrier Reef is approximately 2,300 km long.",
"signer": signer,
"sources": [
{"url": "https://barrierreef.org/the-reef/facts"},
],
"issued_at": datetime.now(timezone.utc).isoformat(timespec="seconds"),
},
seed,
)
# Verify it.
result = verify(attestation)
if result.ok:
print("Signature verified.")
else:
print(f"Rejected: {result.reason} — {result.detail}")
API
sign(claim, private_key_seed) -> dict
Produces a signed CiteSig attestation.
claim— partial attestation mapping.claim,signer,sources,issued_atare required;vdefaults tocitesig/0.1. Extension fields with names containing a colon (e.g.myapp:field) are included in the signed content per spec §3.2.private_key_seed— 32-byte Ed25519 seed.- Returns — a new dict with all input fields plus a populated
sig(base64url-encoded 64-byte Ed25519 signature).
verify(attestation, *, resolve_signer=None) -> VerifyResult
Verifies a CiteSig attestation per spec §5.3.
attestation— a signed attestation mapping.resolve_signer— optional callable(signer: str) -> Iterable[bytes]. Called forhttps://signers. Not called fordid:key:signers, which resolve offline.- Returns — a
VerifyResult(ok, reason=None, detail=None). On failure,reasonis one of:version-unknown—vis notcitesig/0.1field-missing— a REQUIRED field is absentfield-malformed— a field has the wrong type or shapesignature-invalid— signature does not match the signed contentsigner-unresolvable— signer identifier could not be resolved to a public key
Constants live on RejectionReason (e.g. RejectionReason.SIGNATURE_INVALID).
Use verify_async(...) if you need an async resolve_signer.
canonicalize(value) -> bytes
Returns the JCS (RFC 8785) canonicalization of value as UTF-8 bytes. This is the signing input format per spec §4.
to_compact(attestation) -> str
Encodes a signed attestation as its compact form per spec §6:
citesig:0.1:<b64u(claim)>:<b64u(signer)>:<b64u(sourcedigest)>:<b64u(issued_at)>:<b64u(sig)>
where sourcedigest is SHA-256(JCS(sources)). Compact form is lossy for sources — recipients can verify the signature but must obtain the full attestation to enumerate sources.
parse_compact(compact) -> CompactAttestation
Parses a compact-form string into a CompactAttestation dataclass with v, claim, signer, source_digest (32 bytes), issued_at, sig. The original sources list cannot be recovered from a compact-form string.
Utility exports
ed25519.public_key_from_seed(seed) -> bytes— derive the public key from a 32-byte seed.ed25519.sign(message, seed) -> bytes— raw Ed25519 signing (64-byte signature).ed25519.verify(message, signature, public_key) -> bool— raw Ed25519 verification.ed25519_public_key_from_did_key(did) -> bytes— decode adid:key:z...to its 32-byte public key.did_key_from_ed25519_public_key(public_key) -> str— encode a 32-byte public key asdid:key:z....base64url.encode(bytes) -> str/base64url.decode(str) -> bytes— RFC 4648 §5 (no padding).
Conformance
This implementation passes all v0.1 conformance vectors from the specification:
- 3 ACCEPT signature vectors (two sources / empty sources / extension field)
- 3 REJECT signature vectors (claim tampering / unknown version / extension stripping)
- 4 canonicalization vectors (key ordering / whitespace / UTF-8 / empty arrays)
Run the test suite yourself:
git clone https://github.com/citesig/spec
cd spec/impl/py
pip install -e ".[test]"
pytest
The JavaScript reference implementation (@citesig/core on npm) and this Python implementation are byte-for-byte compatible — signatures produced by one verify with the other.
Specification
License
MIT for code (this package). CC-BY-4.0 for specification prose in the main repository.
Security
A valid CiteSig signature does not mean the claim is true. It means the signer identified by signer produced the attestation, and the claim + sources + timestamp + extension fields have not been altered since. Consumers building trust decisions on top of CiteSig must layer signer reputation, source quality, and independent verification on top.
Report vulnerabilities to security@citesig.org (or open an issue at github.com/citesig/spec/issues for non-sensitive reports).
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
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 citesig-0.1.0.tar.gz.
File metadata
- Download URL: citesig-0.1.0.tar.gz
- Upload date:
- Size: 15.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5f451c677a13d6bf2dbf96026b0ec91f00dc2c5a7260463b066371bbcab4a8f
|
|
| MD5 |
e30927cc34ed200b1d5a52039e66d788
|
|
| BLAKE2b-256 |
d23a8263227f7c5474d8f01ac35967c528b9c4c252d8639752975a525bf110cf
|
File details
Details for the file citesig-0.1.0-py3-none-any.whl.
File metadata
- Download URL: citesig-0.1.0-py3-none-any.whl
- Upload date:
- Size: 13.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
86b5a61e7f1c7ce9b72f0c13ba3d366756c7970ff86999b983e3b483c0e88b54
|
|
| MD5 |
d9b3b69ef441da2e807b8c2212c0b843
|
|
| BLAKE2b-256 |
edc3ae45df567f44cc24a15726f30fe2807ec4cac2280f834e205a597325b650
|