Documentation
¶
Overview ¶
Package attestix is an offline verifier for credentials and delegations issued by the Attestix Python core (https://github.com/VibeTensor/attestix).
It verifies, with no Python runtime and no network, the three artefact types Attestix produces:
- W3C Verifiable Credentials with Ed25519 proofs (VerifyCredential)
- did:key Ed25519 identities (DecodeDidKey)
- UCAN delegation chains as EdDSA JWTs (VerifyDelegationChain)
The crux is the canonical form. Attestix signs over a JCS-STYLE canonicalization that is deliberately NOT strict RFC 8785: it applies Unicode NFC normalization to every string value and object key, sorts keys by code point, emits raw UTF-8 (no \uXXXX escapes), collapses whole-number floats to integers, and preserves integers at arbitrary precision. Canonicalize reproduces that form byte-for-byte; the package is validated against the shared cross-language conformance vectors (testdata/vectors.json).
Index ¶
- func Canonicalize(raw []byte) ([]byte, error)
- func CanonicalizeValue(v interface{}) ([]byte, error)
- func DecodeDidKey(did string) (ed25519.PublicKey, error)
- func DecodeMultibaseKey(mb string) (ed25519.PublicKey, error)
- func DidKeyMultibase(did string) (string, error)
- func EncodeDidKey(pub ed25519.PublicKey) (string, error)
- func VerificationMethod(did string) (string, error)
- func VerifyChainFull(token string, pub ed25519.PublicKey, now time.Time) (bool, error)
- type CredentialResult
- type DelegationClaims
- type DelegationResult
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Canonicalize ¶
Canonicalize produces the Attestix JCS-style canonical UTF-8 bytes for an arbitrary JSON document supplied as raw bytes.
This is NOT strict RFC 8785. It reproduces, byte-for-byte, the output of the reference implementation attestix/auth/crypto.py::canonicalize_json (attestix 0.4.0). The rules:
- Every string VALUE and every object KEY is NFC-normalized.
- Object keys are sorted ascending by Unicode code point.
- Separators are "," and ":" with no whitespace.
- Non-ASCII characters are emitted as raw UTF-8, never \uXXXX.
- Whole-number floats collapse to integers (1.0 -> 1); a literal that has no fraction/exponent is an int and is preserved verbatim, including values larger than 2^53 (arbitrary precision via math/big).
- Non-whole numbers keep their literal form (the vectors only use 1.5).
- true / false / null are the lowercase JSON literals.
func CanonicalizeValue ¶
CanonicalizeValue canonicalizes an already-decoded value. The value must use the decoder's UseNumber() representation (json.Number for numbers) so that integer precision and literal form are preserved. This is exposed so callers that already hold a parsed document (e.g. a VC with fields removed) can canonicalize without re-marshalling.
func DecodeDidKey ¶
DecodeDidKey decodes an Ed25519 did:key into its raw 32-byte public key.
did:key form: "did:key:z" + base58btc(0xed 0x01 || raw32). The leading "z" is the multibase code for base58btc.
Example ¶
ExampleDecodeDidKey resolves an Ed25519 public key from a did:key.
package main
import (
"fmt"
attestix "github.com/VibeTensor/attestix-go"
)
func main() {
pub, err := attestix.DecodeDidKey("did:key:z6Mko5TBPGKHkCxSgmf3aC6p6SGj2auwCfRmBydXJFEwL4ev")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("%x\n", pub)
}
Output: 8022fe847be6106443a4030d74d390c8d9a91319b9f51526bc7c3d88a27c9b7b
func DecodeMultibaseKey ¶
DecodeMultibaseKey decodes a "z..." base58btc multibase string carrying the 0xed01 multicodec prefix into the raw 32-byte Ed25519 public key.
func DidKeyMultibase ¶
DidKeyMultibase returns the multibase portion (the "z..." string) of a did:key, i.e. everything after the "did:key:" prefix. The verificationMethod fragment for a did:key is "#" + this value.
func EncodeDidKey ¶
EncodeDidKey is the inverse of DecodeDidKey: it renders a raw Ed25519 public key as a did:key string. Provided for round-trip testing and issuance tools.
func VerificationMethod ¶
VerificationMethod returns the canonical verificationMethod identifier for a did:key, "<did>#<multibase>".
func VerifyChainFull ¶
VerifyChainFull recursively verifies a UCAN token and its entire prf ancestor chain against pub: every token's EdDSA signature must verify, none may be expired (exp claim, compared against now), and the chain must contain no cycles (a repeated jti). Capability attenuation is asserted at each link: each token's att must be a subset of every parent it references in prf.
Types ¶
type CredentialResult ¶
type CredentialResult struct {
// SignatureValid is true when proof.proofValue is a valid Ed25519 signature
// over the JCS-canonical bytes of the VC with proof + credentialStatus
// removed.
SignatureValid bool
// NotExpired is true when there is no expirationDate, or the verification
// time precedes it.
NotExpired bool
// NotRevoked is true unless credentialStatus.revoked is truthy.
NotRevoked bool
// StructureValid is true when the VC has the required shape (proof with a
// proofValue, etc.).
StructureValid bool
}
CredentialResult is the structured outcome of verifying a W3C VC.
func VerifyCredential ¶
func VerifyCredential(raw []byte) (CredentialResult, error)
VerifyCredential verifies a W3C Verifiable Credential supplied as raw JSON bytes. The issuer public key is resolved from the credential's issuer.id / verificationMethod did:key. Verification time is time.Now().
Example ¶
ExampleVerifyCredential shows the 10-line offline verification of a W3C VC issued by the Attestix Python core. No network, no Python runtime.
package main
import (
"fmt"
"os"
attestix "github.com/VibeTensor/attestix-go"
)
func main() {
vc, err := os.ReadFile("testdata/sample-vc.json")
if err != nil {
fmt.Println("read:", err)
return
}
res, err := attestix.VerifyCredential(vc)
if err != nil {
fmt.Println("verify:", err)
return
}
fmt.Printf("signature=%v expired=%v revoked=%v verify=%v\n",
res.SignatureValid, !res.NotExpired, !res.NotRevoked, res.Verify())
}
Output:
func VerifyCredentialAt ¶
func VerifyCredentialAt(raw []byte, now time.Time) (CredentialResult, error)
VerifyCredentialAt is VerifyCredential with an explicit verification time, used for deterministic testing of the expiry check.
func VerifyCredentialWithKey ¶
func VerifyCredentialWithKey(raw []byte, pub ed25519.PublicKey, now time.Time) (CredentialResult, error)
VerifyCredentialWithKey verifies a VC against a caller-supplied issuer public key, bypassing did:key resolution from the document. Use when the trusted key is pinned out of band.
func (CredentialResult) Verify ¶
func (r CredentialResult) Verify() bool
Verify reports the overall verdict: signature valid AND not expired AND not revoked (structure must also be valid).
type DelegationClaims ¶
type DelegationClaims struct {
Iss string `json:"iss"`
Aud string `json:"aud"`
Sub string `json:"sub"`
Iat int64 `json:"iat"`
Exp int64 `json:"exp"`
Nbf int64 `json:"nbf"`
Jti string `json:"jti"`
Att []string `json:"att"`
Prf []string `json:"prf"`
}
DelegationClaims are the UCAN payload claims relevant to verification.
func VerifyToken ¶
VerifyToken verifies a single UCAN JWT signature against pub, returning the decoded claims. It rejects any algorithm other than EdDSA. It does not walk the prf chain; use VerifyChainFull for recursive verification.
type DelegationResult ¶
type DelegationResult struct {
// ParentSignatureValid is the EdDSA signature verdict for the parent token.
ParentSignatureValid bool
// ChildSignatureValid is the EdDSA signature verdict for the child token.
ChildSignatureValid bool
// AttenuationIsSubset is true when the child att is a subset of parent att.
AttenuationIsSubset bool
}
DelegationResult is the structured outcome of verifying a delegation chain.
func VerifyDelegationChain ¶
func VerifyDelegationChain(childToken, parentToken string, childAtt, parentAtt []string, pub ed25519.PublicKey) (DelegationResult, error)
VerifyDelegationChain verifies a two-link UCAN delegation (parent -> child). Each token is an EdDSA-signed compact JWT; the signed message is base64url(header)."."base64url(payload) (unpadded, per the JWT spec). Only alg=EdDSA is accepted; alg:none is rejected.
Both tokens are verified against pub (in Attestix every token in a chain is signed by the single server key). The child att MUST be a subset of the parent att or the chain is rejected for privilege escalation.
childAtt and parentAtt, when non-nil, are the authoritative capability lists to compare; when nil they are taken from the respective token payloads.
func (DelegationResult) Verify ¶
func (r DelegationResult) Verify() bool
Verify reports the overall delegation verdict: both signatures valid AND the child capabilities are a subset of the parent's.