Skip to main content

DTGCredential

Struct DTGCredential 

Source
pub struct DTGCredential { /* private fields */ }
Expand description

Defined DTG Credentials

Implementations§

Source§

impl DTGCredential

Source

pub fn new_vmc( issuer: String, subject: String, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, personhood: bool, ) -> Self

Creates a new community-issued Verifiable Membership Credential (VMC) — the membership grant, the community → member half of a membership edge.

A membership edge is a pair of VMCs, and this is only one of them. The member answers with DTGCredential::new_member_vmc_for, and the edge is not complete until they have: a community can always issue a credential naming somebody as a member, but it cannot produce the acknowledgement without that party’s signature. The pair is what makes an unconsented membership claim unprovable.

The grant MUST NOT carry a digestMultibase — that property is what marks the other direction — and this constructor does not set one.

§issuerScope is always public

A community’s own identifier can only truthfully be declared public — a community that cannot be found cannot be joined — so there is no parameter for it: the grant declares public, and a grant declaring anything else is refused at parse. The member declares its own scope in the acknowledgement.

issuer: The identifier of the VTC or VTN granting membership subject: The member’s identifier, or the member VTC’s own for VTN membership valid_from: The datetime from which this credential is valid valid_until: Optional: The datetime this credential is valid until personhood: Whether this VMC can be used as a form of Personhood Credential - Adds PersonhoodCredential to the type array if true, as the non-authoritative hint DTG Core Credentials permits. PHC status is determined by governance and trust registries, never by this string.

§Give it an id

Chain DTGCredential::with_id on: the member stores the grant under its id, and re-issuing is only recognisable as a renewal rather than a duplicate if there is one.

Source

