-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
spec: progress on presentation proof
- Loading branch information
Showing
3 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# The Design of Mina Credentials | ||
|
||
This document is a high-level overview of the Mina Credentials system, | ||
its relation to other systems, its (informal) design goals and rationale for the design choices made in the system. | ||
The goal of this project is to design a system sufficiently flexible to encompass any "anonymous credential"-style application, | ||
while unifying the user interface, API and design with the aim of providing a clear specification with minimal footgun potential. | ||
|
||
This is achieved by using the recursive proofs of Mina extensively: | ||
seperating the "creation" of the credential from the "presentation" of the credential: | ||
besides unifying the presentation proof, it also allows doing most of the expensive operations (e.g. verifying an RSA signature using SHA256 and parsing a JSON object) once during the creation of the credential. | ||
|
||
At the highest possible level of abstraction, Mina credentials | ||
are a set of "attributes" (e.g. name, age, SSN) attested to by a "issuer". | ||
The issuer is an opaque entity (a hash) and may identify e.g. the root of a Merkle tree, | ||
the root authorities of a PKI, the hash of Google's OAuth public key, etc. | ||
By exploiting the recursive proofs of Mina, this diverse set of issuers / applications | ||
can be brought into a standard form: a SNARK on a hash of the attributes. | ||
All these credentials can also be stored/verified/used in the same way. | ||
This provides a plug-and-play system: | ||
allowing developers to create new credential types and application logics seperately, combining them in a safe, modular way. | ||
|
||
# Related Works & Systems | ||
|
||
## [zkLogin](https://docs.sui.io/concepts/cryptography/zklogin) | ||
|
||
zkLogin | ||
|
||
## [zkPassport](https://zkpassport.id/) | ||
|
||
zkPassport is based | ||
|
||
## [World ID and Semaphore](https://worldcoin.org/blog/worldcoin/intro-zero-knowledge-proofs-semaphore-application-world-id) | ||
|
||
|
||
The goal of Mina credentials is broader than these systems and must enable | ||
the implementation of these systems within the Mina ecosystem. | ||
|
||
# Design Rational | ||
|
||
The cryptography team considered two options for the presentation proof. | ||
|
||
1. Credentials are "bearer tokens": simply knowing the credential is sufficient to present it. | ||
2. Credentials are associate attributes/capabilities to public keys. | ||
|
||
To explain our choice, let us first explore the two options in more detail. | ||
|
||
## Option 1: "Bearer Tokens" | ||
|
||
In this scenario, the simpler of the two, knowing the credential is equivalent to owning it. | ||
Let us make that more concrete. For instance, the credential might simply be a signature over a set of attributes: | ||
|
||
```javascript | ||
cred = { | ||
'attributes': { | ||
'name': 'Alice', | ||
'age': 25, | ||
'ssn': '123-45-6789' | ||
}, | ||
'signature': '---signature---' | ||
'issuer': '---issuer public key---' | ||
} | ||
``` | ||
|
||
Knowing this signed object is equivalent to "being" Alice. | ||
To "present" the credential, to e.g. show that Alice is over 18, Alice creates a proof of the following relation: | ||
|
||
```javascript | ||
// verify the issuer signature | ||
cred.signature.verify(cred.issuer, cred.attributes); | ||
|
||
// check the issuer | ||
assert(cred.issuer == issuer); | ||
|
||
// verify the claim | ||
assert(cred.attributes['age'] >= 18); | ||
``` | ||
|
||
Using the context of the presentation as an public input to the proof: | ||
the context is a domain-specific hash of the context in which the credential is presented, | ||
incorporating the entity to which the credential is presented, a nonce to avoid replay attacks and additional information as needed. | ||
We describe this in more detail in the formal specification. | ||
|
||
## Option 2: "Associated Attributes" | ||
|
||
In this scenario, the credential is associates a set of attributes with a public key. | ||
Knowning the corresponding signing key allows one to present the credential. | ||
To make this more concrete, the credential might look like this: | ||
|
||
```javascript | ||
cred = { | ||
'owner': '---owner public key---', | ||
'attributes': { | ||
'name': 'Alice', | ||
'age': 25, | ||
'ssn': '123-45-6789' | ||
}, | ||
'signature': '---signature---' | ||
'issuer': '---issuer---' | ||
} | ||
``` | ||
|
||
To "present" the credential, Alice creates a proof of the following relation: | ||
|
||
```javascript | ||
// verify the issuer signature | ||
cred.signature.verify(cred.issuer, cred.owner, cred.attributes); | ||
|
||
// verify the owner signature | ||
ownerSignature.verify(cred.owner, [cred.attributes, cred.issuer, context]); | ||
|
||
// check the issuer | ||
assert(cred.issuer == issuer); | ||
|
||
// verify the claim | ||
assert(cred.attributes['age'] >= 18); | ||
``` | ||
|
||
Again, using the context as an public input to the proof: but now the context is also signed by the owner | ||
and the signature is verified in-circuit. | ||
|
||
## Justification | ||
|
||
We deemed that the additional complexity of the second option, | ||
an in-circuit verification of a native signature, | ||
is outweighed by the following benefits: | ||
|
||
- Allow use of existing infrastructure for key management. | ||
Including hardware enclaves and the ability to authortize presentations efficiently using MPC: | ||
authortization requires the parties to threshold sign using Schnorr. | ||
An untrusted party can be asked to compute the actual proof at the cost of privacy. | ||
|
||
- Out-sourcing the computation of the presentation proof is possible at the cost of privacy: | ||
the user must reveal the credential and the context to the prover, but the prover cannot impersonate the user or change the intented action. | ||
This is useful in scenarios where the prover is a resource-constrained device. | ||
|
||
- A compromise of the credential object itself does not allow impersonation. | ||
|
||
- Easy integration with the existing Nullifier system within Mina: every credential comes with a public key | ||
and nullifiers can be computed / exposed against this public key to allow linkability when desired. | ||
|
||
- From a theorectical/robustness perspective, a small benefit is that we can assume weaker properties of the proof system: | ||
the first scheme requires [(weak) simulation extractability](https://eprint.iacr.org/2020/1306.pdf) since the "context". | ||
|
||
We obtain a design in which the SNARK serves only to hide the | ||
|
||
We show that if even if the SNARK prover is malicious, the credential remains unforgeable: | ||
this is useful in applications such as zkLogin, where for practicality reasons, the proving is outsourced to a third party in practice. | ||
We want to allow the Mina ecosystem this option should it be relevant to particular applications. | ||
|
||
We note that for some applications, most notably zkPassport, secure outsourcing is inherently not possible when *creating the credential*: | ||
since knowledge of the witness (the digitial signature on the ePassport) identifies the owner. | ||
In such applications we must rely on the security of the SNARK prover during the creation of the credential, | ||
however, the presentation proof remains secure (unforgeable) even if the SNARK prover is malicious. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
# Technical Specification for Mina Credentials | ||
|
||
This document is a low-level technical specification for the Mina Credentials system. | ||
It is intended as document for the accompanying codebase and implementators. | ||
It does not include security proofs or motivations for the design choices, | ||
see the RFC for such discussions. | ||
|
||
# Protocol: Presenting Credentials | ||
|
||
The application logic only interacts with the `Credential` struct: | ||
|
||
```javascript | ||
Attributes { | ||
owner: PublicKey, // the owners public key | ||
attrs: Attributes, // hidden attributes (e.g. age, name, SSN) | ||
} | ||
``` | ||
|
||
```javascript | ||
Credential { | ||
owner: PublicKey, // the owners public key | ||
attributes: Attributes, // hidden attributes (e.g. age, name, SSN) | ||
} | ||
``` | ||
|
||
```javascript | ||
PublicInput { | ||
context: Field, // context : specified later | ||
claims: Claims // application specific public inputs. | ||
} | ||
``` | ||
|
||
This means that the application logic does not need to know about the underlying cryptographic primitives | ||
and is pluggable between the simple and recursive credentials. | ||
The public input for both credential types is: | ||
|
||
The issuing authority is either: | ||
|
||
- A hash of a public key for simple credentials. | ||
- A hash of a sequence of public inputs and verification keys for recursive credentials: | ||
binding the credential to a specific verication logic (e.g. a circuit implementing RSA verification) and input (e.g. hash of Google's RSA public key). | ||
|
||
## Circuit: Present Simple Credential | ||
|
||
A standardized circuit for presenting simple credentials. | ||
|
||
The circuit verifies two signatures: one from the issuer and one from the owner. | ||
|
||
```javascript | ||
// the private inputs for the circuit | ||
PrivateInput { | ||
credential: Credential, | ||
issuerPk: PublicKey, | ||
issuerSignature: Signature, | ||
ownerSignature: Signature, | ||
} | ||
|
||
// hash the credential | ||
let credHash = Poseidon.hashPacked(Credential, credential); | ||
|
||
// verify the credential issuer signature | ||
issuerSignature.verify(issuerPk, credHash); | ||
|
||
// convert issuerPK to opaque field element | ||
let issuer = Poseidon.hashWithPrefix( | ||
"mina-cred:v0:simple", // sep. the domain of "simple" and "recursive" issuers | ||
issuerPk | ||
); | ||
|
||
// verify the credential owners signature | ||
ownerSignature.verify(owner, [credHash, issuer, context]); | ||
|
||
// verify application specific constraints using the standard API | ||
applicationConstraints( | ||
credential, // hidden attributes/owner | ||
issuer, // potentially hidden issuer | ||
claims, // application specific public input | ||
) | ||
``` | ||
|
||
## Circuit: Present Recursive Credential | ||
|
||
A standardized circuit for presenting recursive credentials. | ||
|
||
The circuit verifies a proof "from" the issuing authority and a signature from the owner. | ||
|
||
```javascript | ||
// the private inputs for the circuit | ||
PrivateInput { | ||
vk: VerificationKey, | ||
credIdent: Field, | ||
credProof: Proof, | ||
credential: Credential, | ||
ownerSignature: Signature, | ||
} | ||
|
||
// hash the credential | ||
let credHash = Poseidon.hashPacked(Credential, credential); | ||
|
||
// verify the credential proof | ||
credProof.publicInput.assertEquals([credHash, credIdent]); | ||
credProof.verify(vk).assertEqual(true); | ||
|
||
// the issuer is identified by the recursive relation and public input | ||
let issuer = Poseidon.hashWithPrefix( | ||
"mina-cred:v0:recursive", // sep. the domain of "simple" and "recursive" issuers | ||
[vk.hash, credIdent] // identifies the issuing authority / validation logic | ||
); | ||
|
||
// verify the credential owners signature | ||
ownerSignature.verify(owner, [credHash, issuer, context]); | ||
|
||
// verify application specific constraints using the standard API | ||
applicationConstraints( | ||
credential, // hidden attributes/owner | ||
issuer, // potentially hidden issuer | ||
claims, // application specific public input | ||
) | ||
``` | ||
|
||
# Context Identifiers | ||
|
||
The verifier computes the context (out-of-circuit) as: | ||
|
||
```javascript | ||
context = Poseidon.hashWithPrefix( | ||
"mina-cred:v0:context", // for versioning | ||
[ | ||
vk.hash, // the verification key hash (of the presentation proof) | ||
claims, // the public input (the set of "claims" being presented) | ||
nonce, // a random nonce | ||
verifier, // a URI for the verifiers identifier (see below) | ||
action, // the "action" being performed (e.g. login, transaction hash etc.) | ||
] | ||
) | ||
``` | ||
|
||
The nonce MUST be a uniformly random value generated by the prover. | ||
|
||
### Web Application | ||
|
||
[Uniform Resource Identifier](https://datatracker.ietf.org/doc/html/rfc3986) | ||
|
||
```javascript | ||
let verifier = Keccak256.hash("https://example.com/verify"); | ||
``` | ||
|
||
The scheme MUST be `https`. | ||
|
||
# Discussion | ||
|
||
Discuss the following with Gregor: | ||
|
||
1. Should the `issuer` be a struct instead? (e.g. `Issuer { pk: PublicKey, signature: Signature }`) | ||
1. What is the standard way to provide domain-specific for signautures in the Mina ecosystem? should we do: | ||
``` | ||
m = Poseidon.hashWithPrefix("mina-cred:v1:", [credHash, issuer, context]); | ||
signature.verifySignedHashV2(message, m); | ||
``` | ||
1. Discuss [Nullifiers](https://github.com/o1-labs/o1js/issues/756) in the context of Mina Credentials. | ||
|
||
# Example: Merkle-Tree Credential | ||
|
||
# Example: RSA Credential | ||
|
||
# Example: PKI Credential | ||
|
||
# Bearer Credentials to Mina Credentials | ||
|
||
In some applications, e.g. zkPassport (ICAO), knowledge of the signed object constitutes the credential. | ||
To reduce the window of attack (e.g. avoid storing passport scans) and bring the credential into the Mina Credentials system, | ||
a public key must be bound to the knowledge of this signed object. |