Skip to content

Commit

Permalink
spec: progress on presentation proof
Browse files Browse the repository at this point in the history
  • Loading branch information
rot256 committed Oct 13, 2024
1 parent 7a0b62c commit 1187469
Show file tree
Hide file tree
Showing 3 changed files with 354 additions and 0 deletions.
153 changes: 153 additions & 0 deletions spec/design.md
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.
28 changes: 28 additions & 0 deletions spec/rfc.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,34 @@ but is unable to use the credential to sign transactions or otherwise impersonat
**Compromised Credentials:** We use the flexibility afforded by the Kimchi proof
system to reduce the practical impact of a compromised credential.


## Protocols

### Credential Types

At a high level, a credential is a collection of *attributes* along a proof and corresponding verification procedure:
checking the validity of the credential.

#### Native Credentials

A native credential is simply a native Mina signature on the set of attributes.
This type of credential is supported to allow Mina-native application to have very efficient credentials.

#### Recursive Credentials

A recursive credential is Kimchi proof taking the set of attributes as public input.
This type of credential is supported to accomodate any possible credential
and the ability to integrate existing credential systems (such as ePassport) into the Mina ecosystem
in a modular way.

- ECDSA signatures over foreign fields on JSON document (e.g. JSON Web Tokens),
- RSA signatures.
- Merkle tree inclusion/exclusion proofs.


### Issuance


## Motivation

The motivation behind extending the Mina wallet provider API stems from the evolving needs of the Mina ecosystem's zkApp landscape. Currently, there exists no standard for Mina wallets to interact with zkApps and attest to known private data. This limitation hinders the full potential of Mina's composable privacy feature, which is vital for user autonomy and data security.
Expand Down
173 changes: 173 additions & 0 deletions spec/spec.md
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.

0 comments on commit 1187469

Please sign in to comment.