pub fn new_member_vmc_for( grant: &Value, member: &str, issuer_scope: IssuerScope, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Result<Self, DTGCredentialError>

Creates a new member-issued Verifiable Membership Credential (VMC) — the membership acknowledgement, the member → community half of a membership edge.

The roles of DTGCredential::new_vmc are reversed (the member issues, the community is the subject) and the subject carries a digestMultibase of the grant being acknowledged. That digest is what binds the two halves into one edge: an acknowledgement whose digest matches no valid grant does not complete anything, and the binding forces an order — the grant must exist before this can reference it.

This is the member’s consent artifact. Because the member is its issuer, withdrawing consent needs no cooperation from the community.

§Takes the grant in its wire form, deliberately

grant is the JSON the community sent, not a parsed DTGCredential. The digest has to cover the document the community will recompute it over, and this library does not model every member a credential may carry — credentialStatus, which every VMC issued against a status list carries, is dropped by a parse-then-re-serialise round trip. Building the acknowledgement from a parsed grant would produce a digest that verifies nowhere, and would do it silently.

So: keep the bytes you were given, and pass them here.

member: The member acknowledging — the party whose key will sign this. Refused unless the grant names exactly this identifier as its subject. issuer_scope: The correlation scope the member declares for member — its own choice, which this specification does not constrain. Declare the same scope on every credential issued under one identifier. valid_from: The datetime from which this credential is valid valid_until: Optional: The datetime this credential is valid until. Must not be later than the grant’s own validUntil, and must be set if the grant’s is.

§Errors

DTGCredentialError::NotAMembershipGrant if grant is not a JSON object, does not carry MembershipCredential in its type, has no issuer or credentialSubject.id, or already carries a digestMultibase — that last is an acknowledgement, and acknowledging one does not form an edge. Also if its issuerScope is not public, the only scope a community-issued grant can carry.

It is also DTGCredentialError::NotAMembershipGrant if the grant carries a validUntil that is not an RFC 3339 timestamp.

DTGCredentialError::NotTheGrantSubject if the grant’s credentialSubject.id is not member.

DTGCredentialError::OutlivesGrant if the grant expires and valid_until is later than it, or absent.

DTGCredentialError::InvalidValidityWindow if valid_until is not after valid_from, and DTGCredentialError::JsonTooDeep if the grant is nested more deeply than crate::MAX_JSON_DEPTH.

§Give it an id

Chain DTGCredential::with_id on before signing. A community keys a member’s VMC by id to tell a re-send from a renewal.

§Security

The result is binding evidence, not membership. This constructor does not verify the grant’s proof, so it builds an acknowledgement of a grant nobody signed as readily as of one the community did. Verify the grant first — with verify_grant_with_public_key under the affinidi-signing feature, or against your own resolver — and treat the edge as complete only once both proofs and both windows have verified.

Pass as member the identity whose key will sign the acknowledgement, established independently of the grant. An identifier read out of the grant would make the check compare the grant with itself.

Source

pub fn new_vrc( issuer: String, issuer_scope: IssuerScope, subject: String, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Self

Creates a new Verified Relationship Credential (VRC) issuer: The DID of the source party issuer_scope: The scope the source party declares for issuer. pairwise is RECOMMENDED; a wider declaration is a disclosure made deliberately. subject: The DID of the target party as used in this relationship valid_from: The datetime from which this credential is valid valid_until: Optional: The datetime this credential is valid until

Source

pub fn new_vic( issuer: String, issuer_scope: IssuerScope, subject: String, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Self

Creates a new Verified Invitation Credential (VIC) issuer: The DID of the VTC or VTN, or of an authorized member or member VTC issuer_scope: The scope the issuer declares for issuer — public where the issuer is the VTC or VTN itself subject: The DID of the prospective member or member VTC valid_from: The datetime from which this credential is valid valid_until: Optional: The datetime this credential is valid until. Keep it short: an invitation should be single-use and short-lived.

Source

pub fn new_vac( issuer: String, issuer_scope: IssuerScope, subject: String, scope: String, actions: Vec<String>, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, ) -> Result<Self, DTGCredentialError>

Creates a new Verifiable Authority Credential (VAC) — a chain root.

The issuer is the party governing scope. To derive a narrower VAC from one you already hold, use DTGCredential::attenuate instead: a chain root is a grant made by the governing party, and minting one directly is how a self-issued grant of arbitrary authority gets in.

actions MUST NOT be empty — an empty list confers nothing rather than everything.

issuer_scope is the governing party’s declaration for its own identifier — public where the issuer is the party governing the scope, which a chain root’s issuer is. For a community conferring a role on a member, DTGCredential::new_community_role_vac fixes it.

Attenuation below this VAC is permitted by default. To bound or forbid it, chain DTGCredential::with_max_attenuation on before signing.

let vac = DTGCredential::new_vac(
    "did:example:room".to_string(),
    IssuerScope::Public,
    "did:example:member".to_string(),
    "did:example:room".to_string(),
    vec!["read".to_string(), "write".to_string()],
    Utc::now(),
    Utc::now() + Duration::days(30),
)
.unwrap();
assert_eq!(vac.credential().authority().unwrap().actions, ["read", "write"]);
§valid_until is required

Not optional, unlike the base structure and unlike every other new_* constructor here. Nothing about the subject’s current standing is consulted when a VAC is verified, so authority that does not expire is authority nobody can withdraw by waiting.

§Errors

DTGCredentialError::InvalidValidityWindow if valid_until is not after valid_from, and DTGCredentialError::EmptyAuthorityActions if actions is empty.

Source

pub fn new_community_role_vac( community: String, member: String, role: &str, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, ) -> Result<Self, DTGCredentialError>

Creates the VAC a community issues to confer a role on one of its members: a chain root with scope the community’s own DID and actions the single role:<name> action role_action spells.

A role is authority, not reputation. It is conferred by the party governing the scope — the community — and a verifier reads it as a decision that party made, which is what a VAC is and an endorsement is not. So the vetter role a community makes a member eligible for is:

let vac = DTGCredential::new_community_role_vac(
    "did:example:community".to_string(),
    "did:example:member".to_string(),
    "vetter",
    Utc::now(),
    Utc::now() + Duration::days(90),
)
.unwrap();

assert_eq!(vac.issuer_scope(), IssuerScope::Public);
let authority = vac.credential().authority().unwrap();
assert_eq!(authority.scope, "did:example:community");
assert_eq!(authority.actions, ["role:vetter"]);

issuerScope is public, the only scope a community can truthfully declare. The role is checked like any other action — exactly and case-sensitively, through crate::authority::verify_chain with requested_scope the community’s DID and requested_action the role:<name> string. A member holding several roles holds several VACs, or one built with DTGCredential::new_vac listing every role action: actions stays a plain string set, and nothing here privileges the role: convention over any other action a community defines.

Membership is a separate credential. A role VAC does not attest that its subject is a member, and a verifier requiring both asks for both.

§Errors

As DTGCredential::new_vac. role is not validated beyond being non-empty.

Source

pub fn with_max_attenuation( self, max_attenuation: u32, ) -> Result<Self, DTGCredentialError>

Sets authority.maxAttenuation on a VAC: the number of further attenuations permitted below it, with 0 forbidding attenuation outright.

For a chain root, where any value is the governing party’s to choose. On a VAC this library attenuated, pass the limit to DTGCredential::attenuate instead, which refuses one above what the parent permits; set here, an over-limit value is not caught until crate::authority::verify_chain rejects the chain.

§Set it before signing

Same caveat as DTGCredential::with_id.

§Errors

DTGCredentialError::NotAnAuthorityCredential on anything but a VAC.

Source

pub fn attenuate( &self, issuer_scope: IssuerScope, subject: String, actions: Vec<String>, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, max_attenuation: Option<u32>, ) -> Result<Self, DTGCredentialError>

Derive a narrower VAC from one this holder already holds.

This is what lets a member equip an agent, a device, or a short-lived session with only the authority that task needs, rather than lending it their own. The derived credential is issued by the holder, not by the party governing the scope, and carries parent — the digest of the credential it narrows — so a verifier can walk back to a root.

Refuses anything that would widen. The checks here mirror crate::authority::verify_chain on purpose: a holder should be unable to build a chain a verifier would reject, so the failure surfaces at issue time rather than at use — but the verifier’s checks remain authoritative, because nothing stops a different implementation constructing the JSON by hand.

  • self must be a VAC, and must not bear maxAttenuation 0.
  • actions must be a subset of what self confers.
  • valid_until must not exceed self’s.
  • max_attenuation must not exceed one less than self’s, where self bears one. None takes that ceiling — the strictest the chain already imposes — or, where self bears none, leaves the derived VAC unbounded too.

issuer_scope is the attenuating holder’s own declaration for its identifier: the derived VAC’s issuer is self’s subject, and it is that party’s scope to declare.

§Binding the derivative to the agent is subject, not a separate field

A VAC is not a bearer credential: crate::authority::verify_chain requires the party presenting the leaf to be its subject. So equipping an agent means naming the agent in subject, and there is nothing further to bind. An earlier version of this method took an audience for that job; it was removed with the property.

§Digests the model

The parent digest is computed with DTGCredential::digest_multibase, which hashes this in-memory credential. That is right for a VAC this process built and signed. For one that arrived from a counterparty, use DTGCredential::attenuate_from_json and give it the bytes you received — the same distinction DTGCredential::new_member_vmc_for draws, and for the same reason.

Source

pub fn attenuate_from_json( parent: &Value, issuer_scope: IssuerScope, subject: String, actions: Vec<String>, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, max_attenuation: Option<u32>, ) -> Result<Self, DTGCredentialError>

Derive a narrower VAC from a parent in its wire form.

Identical to DTGCredential::attenuate except that the parent is the JSON a counterparty sent rather than a parsed credential, so the parent digest covers the document the verifier will recompute it over. Use this whenever the VAC being narrowed came from somewhere else.

§Errors

DTGCredentialError::NotAnAuthorityCredential if parent is not a JSON object carrying AuthorityCredential in its type and a well-formed credentialSubject.authority, and the same widening errors as DTGCredential::attenuate.

Source

pub fn new_vdc( issuer: String, issuer_scope: IssuerScope, subject: String, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, scope: Vec<String>, max_depth: Option<u32>, ) -> Result<Self, DTGCredentialError>

Creates a new Verifiable Delegation Credential (VDC) — the delegation grant, the delegator → delegate half of a delegation edge.

Establishes that subject may act in the issuer’s name, for the acts named in scope, until valid_until. Within that scope what the delegate does is attributable to the delegator.

§This is not authority

A VDC never supplies permission the delegator did not itself hold. A verifier substitutes the delegator for the delegate and then asks the permission question it would have asked of the delegator directly — so withdrawing the delegator’s own permission ends the delegate’s ability to act immediately, without revoking anything. See DTGCredential::new_vac for the credential that answers that question.

§The edge is not complete without the acceptance

This is one half. The delegate answers with DTGCredential::new_delegate_vdc_for, and a verifier MUST obtain and verify that half before accepting any party as acting under the delegation: a grant alone establishes what the delegator appointed, not what the delegate agreed to. Same consent rule as a membership edge, and for the same reason — a delegator can always name someone as its delegate, but cannot produce the countersignature.

scope MUST NOT be empty: a VDC cannot express an unbounded appointment by omitting it.

max_depth is the number of further re-delegations permitted below this one. None and Some(0) both prohibit re-delegation — the default is a single hop, and setting it above zero is the delegator’s explicit authorisation, of which there is no other kind.

issuer_scope is the delegator’s declaration for issuer. A VDC is presented to every verifier the delegate acts toward, so pairwise is seldom truthful and directed is the ordinary declaration.

§valid_until is required

An appointment with no expiry cannot be reasoned about by a verifier that cannot reach the delegator.

§Errors

DTGCredentialError::MalformedDelegation if scope is empty, and DTGCredentialError::InvalidValidityWindow if valid_until is not after valid_from.

Source

pub fn redelegate( &self, issuer_scope: IssuerScope, subject: String, scope: Vec<String>, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, ) -> Result<Self, DTGCredentialError>

Derive a further VDC from one this delegate already holds — a re-delegation.

Only permitted where the held VDC sets maxDepth above zero, and only for a subset of the acts it was itself appointed for. The default is a single hop: a delegate that needs a further delegate and is not authorised to re-delegate asks the principal, who issues a fresh root delegation directly — so that the principal always holds the complete register of who may speak in its name.

The derived VDC carries parent, the digest of the VDC it derives from, and a maxDepth one less than its parent’s.

Like DTGCredential::attenuate, this digests the in-memory model; for a grant that arrived from a counterparty, use DTGCredential::redelegate_from_json.

issuer_scope is the re-delegating delegate’s own declaration for its identifier.

§Errors

DTGCredentialError::MalformedDelegation if self is not a delegation grant, if it does not permit re-delegation, if scope is empty or not a subset of the parent’s, or if valid_until is later than the parent’s. DTGCredentialError::InvalidValidityWindow if valid_until is not after valid_from.

Source

pub fn redelegate_from_json( parent: &Value, issuer_scope: IssuerScope, subject: String, scope: Vec<String>, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, ) -> Result<Self, DTGCredentialError>

Derive a further VDC from a parent grant in its wire form.

Identical to DTGCredential::redelegate except that the parent is the JSON the delegator sent, so the parent digest covers the document a verifier will recompute it over.

Source

pub fn new_delegate_vdc_for( grant: &Value, delegate: &str, issuer_scope: IssuerScope, valid_from: DateTime<Utc>, valid_until: DateTime<Utc>, ) -> Result<Self, DTGCredentialError>

Creates the delegate-issued half of a delegation edge — the acceptance.

The roles of DTGCredential::new_vdc are reversed (the delegate issues, the delegator is the subject) and the subject carries accepts, the digest of the grant being taken on. That digest is what binds the two halves into one edge.

An acceptance carries no scope of its own. What the delegate consented to is the scope of the grant it names, which a verifier holds in any case; restating it would require an equality check across the two credentials that cannot be satisfied under selective disclosure of either.

This is the delegate’s consent artifact, and its accountability for acting in another’s name. Because a delegator cannot produce it, a party holding only the delegate’s key cannot manufacture appointments either.

§Takes the grant in its wire form, deliberately

Same reasoning as DTGCredential::new_member_vmc_for: the digest has to cover the document the delegator will recompute it over. Keep the bytes you were given and pass them here.

§Errors

DTGCredentialError::NotADelegationGrant if grant is not a JSON object carrying DelegationCredential in its type, has no issuer or credentialSubject.id, or already carries accepts — that last is itself an acceptance, and accepting one forms no edge.

It is also DTGCredentialError::NotADelegationGrant if the grant carries a validUntil that is not an RFC 3339 timestamp.

DTGCredentialError::NotTheGrantSubject if the grant’s credentialSubject.id is not delegate.

DTGCredentialError::OutlivesGrant if valid_until is later than the grant’s.

DTGCredentialError::InvalidValidityWindow if valid_until is not after valid_from, and DTGCredentialError::JsonTooDeep if the grant is nested more deeply than crate::MAX_JSON_DEPTH.

§Security

The result is binding evidence, not an appointment. This constructor does not verify the grant’s proof, so it builds an acceptance of a grant nobody signed as readily as of one the delegator did. Verify the grant first — with verify_grant_with_public_key under the affinidi-signing feature, or against your own resolver — and treat the edge as complete only once both proofs and both windows have verified.

Pass as delegate the identity whose key will sign the acceptance, established independently of the grant. An identifier read out of the grant would make the check compare the grant with itself.

Source

pub fn new_vpc( issuer: String, issuer_scope: IssuerScope, subject: String, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Self

Creates a new Verified Persona Credential (VPC) issuer: The DID under which the persona is asserted issuer_scope: The scope declared for issuer — ordinarily directed, since a persona exists to be recognized across counterparties the holder chooses subject: The DID of the counterparty as used in the relationship valid_from: The datetime from which this credential is valid valid_until: Optional: The datetime this credential is valid until

Source

pub fn with_id(self, id: impl Into<String>) -> Self

Sets this credential’s own identifier, consuming and returning it so it chains onto any of the new_* constructors above.

id MUST be a single URL per the W3C VC Data Model; urn:uuid:<uuid> is the usual choice for a credential with no dereferenceable home. This crate does not validate it.

let vmc = DTGCredential::new_vmc(
    "did:example:community".to_string(),
    "did:example:member".to_string(),
    Utc::now(),
    None,
    false,
)
.with_id("urn:uuid:2a4e1d90-6e0c-4d3f-9a4a-6d0a8f7c1b52");
assert_eq!(vmc.id(), Some("urn:uuid:2a4e1d90-6e0c-4d3f-9a4a-6d0a8f7c1b52"));
§Set it before signing

A Data Integrity proof covers the credential minus its proof, so id is part of what is signed. Chain this onto the constructor, before DTGCredential::sign — adding an id to an already-signed credential leaves a document whose proof no longer verifies.

Source

pub fn set_id(&mut self, id: impl Into<String>)

Sets this credential’s own identifier in place.

The non-consuming form of DTGCredential::with_id; the same “before signing” caveat applies.

Source

pub fn with_task_citation( self, document: &Value, ) -> Result<Self, DTGCredentialError>

Cites a Trust Task document: sets taskContext to its id and taskDigestMultibase to its task digest, together.

Use it for any credential whose meaning depends on an exchange — a VSC under a profile requiring taskContext, such as a VWC or a statement a vetting/session produces. Name the innermost exchange that attests what the credential states, by the document that initiated it (Trust Tasks §4.9.1). Setting both halves from one document is the point: the id locates the exchange and the digest binds the credential to it, and a pair taken from two places binds nothing. See crate::task_digest_multibase_json for how the digest is computed.

Replaces a taskContext and taskDigestMultibase already set.

§Set it before signing

Both members are covered by the credential’s proof, as for DTGCredential::with_id.

§Errors

DTGCredentialError::MalformedTaskDocument if document is not an object with a string id; DTGCredentialError::JsonTooDeep if it is nested past crate::MAX_JSON_DEPTH.

Source

pub fn set_task_citation( &mut self, document: &Value, ) -> Result<(), DTGCredentialError>

Cites a Trust Task document in place.

The non-consuming form of DTGCredential::with_task_citation; the same caveats apply. On error, the credential is left unchanged.

Source

pub fn with_credential_status(self, status: Value) -> Self

Attaches the status mechanism through which a verifier determines whether this credential has been revoked.

The entry is opaque to this library: the mechanism is chosen by the governing VTC or VTN, and nothing here selects one or resolves it. BitstringStatusListEntry is the common choice.

let vdc = DTGCredential::new_vdc(
    "did:example:delegator".to_string(),
    IssuerScope::Directed,
    "did:example:delegate".to_string(),
    Utc::now(),
    Utc::now() + Duration::days(90),
    vec!["sign:invoices".to_string()],
    None,
)
.unwrap()
.with_credential_status(json!({
    "id": "https://example.com/status/3#94567",
    "type": "BitstringStatusListEntry",
    "statusPurpose": "revocation",
    "statusListIndex": "94567",
    "statusListCredential": "https://example.com/status/3"
}));
assert!(vdc.credential().credential_status.is_some());
§When a VDC needs one

CONDITIONAL, not required. A verifier MUST be able to establish that an appointment is in force without contacting the delegator, and either of two things satisfies that: a validUntil short enough that expiry alone bounds the exposure, or a status entry the verifier can check. A VDC MUST carry one where its validity period exceeds the freshness window the governing VTC or VTN defines for delegations, and MAY omit it otherwise.

That window is governance this library does not know, so it cannot decide for a caller which side of the condition a given VDC falls on — hence a setter rather than a constructor parameter. Prefer short validity and re-issuance wherever the delegator is reachable: a status check is a live lookup that reveals the verification event to whoever hosts the status list. A long-lived appointment made in advance of a delegator’s unavailability is the case this exists for.

§Set it before signing

Same caveat as DTGCredential::with_id — a Data Integrity proof covers the credential minus its proof, so attaching a status entry to an already-signed credential leaves a document whose proof no longer verifies.

§This library does not check it

Neither crate::delegation::verify_chain nor crate::authority::verify_chain resolves a status entry; both verify structure, scope and validity only. Revocation is a live lookup the caller performs.

§Security

The entry is embedded verbatim and nothing about it is checked when it is attached. It tells a verifier where to look, so a verifier must check the credential’s proof before following it, and should apply its own policy to where it leads. Nesting past crate::MAX_JSON_DEPTH is refused when the credential is validated, digested or signed, because this setter cannot return an error.

Source

pub fn set_credential_status(&mut self, status: Value)

Attaches a revocation status mechanism in place.

The non-consuming form of DTGCredential::with_credential_status; the same “before signing” caveat, the same CONDITIONAL rule and the same security notes apply.

Source§

impl DTGCredential

Source

pub fn new_vsc( issuer: String, issuer_scope: IssuerScope, subject: String, predicate: impl Into<String>, object: StatementObject, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Result<Self, DTGCredentialError>

Creates a Verifiable Statement Credential (VSC) under any predicate.

  • issuer / issuer_scope: the party making the statement, and the correlation scope it declares for that identifier.
  • subject: the DID of the node the statement is about.
  • predicate: the absolute IRI fixing the statement’s meaning — one of the core constants (ENDORSES_V1, WITNESSED_V1, VETTED_V1, PRESENTED_V1) or a predicate a community defines in a namespace it controls.
  • object: exactly one of id, digestMultibase or value.

Prefer the profile constructors for the core predicates — Self::new_endorses_vsc, Self::new_witnessed_vsc, Self::new_vetted_vsc, Self::new_presented_vsc — which set the citation a profile requires and enforce its subject–object rule. This one checks what it can without them: the predicate, the object kind and the minimum issuerScope of a core profile. A profile that REQUIRES taskContext is completed with DTGCredential::with_task_citation, and DTGCredential::validate — so also DTGCredential::sign — refuses it until it is.

§Errors

DTGCredentialError::InvalidPredicate for a predicate that is not an absolute NFC IRI, DTGCredentialError::ProfileViolation for an object a core profile does not permit, DTGCredentialError::IssuerScopeTooNarrow for a scope below a core profile’s minimum, DTGCredentialError::InvalidValidityWindow for a window that closes before it opens, and DTGCredentialError::JsonTooDeep for an object.value nested past crate::MAX_JSON_DEPTH.

Source

pub fn new_endorses_vsc( issuer: String, issuer_scope: IssuerScope, subject: String, endorsement: Value, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Result<Self, DTGCredentialError>

Creates a VEC — a statement under ENDORSES_V1 — endorsing subject with endorsement as object.value.

The endorsement’s schema is the governing community’s vocabulary, and nothing about it is checked but its depth. A VEC says only that its issuer said this: a verifier must establish that the issuer is one whose endorsements it accepts for this purpose before relying on any member of it.

Not a role grant. A role confers something, and conferring is a VAC’s job: see DTGCredential::new_community_role_vac.

§Errors

As DTGCredential::new_vsc.

Source

pub fn new_witnessed_vsc( issuer: String, issuer_scope: IssuerScope, witnessed: &Value, session: &Value, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, witness_context: Option<WitnessContext>, ) -> Result<Self, DTGCredentialError>

Creates a VWC — a statement under WITNESSED_V1 — attesting that the witness observed a party issue witnessed, in the witness/session that session opened.

Both halves of every binding are read from the documents themselves, so none of them can disagree:

  • credentialSubject.id is witnessed’s issuer — the profile’s subject–object rule: the witness observed the subject issue it. One VWC per direction, so a witnessed VRC pair takes two calls, one with each VRC.
  • object.digestMultibase is the digest of witnessed in its wire form, top-level proof excluded, as crate::digest_multibase_json computes it.
  • taskContext is the id of session, and taskDigestMultibase its task digest (witness/session/submit Conformance, item 1).

issuer is the witness — a member, or a VTA acting under VTC policy — and must declare at least directed: a witness’s identifier must be recognizable to both parties and to the community, so pairwise cannot describe it truthfully.

§Errors
§Security

Pass the session document the witness itself received and answered, not one the party supplies alongside its submission, and the edge credential the witness itself observed. The digests are load-bearing because the witness signs them.

Source

pub fn new_vetted_vsc( issuer: String, issuer_scope: IssuerScope, subject: String, vetting: Value, session: &Value, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Result<Self, DTGCredentialError>

Creates a vetting statement — a statement under VETTED_V1 — recording that the issuer checked subject’s claimed identity in the vetting exchange session opened.

vetting is object.value. This library is generic about it: the payload’s schema is the registry definition’s, and a typed payload belongs with the code that fills it in. session is the initiating document of the vetting exchange; taskContext and taskDigestMultibase are both read from it.

issuer is the vetter, issuing under the member identifier its VMC names, and must declare at least directed. Whether it was eligible to vet is a fact about the community, checked separately — a community-issued VAC is the credential that answers it (see DTGCredential::new_community_role_vac).

§Errors

DTGCredentialError::IssuerScopeTooNarrow for pairwise, DTGCredentialError::MalformedTaskDocument if session has no string id, and the errors of DTGCredential::new_vsc.

Source

pub fn new_presented_vsc( issuer: String, issuer_scope: IssuerScope, presented: &Value, session: &Value, valid_from: DateTime<Utc>, valid_until: Option<DateTime<Utc>>, ) -> Result<Self, DTGCredentialError>

Creates a statement under PRESENTED_V1, attesting that the issuer observed a party present presented in the exchange session opened.

As for DTGCredential::new_witnessed_vsc, both halves of each binding are read from the documents: credentialSubject.id is presented’s own credentialSubject.id — the party who holds it, and the profile’s subject–object rule — object.digestMultibase its digest, and the citation session’s id and task digest.

§Errors

DTGCredentialError::IssuerScopeTooNarrow for pairwise, DTGCredentialError::ProfileViolation if presented has no credentialSubject.id, DTGCredentialError::MalformedTaskDocument if session has no string id, and the errors of DTGCredential::new_vsc.

Source

pub fn witnesses_issuance_of( &self, credential: &Value, ) -> Result<bool, DTGCredentialError>

Is this a WITNESSED_V1 statement about credential — the edge credential in its wire form — and does the profile’s subject–object rule hold?

True when the predicate is witnessed/1, object.digestMultibase matches credential’s recomputed digest (decoded bytes, not strings), and credentialSubject.id is credential’s issuer. A verifier holding the referenced credential MUST check both, and this is those two checks.

§What this does not check

The statement’s proof, its window, whether its issuer is a witness the verifier trusts, its taskContext against the session (see DTGCredential::cites_task), and whether credential is itself valid: a witness attestation is about claims at the moment of witnessing, not about their being current.

§Errors

DTGCredentialError::InvalidDigest or DTGCredentialError::UnsupportedDigestAlgorithm if the carried digest cannot be read, rather than Ok(false).

Source

pub fn witnesses_presentation_of( &self, credential: &Value, ) -> Result<bool, DTGCredentialError>

Is this a PRESENTED_V1 statement about credential in its wire form, with credentialSubject.id equal to credential’s own credentialSubject.id?

The PRESENTED_V1 counterpart of DTGCredential::witnesses_issuance_of, with the same caveats.

Source§

impl DTGCredential

Source

pub fn credential(&self) -> &DTGCommon

get the raw credential

Source

pub fn credential_mut(&mut self) -> &mut DTGCommon

Get the raw credential as mutable

Source

pub fn signed(&self) -> bool

Has this credential been signed?

Source

pub fn type_(&self) -> DTGCredentialType

get the credential type

Source

pub fn id(&self) -> Option<&str>

This credential’s own identifier, if it has one.

None for a credential built by one of the new_* constructors and never given one with DTGCredential::with_id. See DTGCommon::id for why a counterparty may require it.

Source

pub fn issuer(&self) -> &str

Returns the Issuer DID

Source

pub fn issuer_scope(&self) -> IssuerScope

The correlation scope the issuer declares for its own identifier. See IssuerScope.

Source

pub fn statement(&self) -> Option<&CredentialSubjectStatement>

The statement, when this credential is a VSC. See DTGCommon::statement.

Source

pub fn predicate(&self) -> Option<&str>

The predicate IRI, when this credential is a VSC.

Compare it byte for byte — PredicateAcceptList does — and never by spelling, prefix or a published equivalence.

Source

pub fn subject(&self) -> &str

Returns the Subject DID

Source

pub fn valid_from(&self) -> DateTime<Utc>

Returns the valid_from timestamp

Source

pub fn valid_until(&self) -> Option<DateTime<Utc>>

Returns the valid until timestamp

Source

pub fn task_context(&self) -> Option<&str>

The id naming the trust task exchange this credential cites, if set. See DTGCommon::task_context.

This is always Some for a VSC under a predicate profile that makes taskContext REQUIRED — WITNESSED_V1, VETTED_V1 and PRESENTED_V1.

Source

pub fn task_digest_multibase(&self) -> Option<&str>

The task digest of the Trust Task document taskContext names, if set. See DTGCommon::task_digest_multibase.

Source

pub fn cites_task(&self, document: &Value) -> Result<bool, DTGCredentialError>

Does this credential cite document — the Trust Task document its taskContext names — and is it bound to that document’s content?

Both halves of the citation have to hold:

  1. taskContext equals the document’s id, which locates the exchange;
  2. taskDigestMultibase matches the task digest recomputed from document, which binds the credential to it.

Returns Ok(false) where either fails, and where the credential carries no taskContext or no taskDigestMultibase. The last case is deliberate: Trust Tasks §4.9.3 forbids falling back to comparing ids alone, because an id is a name anyone may reuse on a counterfeit.

§Compares bytes, not strings

The digest is recomputed with the top-level proof removed, so a signed and an unsigned copy of the same document agree, and compared as decoded multihash bytes. A task digest may be base58btc or base64url: two conforming encodings of one digest are different strings, and a string comparison would reject an honest citation.

§What this does not check

That the exchange completed, which needs the outcome evidence of DTG Core Credentials §Outcome Interpretability, and that the document was attributable, which needs its own proof. A task digest attests content, not authenticity. It is load-bearing because it is the credential’s issuer who signed it.

§Errors

DTGCredentialError::InvalidDigest if the carried value is not a well-formed multibase multihash, and DTGCredentialError::UnsupportedDigestAlgorithm if it names a hash this library does not implement. Trust Tasks §4.9.3 requires such a citation to be treated as unverified, never recomputed under another algorithm, so it is reported rather than folded into Ok(false). DTGCredentialError::JsonTooDeep if document is nested past MAX_JSON_DEPTH.

Source

pub fn digest_multibase(&self) -> Result<String, DTGCredentialError>

This credential’s digest, in the encoding a credential that references it carries — a member-issued VMC acknowledging a membership grant, a VSC whose object names it (a VWC attesting an edge credential), or the parent of an attenuated VAC.

Per DTG Core Credentials Digest Encoding, that is the SHA-256 hash of the credential’s JSON representation excluding its top-level proof member, canonicalized with the JSON Canonicalization Scheme (JCS, RFC 8785), wrapped in a sha2-256 multihash and encoded base58btc with a multibase z prefix.

§Why proof is excluded

The digest binds to what the credential says, not to a particular signature over it. A referencing credential therefore survives a re-proofing of its referent: a re-signed grant carrying identical claims still satisfies an acknowledgement made against the earlier signature. It also means the digest can be computed before the referent is signed, and is stable whichever of its proofs a holder happens to have.

§Prefer the wire form for a credential you received

This digests the model. DTGCommon::extra carries top-level members this library does not model through a round trip, so for most received credentials the two agree — but a member inside credentialSubject that the subject types do not model is still not represented. Where you still hold the bytes a counterparty sent, digest those with digest_multibase_json.

§Errors

DTGCredentialError::JsonTooDeep if an open JSON member takes the credential past MAX_JSON_DEPTH, checked before the credential is cloned or serialized.

Source

pub fn digest(&self) -> Result<String, DTGCredentialError>

👎Deprecated since 0.7.0:

Working Draft 02 replaced the sha256:<hex> digest with a base58btc multibase multihash under the property name digestMultibase. Use DTGCredential::digest_multibase. This method will be removed in a future release.

This credential’s digest in the superseded sha256:<hex> encoding.

Source

pub fn subject_digest(&self) -> Option<&str>

The digest this credential carries of the credential it references, if it carries one.

Some for a member-issued VMC (which MUST carry one), for a VSC whose object is a digestMultibase (a VWC bound to the edge credential it attests, among them), for an attenuated VAC (authority.parent), and for a derived or accepting VDC (delegation.parent / delegation.accepts). None for a community-issued VMC, which MUST omit it, and for a credential that references nothing.

Source

pub fn verify_digest( &self, referenced: &DTGCredential, ) -> Result<bool, DTGCredentialError>

Checks that the digest this credential carries matches the credential it claims to reference.

Answers one question only — whether the hashes agree. It does not check that the two credentials are of the types the reference requires, nor that their issuers and subjects line up. For a membership acknowledgement, DTGCredential::acknowledges checks all of that together and is what a verifier completing an edge should call.

§Compares bytes, not strings

The specification requires a verifier to decode the multibase envelope and the multihash inside it, and to compare the algorithm identifier and the raw digest — never the encoded strings. Two equal digests can be written differently, and a string comparison would report a mismatch where the credentials agree.

Returns Ok(false) if the digests do not match, or if this credential carries no digest, in which case there is nothing to rely on.

§Errors

DTGCredentialError::InvalidDigest if the carried value is not a well-formed digestMultibase — a Working Draft 01 sha256:<hex> value among them — and DTGCredentialError::UnsupportedDigestAlgorithm if it names a hash this library does not implement. Both are reported rather than folded into Ok(false): a digest that cannot be read is not a digest that disagrees.

Source

pub fn acknowledges( &self, grant: &DTGCredential, ) -> Result<bool, DTGCredentialError>

Does this member-issued VMC acknowledge grant, completing that membership edge?

A membership edge is complete only when both VMCs of the pair exist and are valid: the community-issued VMC that grants membership, and the member-issued VMC that acknowledges it. This checks everything that binds the two together:

  1. grant is a MembershipCredential carrying no digest — a community-issued grant
  2. self is a MembershipCredential carrying one — a member-issued acknowledgement
  3. the two name the same pair of parties, in mirrored roles: this credential’s issuer is the grant’s subject, and its subject is the grant’s issuer
  4. the digest matches the grant

Returns Ok(false) where any of those does not hold, rather than distinguishing them: a caller deciding whether an edge is complete has one decision to make, and every failing case answers it the same way.

§What this does not check

Neither credential’s proof, and neither validity window. Both are the caller’s to verify — proof verification needs a resolver this crate does not hold, and whether a window is current is a question about an instant the caller chooses. An edge is complete when both VMCs are valid as well as bound, and this covers only the binding.

§Security

Ok(true) is binding evidence, not membership. A pair binds whether or not anybody signed either half: an acknowledgement can be built against a grant the community never issued, and this accepts the two together. Before treating an edge as complete, verify the grant’s proof against the community’s key and the acknowledgement’s against the member’s — each made by a verification method of that credential’s issuer — and check both windows at the instant you care about. verify_grant_with_public_key, under the affinidi-signing feature, does that for the grant in its wire form.

Source

pub fn accepts(&self, grant: &DTGCredential) -> Result<bool, DTGCredentialError>

Does this delegate-issued VDC accept grant, completing that delegation edge?

A delegation edge is complete only when both VDCs exist and are valid: the delegator’s grant, and the delegate’s acceptance of it. This checks everything that binds the two together:

  1. grant is a DelegationCredential carrying scope and no accepts — a grant
  2. self is a DelegationCredential carrying accepts — an acceptance
  3. the two name the same pair of parties in mirrored roles: this credential’s issuer is the grant’s subject, and its subject is the grant’s issuer
  4. the accepts digest matches the grant

Returns Ok(false) where any of those does not hold, rather than distinguishing them: a caller deciding whether an edge is complete has one decision to make, and every failing case answers it the same way.

§What this does not check

Neither credential’s proof, neither validity window, and neither’s revocation status. Nor does it establish that the delegator may perform the act in question — that is a separate question, asked of the delegator at the time of the act, which a VDC moves but never answers. This covers the binding.

§Security

As with DTGCredential::acknowledges, Ok(true) is binding evidence only. Verify both proofs, each against a verification method of its own credential’s issuer, and both windows, before accepting anybody as acting under the delegation.

Source

pub fn proof_value(&self) -> Option<&str>

Returns the proof value if signed else None

Source

pub fn validate(&self) -> Result<(), DTGCredentialError>

Checks the invariants this library holds a credential to before putting a proof on it.

  • The validity window is well formed: validUntil, where present, is after validFrom (DTGCredentialError::InvalidValidityWindow).
  • No open JSON member — a statement’s object.value, credentialStatus, an unmodelled member — takes the document past MAX_JSON_DEPTH (DTGCredentialError::JsonTooDeep). The check does not recurse.
  • Everything a parse checks still holds: the @context and type arrays, the subject’s shape for the type, issuerScope where the type or profile constrains it, a VSC’s predicate, and the constraints of its core profile — taskContext and taskDigestMultibase where WITNESSED_V1, VETTED_V1 or PRESENTED_V1 require them. DTGCredential::credential_mut can break any of these, and this is where a broken credential is caught before it is signed.

DTGCredential::sign calls this first, so this library never signs a credential that fails it, and DTGCredential::verify_proof_with_public_key calls it before examining a proof. The new_* constructors that return a plain Self have no way to refuse, so a credential built by one of them is checked here rather than there. If you sign with another backend, call this yourself before you do.

A validFrom in the past is accepted. Backdating is legitimate — re-issuing a credential with the date the original took effect is the usual case — so only the ordering of the two ends is checked, never either end against the clock.

Source

pub async fn sign( &mut self, signing_secret: &Secret, create_time: Option<DateTime<Utc>>, ) -> Result<DataIntegrityProof, DTGCredentialError>

Sign the credential using W3C Data Integrity Proof with JCS EdDSA 2022 signing_secret: The secret key to use to sign the credential create_time: Optional creation time for the proof, defaults to now if None

§Errors

Anything DTGCredential::validate refuses, before any signing is attempted.

Source

pub fn verify_proof_with_public_key( &self, public_key_bytes: &[u8], ) -> Result<(), DTGCredentialError>

Verify the credential if you already know the public key bytes otherwise use the affinidi_tdk:verify_data() method public_key_bytes: The public key bytes to use to verify the credential

§Errors

Anything DTGCredential::validate refuses, before the proof is examined: a credential this library would not have signed does not verify either.

Source

pub fn get_w3c_vc_version(&self) -> W3CVCVersion

Is this credential a W3C VC Version 1.1 or 2.0 credential?

Source

pub fn is_personhood_credential(&self) -> bool

returns true if this credential a personhood credential (PHC)

Trait Implementations§

Source§

impl Clone for DTGCredential

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DTGCredential

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for DTGCredential

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Serialize for DTGCredential

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl TryFrom<DTGCommon> for DTGCredential

Post deserialize setup of a CredentialSubject and CredentialType

Source§

type Error = DTGCredentialError

The type returned in the event of a conversion error.
Source§

fn try_from(value: DTGCommon) -> Result<Self, Self::Error>

Performs the conversion.
Source§

impl TryFrom<Value> for DTGCredential

Deserialization: @context and type are checked on the raw document first, so that a credential of a retired type, or under an unrecognized context, is refused with an error that says so — rather than with whatever the subject’s shape happens to fail on.

Source§

type Error = DTGCredentialError

The type returned in the event of a conversion error.
Source§

fn try_from(value: Value) -> Result<Self, Self::Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more