| Title: | Offline Verifier for Attestix Ed25519 Credentials and UCAN Delegations |
|---|---|
| Description: | An offline verifier for verifiable credentials and delegation chains issued by the Attestix Python core. Verifies Ed25519 (RFC 8032) signatures over W3C Verifiable Credentials, decodes Ed25519 'did:key' identifiers, and verifies UCAN delegation chains (EdDSA 'JWT's) including capability attenuation, with no Python runtime required. Reproduces the Attestix JCS-style JSON canonical form (a practical subset of RFC 8785 that additionally applies 'NFC' Unicode normalization) byte-for-byte so that signatures produced by the reference implementation verify here. Useful for compliance, research and biostatistics users who work in R and need to check AI-agent compliance credentials. See <https://attestix.io> for the project and <https://attestix.io/spec/bundle/v1> for the bundle wire format. |
| Authors: | Pavan Kumar Dubasi [aut, cre], VibeTensor [cph, fnd] |
| Maintainer: | Pavan Kumar Dubasi <[email protected]> |
| License: | Apache License (>= 2) |
| Version: | 0.4.1 |
| Built: | 2026-07-05 08:19:20 UTC |
| Source: | https://github.com/vibetensor/attestix-r |
Reproduces attestix/auth/crypto.py::canonicalize_json byte-for-byte.
atx_canonicalize(obj)atx_canonicalize(obj)
obj |
A JSON string, raw UTF-8 bytes, or a value tree produced by
|
This is JCS-style, NOT strict RFC 8785. The two load-bearing divergences from RFC 8785 are:
NFC Unicode normalization is applied to every string value and every object key (RFC 8785 does not normalize).
Whole-number floats collapse to integers (1.0 -> 1); non-whole
floats use Python's repr (the vectors only use integers and
1.5, on which every port agrees).
Keys are sorted by Unicode code point, separators are "," and
":" with no whitespace, output is raw UTF-8 (no \uXXXX
escapes), and large integers (> 2^53) are preserved exactly.
A raw vector of the canonical UTF-8 bytes.
bytes <- atx_canonicalize('{"b":2,"a":1}') rawToChar(bytes) # {"a":1,"b":2}bytes <- atx_canonicalize('{"b":2,"a":1}') rawToChar(bytes) # {"a":1,"b":2}
did:key to its raw 32-byte public keyReproduces attestix/auth/crypto.py::did_key_to_public_key: strip the
did:key:z prefix, base58btc-decode the multibase payload, assert the
first two bytes are the 0xed 0x01 multicodec prefix, and return the
remaining 32 raw bytes.
atx_decode_did_key(did)atx_decode_did_key(did)
did |
A |
A raw vector of length 32 (the Ed25519 public key).
did <- "did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev" length(atx_decode_did_key(did)) # 32did <- "did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev" length(atx_decode_did_key(did)) # 32
did:key
The verification method is <did>#<multibase> where <multibase>
is the z... portion of the did:key. This returns that fragment with a
leading #.
atx_did_key_fragment(did)atx_did_key_fragment(did)
did |
A |
The #z... fragment.
Thin wrapper over sodium::sig_verify (libsodium). Returns a logical
rather than signalling, so callers can fold it into a structured result.
atx_ed25519_verify(message, signature, public_key)atx_ed25519_verify(message, signature, public_key)
message |
Raw vector of the signed message bytes. |
signature |
Raw vector, 64 bytes. |
public_key |
Raw vector, 32 bytes. |
TRUE if the signature is valid, else FALSE.
## Not run: atx_ed25519_verify(msg, sig, pubkey) ## End(Not run)## Not run: atx_ed25519_verify(msg, sig, pubkey) ## End(Not run)
Convenience alias for atx_json_parse.
atx_parse_json(txt)atx_parse_json(txt)
txt |
JSON text or raw UTF-8 bytes. |
The parsed value tree.
did:key
Inverse of atx_decode_did_key.
atx_public_key_to_did_key(pubkey)atx_public_key_to_did_key(pubkey)
pubkey |
A raw vector of length 32. |
A did:key:z... string.
did:key (<did>#<multibase>).Full verification method for a did:key (<did>#<multibase>).
atx_verification_method(did)atx_verification_method(did)
did |
A |
The verification-method string.
Mirrors attestix/services/credential_service.py::verify_credential.
The signing payload is the credential with the proof and
credentialStatus top-level keys removed; that payload is JCS-style
canonicalized (see atx_canonicalize) and the Ed25519 signature
in proof.proofValue is verified against the issuer public key.
atx_verify_credential(vc, public_key = NULL, now = Sys.time())atx_verify_credential(vc, public_key = NULL, now = Sys.time())
vc |
The credential as a JSON string, raw UTF-8 bytes, or a value tree
from |
public_key |
The issuer Ed25519 public key. Accepts a raw 32-byte
vector, a 64-char hex string, or |
now |
The reference time for the expiry check, as a
|
Three independent checks are ANDed:
signature_valid - Ed25519 verification of the canonical bytes.
not_expired - now < expirationDate (tz-aware ISO-8601).
not_revoked - credentialStatus$revoked is falsy.
A list with logical fields signature_valid,
not_expired, not_revoked, structure_valid, and the
overall verify.
## Not run: res <- atx_verify_credential(vc_json) isTRUE(res$verify) ## End(Not run)## Not run: res <- atx_verify_credential(vc_json) isTRUE(res$verify) ## End(Not run)
Mirrors the recursive prf-chain verification in
attestix/services/delegation_service.py. Each token in the chain is a
PyJWT EdDSA JWT; the signed message is the compact header.payload
form (base64url unpadded), NOT the JCS canonical form.
atx_verify_delegation_chain( chain, public_key, now = Sys.time(), revoked_jti = character(0) )atx_verify_delegation_chain( chain, public_key, now = Sys.time(), revoked_jti = character(0) )
chain |
A list describing the chain. Accepts the conformance-vector
shape: |
public_key |
The Ed25519 server public key (raw 32 bytes or hex). |
now |
Reference time. Defaults to |
revoked_jti |
Optional revoked |
The chain verifies iff every token's signature is valid AND every token is
unexpired and unrevoked AND each child's att is a subset of
its parent's att (capability attenuation; escalation is rejected).
Cycles (a repeated jti) are rejected.
A list with parent_signature_valid,
child_signature_valid, attenuation_is_subset, and the overall
verify.
## Not run: atx_verify_delegation_chain(list(parent_token = pt, token = ct), pk) ## End(Not run)## Not run: atx_verify_delegation_chain(list(parent_token = pt, token = ct), pk) ## End(Not run)