The issuance of an anonymous [[ref: credential]] requires several steps and involves the roles [[ref: issuer]], [[ref: holder]] as well as the [[ref: Verifiable Data Registry]] (see diagram below).
sequenceDiagram
autonumber
participant L as Verifiable<br>Data Registry
participant I as Issuer
participant H as Holder
I ->> I: Create Credential Offer
I ->> H: Send Credential Offer
H ->> H: Verify Credential Offer
opt
H ->> L: Request Schema
L ->> H: Return Schema
end
H ->> L: Request Credential Definition
L ->> H: Return Credential Definition
H ->> H: Create Credential Request
H ->> I: Send Credential Request
I ->> I: Verify Credential Request
I ->> I: Issue Credential
I ->> H: Send Credential
H ->> H: Remove Credential Blinding
H ->> H: Verify and Store Credential
rect rgb(191, 223, 255)
Note left of H: 💡The "Verifier" and "Schema Publisher" roles are<br>omitted in this diagram, since they do not participate<br>in the credential issuance data flow.
end
The [[ref: issuer]] prepares a [[ref: Credential Offer]] for the [[ref: holder]] (step 1). A [[ref: Credential Offer]] includes a commitment about the [[ref: credential]] (referencing a [[ref: Public Credential Definition]]) the [[ref: issuer]] is intending to issue to the [[ref: holder]]. The [[ref: issuer]] sends the [[ref: Credential Offer]] to the [[ref: holder]] (step 2), who evaluates the offer (step 3) and fetches data about the offer (the [[ref: Public Credential Definition]]) from the [[ref: Verifiable Data Registry]] (steps 4-7).
Using the data from the [[ref: Credential Offer]] and the [[ref: Public Credential Definition]] retrieved from the [[ref: Verifiable Data Registry]], the [[ref: holder]] prepares a [[ref: Credential Request]] (step 8), a formal request to the [[ref: issuer]] to issue a [[ref: credential]] based on the given [[ref: Public Credential Definition]] to the [[ref: holder]]. The [[ref: Credential Request]] includes a cryptographic commitment to the [[ref: holder]]'s [[ref: link secret]]. The [[ref: holder]] sends the [[ref: Credential Request]] to the [[ref: issuer]] (step 9).
The [[ref: issuer]] verifies and decides whether to accept the [[ref: Credential Request]] (step 10) and if so, prepares the [[ref: credential]] (step 11). The [[ref: issuer]] sends the [[ref: credential]] to the [[ref: holder]] (step 12). The [[ref: holder]] then removes the blinding factor from the credential (step 13), verifies the [[ref: credential]] and (usually) securely stores it (step 14).
Details about each step in the issuance process are covered in the following sections.
The AnonCreds issuance process begins with the [[ref: issuer]] constructing and sending a [[ref: Credential Offer]] to the potential [[ref: holder]]. The Credential Offer contains the following JSON elements:
{
"schema_id": string,
"cred_def_id": string,
"nonce": string,
"key_correctness_proof" : <key_correctness_proof>
}
schema_id
: The ID of the [[ref: Schema]] on which the [[ref: Public Credential Definition]] for the offered [[ref: Credential]] is based.cred_def_id
: The ID of the [[ref: Public Credential Definition]] on which the [[ref: Credential]] to be issued will be based.nonce
: A random number generated for one time use by the [[ref: issuer]] for preventing replay attacks and authentication between protocol steps. Thenonce
must be present in the subsequent [[ref: Credential Request]] from the [[ref: holder]].key_correctness_proof
: The Fiat-Shamir transformation challenge value in the non-interactive mode of Schnorr Protocol. It is calculated by the [[ref: issuer]] as the proof of knowledge of the private key used to create the [[ref: Credential Definition]]. This is verified by the [[ref: holder]] during the creation of [[ref: Credential Request]].
The JSON content of the key_correctness_proof
is:
"key_correctness_proof": {
"c": "103...961",
"xz_cap": "563...205",
"xr_cap": [
[
"<attribute 1>",
"821...452"
],
[
"master_secret",
"156...104"
],
[
"<attribute 1>",
"196...694"
]
]
}
The values in the proof are generated as follows:
c
: (a [[ref: BigNumber]]) can be viewed as the committed value derived from the hash of the concatenated byte values in the process of creating the Credential Definition.
where
-
$z = s ^ {x_z}\ Mod\ n$ where$z$ ,$s$ and$n$ are values in the [[ref: Public Credential Definition]] -
$r_i$ are the values in the$r$ map in [[ref: Public Credential Definition]], individual attribute public keys -
$\tilde{z}$ is similar to$z$ which equals to$s^{\tilde{x_z}}\ mod\ n$ , where$\tilde{x_z}$ is a randomly selected integer between$2$ and$p'q'-1$ -
$\tilde{r_i}$ is similar to$r$ , which equal to$s^{\tilde{x_i}}\ mod\ n$ , where$\tilde{x_i}$ are randomly selected integers between$2$ and$p'q'-1$ -
xz_cap
:$\hat{x_z} = c x_z + \tilde{x_z}$ -
xr_cap
:${ (attribute_i, cr_i + \tilde{r_i}) }_{1 < i < L}$ for$L$ attributes
Both xz_cap
and the second element in the tuple of the xr_cap
vector
are [[ref: BigNumbers]].
The [[ref: issuer]] sends the [[ref: Credential Offer]] to the [[ref: holder]].
A [[ref: Credential Request]] is a formal request from a [[ref: holder]] to an [[ref: issuer]] to get a [[ref: credential]] based on the [[ref: Credential Offer]] (and the referenced [[ref: Public Credential Definition]]) sent by the [[ref: issuer]] to the [[ref: holder]].
On receipt of the [[ref: Credential Offer]], the [[ref: holder]] retrieves the referenced [[ref: Public Credential Definition]] from a [[ref: Verifiable Data Registry]]. The holder MAY want to retrieve the [[ref: Schema]] referenced in the [[ref: Credential Offer]] and verify the consistency between the list of attributes in the [[ref: Schema]] and in the [[ref: Public Credential Definition]].
In addition, the [[ref: holder]] also requires access to their [[ref: link secret]].
The nonce of the [[ref: Credential Offer]] is used to generate the proof of correctness for blinded credential secrets, where it is hashed with the blinded secrets to create the proof which is sent to the [[ref: issuer]].
The [[ref: holder]] must first verify the key_correctness_proof
in the [[ref:
Credential Offer]] using data from the referenced [[ref: Public Credential Definition]]. The
key_correctness_proof
data is described in the previous
section about the [[ref: Credential Offer]].
The key_correctness_proof
verification is as follows:
- Check that all attributes in [[ref: Public Credential Definition]] and
master_secret
(an attribute that will be related to the [[ref: link secret]]) are included inxr_cap
. - Compute
$c'$ , where$c' = H(z || {r_i} || \hat{z'} ||\hat{r_i'})$ . - If
$\hat{z'} == \tilde{z}$ and$\hat{r_i'} == \tilde{r_i}$ , then$c' == c$ . The proof is accepted.
For
Then
$$ \hat{z'} = z^{-c} s^{\hat{x_z}} \ (Mod\ n)$$
The same can be derived for all
The [[ref: holder]] constructs the following [[ref: Credential Request]] JSON structure:
{
"prover_did": "BZpdQDGp2ifid3u3Up17MG",
"cred_def_id": "GvLGiRogTJubmj5B36qhYz:3:CL:8:faber.agent.degree_schema",
"blinded_ms": {
# Structure detailed below
},
"blinded_ms_correctness_proof": {
# Structure detailed below
},
"nonce": "604812518357657692681285"
}
entropy
: a required string.- Called
prover_did
in earlier AnonCreds implementations, and calledprover_id
in Ursa,entropy
is a random alphanumeric string generated by the [[ref: holder]] and used by the [[ref:issuer]] to add entropy in generating the credential signature. The value is combined by the [[ref: issuer]] with the credential revocation index (cred_idx
) if the credential is revocable, and the resulting string is hashed to create thecredential_context
, an input to the credential signing process. Thecredential_context
is them2
item in the issued verifiable credential signature. - Historically in Aries agent implementations, the
prover_did
was populated by the [[ref: holder]] with a [[ref: DID]] they hold, usually the DIDComm peer-to-peer DID shared by the the [[ref: holder]] to the [[ref: issuer]]. However, the item is not verified by the [[ref: issuer]] as a DID nor as an identifier for the [[ref: holder]], and as such an random string is sufficient. - The [[ref: holder]] can verify the provide
entropy
value was used by the [[ref: issuer] in generating the signature by combiningentropy
with thecred_idx
value from the issuer (if the credential is revocable), hashing the resulting string and checking that the hash matchesm2
in the credential signature.
- Called
cred_def_id
: The ID of the [[ref: Public Credential Definition]] on which the [[ref: Credential]] to be issued will be based.blinded_ms
: The [[ref: link secret]] in its blinded form. Described in detail in the section Blinding the Link Secret (below).blinded_ms_correctness_proof
: The [[ref: Blinded Secrets Correctness Proof]] of the blinded [[ref: link secret]]. Described in detail in the section The Blinded Link Secret Correctness Proof (below).nonce
: Used for preventing replay attacks and authentication between protocol steps. The [[ref: holder]] creates an 80 bit nonce in the request which is a randomly generated number.
Once constructed, the [[ref: holder]] sends the [[ref: Credential Request]] to the [[ref: issuer]], who then can reply to the [[ref: holder]] by sending an issued credential.
The [[ref: holder]] generates a [[ref: blinding factor]] and uses this to create a cryptographic commitment to their [[ref: link secret]]. This is the blinded_ms
([[ref: blinded link secret]]) in the Credential Request
. The blinded_ms
will be signed by the [[ref issuer]] along with the rest of the credential attributes to create a blinded Signature. The [[ref: holder]] removes the [[ref: blinding factor]] from the blinded Signature to retrieve the Credential Signature over their unblinded [[ref: link secret]] and the credential attributes.
During presentations, the [[ref: holder]] can prove knowledge of the [[ref: link secret]] within credential or set of credentials being presented, without revealing the [[ref: link secret]] itself. This is the capability that enables the binding of the credentials to each other and to the [[ref: holder]] without revealing a correlatable identifier.
::: todo
Confirm purpose of the blinding factor and add how it is generated.
:::
The [[ref: blinding factor]] is a secret generated by the [[ref: holder]] for blinding
the [[ref: link secret]] before sending it to the [[ref: issuer]]. The [[ref: blinding factor]],
n
.
The process of blinding the link secret uses the [[ref: issuer]]'s
CredentialPrimaryPublicKey
, z
, r
, s
and n
(described
here). While r
contains
the public keys for all of the attributes to be signed, the only one of interest
in this process is
The [[ref: link secret]],
The resulting blinded link secret data structure inserted into the [[ref: Credential Offer]] is defined as follows:
"blinded_ms": {
"u": "331...544",
"ur": null, # Populated when the credential definition supports revoation
"hidden_attributes": [
"master_secret"
],
"committed_attributes": {}
}
Where:
-
u
:$u = (s^{v'} \times A_{bl})\ Mod\ n$ -
ur
: isnull
if revocation is not active for the [[ref: Public Credential Definition], and if revocation is active$u_r = h_2^{s'_r}$ where$s'_r$ is randomly selected quadratic residue of order of the bilinear groupsq
and$h_2$ is part of the revocation public key. -
hidden_attributes
: is an array of hidden attributes from the list of [[ref: Public Credential Definition]. For AnonCreds v1.0, it is always a single entry oflink_secret
.- The [[ref: holder]]'s blinded [[ref: link secret]] is a default hidden attribute in AnonCreds, meaning it is not explicitly defined in the [[ref: Schema]] list of attributes but is included in both the [[ref: Public Credential Definition]] and all issued [[ref: credentials]]. Whilst it is cryptographically possible to have multiple hidden attributes, in this version of AnonCreds, only [[ref: link secret]] is used.
-
committed_attributes
: An empty list of attributes in this version of AnonCreds.
In addition to creating the blinded link secret, the [[ref: holder]] also creates a blinded link secret correctness proof and inserts it into the [[ref: Credential Request]]. The data structure for the blinded link secret correctness proof is as follows:
"blinded_ms_correctness_proof": {
"c": "702...737",
"v_dash_cap": "202...924",
"m_caps": {
"master_secret": "907...913"
},
"r_caps": {}
}
The values in the proof are generated as follows:
c
: (a [[ref: BigNumber]]) can be viewed as the committed value derived from the hash of the concatenated byte values in the process of creating the Credential Definition and thenonce
value .
where
-
$u$ is described above. -
$\tilde{u} = s^{\tilde{v}'} \times r_{linksecret}^{\tilde{A_l}}\ mod\ n$ where$\tilde{v}'$ is randomly selected 3488-bit value and$\tilde{A_l}$ is 593-bit value by reference Anonymous credentials with type-3 revocation by Dmitry Khovratovisch, Michael Lodder and Cam Parra -
$n_0$ is the nonce value. -
v_dash_cap
:$\hat{v'} \leftarrow \tilde{v'} + cv'$ , where$v'$ is the blinding factor and$\tilde{v'}$ is a 3488-bit random number. -
m_caps
:${\hat{m_i} \leftarrow \tilde{m_i} + cm_i}_{i \in A_h}$ , where$A_h$ is the set of all hidden attributes. -
r_caps
: is an empty structure in this version of AnonCreds.
After the [[ref: issuer]] receives the [[ref: Credential Request]] from the [[ref: holder]], the [[ref: issuer]] processes the [[ref: Credential Request]] and decides whether to issue the credential as requested in the [[ref: Credential Request]] to the [[ref: holder]]. In this section, we'll cover issuing a credential that cannot be revoked, and then cover the additional steps/data elements in issuing a credential that can be revoked.
Before deciding to issue the credential, the [[ref: issuer]] must first verify the [[ref: Credential Request]] from the [[ref: holder]] by using the nonce from credential offer (
The blinded_ms_correctness_proof
is verified by [[ref: issuer]]. The blinded_ms_correctness_proof
verification is as follows:
- Compute
$c'$ , where$c' = H(u || \hat{u} || n_0)$ . - If
$\hat{u} == \tilde{u}$ , then$c' == c$ . The proof is accepted.
For
Then
$$ \hat{u} = u^{-c} \times r_{linksecret}^{\hat{m} } \times s^{\hat{v'}} \ (Mod\ n)$$
Once the Credential Request is verified and if the [[ref issuer]] decides to proceed with issuing the credential, the credential creation process is performed.
The Anoncreds signature is not applied on the data attributes themselves, but rather on 32-byte integers encoded from the data attribute values. In the current version of AnonCreds, the process of encoding the attributes (also known as canonicalization) is
a task performed by the [[ref: issuer]], who should do the encoding in a manner understood by all potential [[ref: verifiers]] such that any verifier can confirm that the revealed raw
attributes in the presentation produce
the encoded value signed by the [[ref: issuer]]. To enable the broadest possible interoperability, the Hyperledger Aries community formalized the following encoding rules for the raw
attribute values in an AnonCreds credential, and those rules are adopted into this specification, as follows:
- keep any integer as is
- convert any string integer (e.g. "1234") to be an integer (e.g. 1234)
- for data of any other type:
- convert to string (use string "None" for null)
- encode via utf-8 to bytes
- apply SHA-256 to digest the bytes
- convert the resulting digest bytes, big-endian, to integer
- stringify the integer as a decimal.
An example implementation in Python of these rules can be found here.
A gist of test value pairs can be found here.
::: note
To enable broad interoperability, and to improve the security of AnonCreds by
eliminating the risk of malicious [[ref: holders]] altering the raw
data
values in hopes that the [[ref: verifier]] will not check the encoding as part
of the overall presentation verification, future versions of AnonCreds
credentials will not include [[ref: issuer]]-created encoded values in the AnonCreds [[ref: credentials]], and will instead
require the encoding of the raw
data values on as needed basis.
Implementations of AnonCreds MAY
- Verify the encoded values provided by the issuer and reject the credential input if the encoding does not follow the encoding rules in this specification.
- Ignore the [[ref: issuer]]-provided encoded values and calculate the encoded values before generating signatures based on the encoding rules above.
- Ignore the encoded values placed in credentials and/or presentations and generate the encoded values "on-the-fly" based on the encoding rules above.
:::
To construct a non-revocable [[ref: credential]], the [[ref: issuer]] must have available:
- The identifiers for the [[ref: schema]] and [[ref: Public Credential Definition]].
- The [[ref: Private Credential Definition]] data to be used in signing the credential.
- The
raw
value for each attribute to be included in the credential. - The
encoded
value derived from eachraw
value using the encoding attribute data rules (above). - The blinded link secret from the [[ref: holder]]'s [[ref: Credential Request]].
Additional data is needed for issuing a revocable credential, as described in the section Supporting Revocation in a Credential.
The JSON of a generated AnonCreds credential is as follows:
{
"schema_id": string,
"cred_def_id": string,
"rev_reg_id": null,
"values": {
"first_name": {
"raw": "Alice",
"encoded": "113...335"
},
"last_name": {
"raw": "Garcia",
"encoded": "532...452"
},
"birthdate_dateint": {
"raw": "19981119",
"encoded": "19981119"
}
},
"signature": {
"p_credential": {
"m_2": "992...312",
"a": "548...252",
"e": "259...199",
"v": "977...597"
},
"r_credential": null
},
"signature_correctness_proof": {
"se": "898...935",
"c": "935...598"
},
"rev_reg": null,
"witness": null
}
schema_id
: is the ID of the [[ref: Schema]] upon which the [[ref: Public Credential Definition]] was generated.cred_def_id
: is the ID for the [[ref:Public Credential Definition]] on which the [[ref:Credential]] issued is based.rev_reg_id
isnull
if the credential is not revocable. A description of the element when the credential is revocable is in the section Supporting Revocation in a Credential.values
is the list of attributes in the credential, including for each:- the name of the attribute (in this case
first_name
,last_name
, andbirth_dateint
), - the
raw
data for the attribute, and - the
encoded
data for the attribute, derived from theraw
value has defined in the encoding attribute data rules.
- the name of the attribute (in this case
signature
is the cryptographic signature generated for the credential.- A description of the
p_signature
elements and generation process are in the section The Credential Signature. r_credential
isnull
if the credential is not revocable. A description of ther_signature
elements and generation process when the credential is revocable are in the section Supporting Revocation in a Credential.
- A description of the
signature_correctness_proof
is the [[ref: Signature Correctness Proof]] generated for the credential. A description of the elements and generation process are in the section The Credential Signature Correctness Proof.rev_reg
isnull
if the credential is not revocable. A description of the element and generation process when the credential is revocable are in the section Supporting Revocation in a Credential.witness
isnull
if the credential is not revocable. A description of the element and generation process when the credential is revocable are in the section Supporting Revocation in a Credential.
Once constructed, the [[ref issuer]] sends the credential to the [[ref: holder]] for verification and storage.
::: note
Please note the data attribute "birth_dateint" in the example above. The convention of
putting a _dateint
suffix on a credential attribute name is used to indicate
that the field contains a date in the form of an integer, such as "2022.11.21"
as the integer "20221121" (the number 20,221,121). By putting the date in that
form, AnonCreds predicates can be applied to the data, such as proving "older
than 21" based on date of birth without sharing the date of birth. This
convention was initially defined
here
by the Hyperledger Aries
community.
The credential signature elements are constructed as follows:
- Compute
$q = \frac{Z}{us^{v''}r^{m}_{linksecret}\ (Mod\ n)}$ where$v''$ is a random 2724-bit number with most significant bit as$1$ and$e$ is a random prime such that$2^{596} \leq e \leq 2^{596}+2^{119}$ - Compute
$a = q^{e^{-1}\ (Mod\ p'q')}\ (Mod\ n)$ where$p', q'$ are primes generated during issuer setup, and$e^{-1}$ is the multiplicative inverse of$e$ .
-
m_2
is a linkable identifier to the holder encoded in base 10 that is also called themaster_secret
in old versions. It is constructed as follows:-
$m_2 = H(i || \mathcal{H})$ , where$i$ is an index assigned to the holder, and$\mathcal{H}$ is an identifier with which the [[ref: holder]] is known to the [[ref: issuer]].
-
-
a
is the signature of the blinded known attributes. It's generation is given above. -
e
is a random prime generated by the [[ref: issuer]] for creating signature. -
v
is a number generated by the [[ref: holder]] to unblind the signature of the blinded attributes. It is constructed as follows:-
$v = v' + v''$ , where$v'$ is the blinding factor which the holder has and$v''$ is a random number generated by the issuer.
-
The credential signature correction proof elements are constructed as follows:
Using random nonce
from credential request and
se
is the credential signature correctness proof.c
is the witness for the credential signature correctness proof.
When a credential is revocable, in addition to the listed inputs needed for constructing a credential, the [[ref: issuer]] also needs the ID and private [[ref: Revocation Registry]] data. Using the inputs, the revocation-related fields in the credential JSON are populated. The following describes the elements and how they are produced.
rev_reg_id
is the ID of the [[ref: Revocation Registry Definition]] published on a [[ref: Verifiable Data Registry]] that is to be used by the [[ref: holder]] when trying to generate a Non-Revocation Proof for this credential as part of an AnonCreds presentation.
r_credential
is the following JSON data structure:
"r_credential": {
"sigma": "1 14C...8A8",
"c": "12A...BB6",
"vr_prime_prime": "0F3...FC4",
"witness_signature": {
"sigma_i": "1 1D72...000",
"u_i": "1 0B3...000",
"g_i": "1 10D...8A8"
},
"g_i": "1 10D7...8A8",
"i": 1,
"m2": "FDC...283"
}
The items in the data structure are:
-
c
: is a random number belonging in the group G2$(Mod\ q)$ -
vr_prime_prime
: is also a random number belonging in the group G2$(Mod\ q)$ -
sigma
: is calculated as$$\sigma = (h_0h_1^{m_2}\ .\ u_r\ .\ g_i\ .\ h_2^{v_r''})^{\frac{1}{x+c}}$$ where$h_0$ ,$h_1$ are from revocation public key,$u_r$ is from the blinded credential secrets,$g_i = g^{\gamma^i}$ where$i$ is the issuer's accumulator index,$h_2$ is from revocation public key, and$x$ is from the revocation private key. -
witness_signature
:-
sigma_i
: is calculated as$g'^{\frac{1}{sk+\gamma^i}}$ -
u_i
: is$u^{\gamma^i}$ -
g_i
: is a point in curve G1 which calculated by$g^{\gamma^i}$
-
-
g_i
: is a point in curve G1 which calculated by$g^{\gamma^i}$ -
i
:$i$ is the issuer's accumulator index -
m2
: is the credential context which acts as a linkable identifier to the holder.
rev_reg
is the following JSON data structure:
"rev_reg": {
"accum": "21 118...1FB"
}
The item in the data structure is:
accum
: is the accumulator value of the issuer which is updated with the new tails point as soon as new revocation credential is generated, and published to the public ledger.
witness
is the following JSON data structure:
"witness": {
"omega": "21 124...AC8"
}
The item in the data structure is:
-
omega
: is calculated by$\prod\limits_{j \in V} g'_{L+1-j+i}$ where$V$ is the current set of non revoked indices and$L$ is the number of indices contained in the accumulator.
On receipt of a credential from an [[ref: issuer]], the [[ref: holder]] must verify the credential and, if verified, will likely store the credential in a secure location.
To verify the signature_correctness_proof
, the [[ref: holder]] does the following:
- Verify that
$e$ is a prime and lies within it's range. - Compute $$ q \leftarrow \frac{Z}{S \prod\limits_{i in C_s} R_i^{m_i}} (Mod\ n)$$
- Verify
$q = a^e (Mod\ n)$ - Compute
$\hat{a} \leftarrow a^{c + s_e.e}(Mod\ n)$ - Verify
$c' = H(q || a || \hat{a} || n_1)$
The verifying and securely storing of the credential by the [[ref: holder]] completes the AnonCreds issuance process.
An AnonCreds credential is expected to be retained by the [[ref: holder]] that participated in the issuance process. The [[ref: holder]] should not transfer the credential to others for their use, and should only use the credential to generate an AnonCreds verifiable presentation, as outlined in the AnonCreds Presentation section of this specification.