| 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. Implements the Attestix custom canonical JSON format with 'NFC' Unicode normalization, rather than strict RFC 8785, for the supported verification profile. Useful for applications that check signed AI-agent evidence in R. Cryptographic verification does not establish issuer trust, fresh external revocation status, or the truth or legal validity of credential claims. 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.2 |
| Built: | 2026-10-30 16:36:21 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)