Anonymous credential scheme based on https://eprint.iacr.org/2017/115.pdf
const issuer = new Issuer('./some-storage-path')
const schema = {
"age": "number",
"nationality": "string",
"residence": "string",
"drivers licence": "boolean",
"employed": "boolean",
"gender": "string"
}
issuer.addCertification(schema, (certId) => {
// do something with certId, e.g list online
// answer a verifier's request for this certification
const certInfo = issuer.getCertInfo(certId)
})
Now the issuer is ready to grant credentials from the certification.
A new verifier must first add the certifications it is willing to accept. They may request the necessary info associated with certId
from the issuer
const verifier = new Verifier('./some-other-storage')
verifier.registerCertification(certInfo, () => {
// verifier online
})
The user should have obtained certId
from a public list. Now they may send an application for a credential:
const user = new User()
const application = {
"age": 66,
"nationality": "italy",
"residence": "austria",
"drivers licence": true,
"employed": true,
"gender": "male"
}
/* --- Client Side --- */
const app = user.apply(application, certId)
(app, { metadata }) --> server // metadata should be proof of ID
/* --- Server Side --- */
(app) => {
const issuanceInit = issuer.addIssuance(app)
}
(issuanceInit) --> client
/* --- Client Side --- */
(issuanceInit) => {
const issuanceResponse = user.obtain(issuanceInit)
}
(issuanceResponse) --> server
/* --- Server Side --- */
(issuanceResponse) => {
const final = issuer.grantCredential(issuanceResponse)
}
(final) --> client
/* --- Client Side --- */
(final) => {
user.store(final)
}
Now the user has a credential, which they may present to a verifier:
/* --- Client Side --- */
const transcript = user.present(['age', 'nationality'])
(transcript) --> verifier
/* --- Server Side --- */
verifier.validate(transcript, (err, identifier) => {
if (err) {
// handle reject user
}
// identifier should be associated with this user
// as it is needed to report malicious parties
})
If a malicious user is detected, the transcript
associated with their credential may be reported to the issuer and, if appropriate, the issuer may revoke the entire credential:
/* --- Verifier --- */
(identifier, { incidentReport }) --> issuer
/* --- Issuer --- */
// check incidentReport, if user is at fault:
(identifier) => {
issuer.revokeCredential(identifier, (err) => {
// user has now been revoked
})
}
Instatiate a new Issuer instance. storage
designates the path which shall be used to store revocation list information.
Begin a new issuance protocol. This method takes a user's application
, which is the output of user.apply
and outputs a setup
object, encoding blinded curve points that are used to generate the credential, which may be passed straight to the user.
This is the Issuer's final step during issuance and takes the output of user.obtain
. In this step the Issuer contributes entropy towards the users credential and seals the credential by exponentiating the product of all curve points in the credential by the Issuer's secret key. This term is used in a bilinear pairing equality to verify the sum of exponents during verification of the credential.
Revoke a credential associated with a given identifier
. This method shall publish the root id associated with this key to the certifications revocation list, anyone subscribed to the revocation list may then derive all keys associated with the root id and checks against these keys during verification.
Register a new certification. Takes a JSON schema
specifying field titles and types and returns the resulting ertification's certId
, a unique identifier string, to the callback provided.
The certification is stored in issuer.certifications
under it's certId
and may be accessed by issuer.certifications[certId]
.
Get the public keys and revocation list informnation associated with a given certId
. This info is passed to a verifier for them to recognise new certifications. certInfo
is returned as a buffer
containing the serialized information to be passed to a verifier.
Instantiate a new User.
Generate an application with the relevant details to send to the Issuer responsible for certId
. When sending this to the issuer, this should be accompanied by a document proving these properties, e.g. photo ID.
The user's contribution in the issuance protocol. This takes the output of issuer.addIssuance
as a buffer
and returns an buffer
containing the serialized response.
In this step the user generates random scalars used to exponentiate the blinded curve points received from addIssuance
message and returns them to the issuer, thereby contributing her own entropy to the certificate.
Store a completed credential. msg
should be a buffer
outputted by a call to issuer.grantCredential
, which contains the serialization of the finalised credential and the issuer's signatures for all pseudonyms associated with this id.
This will be stored internally as a new Identity
, which has associated with it a credential as well as the root
from which all pseudonyms are derived. This Identity
can be later accessed using the findId
method below or accessed directly from user.identities
.
Generate a transcript showing a valid credential, only disclosing the properties specified in attributes
. properties
should be passed as an array
of strings
, e.g ['age', 'nationality']
; an appropriate identity with the required attributes is then chosen to present.
Returns a buffer
containing the serialized data required by a verifier to validate the credential. transcript
should be passed as an argument to verifier.validate
Access an identity containing the attributes listed in required
. Takes an array
of strings
, e.g ['age', 'nationality']
, and returns id
as an instance of an Identity
object.
Instantiate a new Verifier. storage
should be a path designated where revocation list data shall be stored.
Validate a given transcript
, which is the buffer
returned by user.present
. cb
should have the signature cb(err)
. An error message shall be passed to cb
if validation fails.
identifier
is needed when reporting bad users to the issuing party, therefore it should be associated with that user account.
Recognise a new certification. cert
is a buffer
as outputted of org.getCertInfo(certId)
, containing the serialization of the certification public keys and the information needed to sync the revocation list
associated with the certification.
The credential scheme itself is described in An efficient self-blindable attribute-based credential scheme. The principle is that each field has an associated curve point in G1. The user's attributes are encoded as scalars and used exponentiate their associated curve point. Express the product of these exponentiated curve points as C
, the issuer provides a signature over this product by exponentiating C
by a secret key, z
, yielding T = C * z
. The user's public key is the tuple of curve points in G1 and the corresponding secret key is the tuple of associated attributes encoded as scalars.
When showing the credential, the user presents it's public key and the attributes they wish to disclose. We exponentiate the curve points associated with disclosed attributes and negate the terms and then calculate the product of these terms as D = S1-k1 * S3-k3 * ... * Si-ki (for all i in disclosure set). To prove they know the undisclosed terms, the user can negate C and demonstrate proof of knowledge of all terms needed to satisfy: D = C-1 * S0k0 * S2k2 * ... * Sjkj (for all j in undisclosed set).
Once this has been proved, the verifier uses a bilinear pairing to check that the relationship T = C * z
still holds and a second pairing to verifier the user's public key against the issuers public key.
Because bilinear pairing is verifying the product of exponents, we may exponentiate both sides of the equality with the same blinding factor whilst maintaining the pairing equality. This allows the user to 'self blind' their credential, thus ensuring unlinkability between separate showings.
The unlinkability presents an issue in the case of a bad acting user. A service may want to ban a user, but since each showing is unlinkable, they have no way of distinguishing whether a credential belongs to this user. Therefore, a set of pseudonyms are generated from a unique root and signed by the Issuer for each credential. Each pseudonym has a keypair associated with it and each credential showing is signed by the pseudonym's keypair. The user then presents the verifier with the transcript
, transcriptSignature
, pseudonymPublicKey
and certSig
. The certSig
is first validated over the pseudonymPublicKey
against the organisation's key to make sure this pseudonym is certified. Then the transcript signature is validated, and finally the credential showing is validated.
A service can report a pseudonymPublicKey
to the Issuer to have them revoked. The Issuer may then determine which identity this pseudonym belongs to and publish the root
to a revocation list associated with the certification. Verifiers interested in this certification subscribe to the revocation list and calculate the pseudonymPublicKey
s associated with the revocation listed user and can check each new user against this list.