Skip to content

VibeTensor/attestix-java

Attestix Java Verifier

Offline verifier for Attestix-issued credentials, in pure Java. Verify Ed25519 W3C Verifiable Credentials and UCAN delegation chains that were issued by the Attestix Python core — with no Python runtime needed.

This is a language port of the Attestix verifier. It reproduces the Attestix canonical form byte-for-byte and is validated against the shared cross-language conformance vectors (spec/verify/v1/vectors.json in the parent repo, vendored here under testdata/ and src/test/resources/).

This port is verifier-only: it verifies credentials and delegation chains; it does not issue them. Issuance stays in the Attestix Python core (pip install attestix) or Attestix Cloud.

Canonical form note. Attestix uses a JCS-style canonical form, not strict RFC 8785. The load-bearing divergence: Attestix applies Unicode NFC normalization to every string value and object key (RFC 8785 does not), and whole-number floats collapse to integers (1.01). See CANONICAL.md for the full rules.

Install

Maven (once published to Maven Central):

<dependency>
  <groupId>io.vibetensor</groupId>
  <artifactId>attestix</artifactId>
  <version>0.4.0</version>
</dependency>

Gradle (Kotlin DSL):

implementation("io.vibetensor:attestix:0.4.0")

Requires Java 11+ at runtime. (CI builds and tests on Temurin 17.)

Verify a credential (10 lines)

import io.vibetensor.attestix.*;
import java.time.Instant;

// The issuer's did:key (published out of band, e.g. in the agent card).
byte[] issuerKey = DidKey.decode("did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev");

String vcJson = /* the credential JSON, e.g. read from a file */ "...";
VerificationResult r = Attestix.verifyCredential(vcJson, issuerKey, Instant.now());

System.out.println("signature valid: " + r.signatureValid());
System.out.println("not expired:     " + r.notExpired());
System.out.println("not revoked:     " + r.notRevoked());
System.out.println("overall verify:  " + r.verify());   // AND of all checks

Verify a delegation chain

byte[] serverKey = DidKey.decode("did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev");
DelegationResult d = Attestix.verifyDelegationChain(parentJwt, childJwt, serverKey, Instant.now());
// d.verify() == every JWT signature valid AND child att ⊆ parent att AND not expired

UCAN delegations are EdDSA JWTs (compact form), not JCS-signed. Only alg=EdDSA is accepted (alg:none is rejected). Capability attenuation is enforced: a child's capabilities must be a subset of its parent's.

Decode a did:key

byte[] rawEd25519 = Attestix.decodeDidKey("did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev");
// rawEd25519 is the 32-byte public key (multibase base58btc + 0xed01 multicodec prefix)

Canonicalize JSON (the Attestix way)

byte[] canonical = Attestix.canonicalize("{\"b\":2,\"a\":1}");
// -> bytes of {"a":1,"b":2}  (keys sorted by code point, no whitespace, raw UTF-8, NFC)

Public API

Method Purpose
Attestix.verifyCredential(vcJson, issuerKey, now) Verify a W3C VC → VerificationResult
Attestix.verifyDelegationChain(parent, child, serverKey, now) Verify a UCAN chain → DelegationResult
Attestix.canonicalize(json) JCS-style canonical UTF-8 bytes
Attestix.decodeDidKey(did) did:key:z… → raw 32-byte Ed25519 key

VerificationResult exposes signatureValid(), notExpired(), notRevoked(), structureValid(), and verify().

Build & test

./gradlew test     # runs the conformance vectors
./gradlew build    # jar + sources + javadoc

CI (.github/workflows/test.yml) builds and runs the vectors on Temurin 17 with SHA-pinned actions.

Conformance

Every vector in testdata/vectors.json is asserted by src/test/java/io/vibetensor/attestix/ConformanceVectorsTest.java:

Vector What it checks
canon-001 JCS-style canonical bytes (NFC, sorted keys, whole-float→int, big int)
didkey-001 did:key decode (0xed01 multicodec, 32-byte key) + round-trip
vc-valid-001 Valid VC → verify true
vc-tampered-001 Tampered claim → signature invalid → verify false
vc-expired-001 Expired VC → not_expired false → verify false
ucan-chain-valid-001 Delegation chain, child att ⊆ parent att → verify true
ucan-chain-escalation-002 Privilege escalation (admin not in parent) → verify false

Releasing to Maven Central

This build is publish-ready (POM metadata, sources + javadoc jars, GPG signing wired) but is not auto-published. To cut a release:

  1. Configure Sonatype OSSRH / Central Portal credentials (OSSRH_USERNAME, OSSRH_PASSWORD) and a GPG key (SIGNING_KEY, SIGNING_PASSWORD) as environment variables or Gradle properties.
  2. ./gradlew publish to stage to OSSRH.
  3. Close & release the staging repository (or use the Central Portal UI).

No tokens are committed; publishing is a manual, credentialed step.

License

Apache-2.0. See NOTICE.

About

Attestix offline credential verifier for Java — verify Ed25519 W3C VCs + UCAN delegations issued by the Attestix Python core, no Python runtime needed.

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages