Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[AIP-X] - Enable interoperability for Federated Keyless Accounts for the same issuer #526

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
170 changes: 170 additions & 0 deletions aips/aip-103.md
heliuchuan marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also describe the changes to the prover service's request format (see https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-75.md#request-format)?

I presume there are no changes to the pepper service, and if so, you should also briefly mention this and maybe discuss why we don't need any changes.

Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
---
aip: 10X
title: Enable interoperability for Federated Keyless Accounts for the same issuer (user-pool/tenant)
author: Oliver He ([email protected])
Status: Draft # | Last Call | Accepted | Final | Rejected>
last-call-end-date (*optional): <mm/dd/yyyy the last date to leave feedbacks and reviews>
type: <Standard (Core, Networking, Interface, Application, Framework) | Informational | Process>
created: 11/08/2024
updated (*optional): <mm/dd/yyyy>
requires (*optional): https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-103.md
---

# AIP-10X - Enable interoperability for Federated Keyless Accounts for the same issuer (user-pool/tenant)

## Summary

This AIP proposes enabling Aptos Federated Keyless to be interoperable with dApps from the same issuer (user-pool/tenant).

For IAM providers like Auth0 and Cognito, JWT tokens are scoped to a user-pool/tenant via the `iss` field, and they are also scoped to a specific application via the `aud` field. This means that JWTs from the same issuer but with different `aud` values are from different applications and cannot be used to derive the same Aptos Federated Keyless Account even though they represent the same user identity within the same user-pool/tenant.

Many customers of Auth0 and Cognito have applications with different branding within the same user-pool/tenant ecosystem. Thus it is natural for such customers to use different application identifiers for their applications for organizational purposes. This AIP will enable Aptos Federated Keyless Accounts to be interoperable across such applications.

## Impact and risks

- The relaxation of the `aud` field will allow for broader interoperability across applications within the same user-pool/tenant. This allows for broader adoption of Aptos Federated Keyless Accounts in such user ecosystems.

Risks
- Developers need to not use `aud`-less accounts when not appropriate. This can be mitigated by the Aptos SDK default behavior so that developers must explicitly enable using `aud`-less accounts.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Developers need to not use aud-less accounts when not appropriate" is not helpful.

You have to describe when it is and when it isn't appropriate.

- This introduces an additional proving path, one where the `aud` is not checked. It is important that such proofs are rejected if the account requires `aud` to be present, as encoded in the KeylessPublicKey.
heliuchuan marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please state more precisely? I assume you are talking about the proving service:

It is important that the prover service rejects audless proving requests for keyless accounts that require aud to be present and non-empty. This includes both normal keyless accounts (cite aip) and federated keyless accounts (cite aip).

I don't know how you can do such rejection though: the request does not include any information about the address (see here)

- As circuit changes are needed to support `aud`-less accounts, a new ceremony will be needed to generate the proving key and verification key.
- We want such accounts to be limited to Federated Keyless Accounts, as constructing Keyless Accounts without aud checks is unsafe. This can be mitigated by the Aptos SDK disallowing `aud`-less accounts from being used as Keyless Accounts. The prover will also reject proof requests for Keyless providers (as of now Google and Apple). However, in a world where 3rd party provers are permitted, we cannot prevent developers from using `aud`-less accounts as Keyless Accounts, but developers would not have any incentive to construct such accounts for their users (these accounts would be accessable by any other dApp, regardless of trust).
- The verification key will need an update, which will invalidate all existing proofs. Additionally the prover will need to start proving with the new proving key right away after the update. The prover has already been updated to support the proving key rotations and the SDK also supports state checks to invalidate old proofs.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"invalidate all existing proofs" --> not very clear what proofs you are referring to.

I think you mean "invalidate all current proofs that are waiting to be sent to the chain by dapps"



#### The updated keyless ZK relation $$\mathcal{R}$$

```math
\mathcal{R}\begin{pmatrix}
\mathsf{pih};\\
\textbf{w} = [
\textbf{w}_\mathsf{pub} = (
\mathsf{epk},
\mathsf{addr\_idc},
\mathsf{exp\_date},
\mathsf{exp\_horizon},
\mathsf{iss\_val},
\mathsf{extra\_field},
\mathsf{header},
\mathsf{jwk},
\mathsf{override\_aud\_val}
),\\
\textbf{w}_\mathsf{priv} = (
\mathsf{\textcolor{red}{skip\_aud\_check\_val}},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please rename this skip\_aud\_check? No _val is needed here. This is just a boolean.

\mathsf{aud\_key},
\mathsf{uid\_key},
\mathsf{uid\_val},
r,
\sigma_\mathsf{oidc},
\mathsf{jwt},
\rho
)
]
\end{pmatrix}
```

The **ZK relation $\mathcal{R}$** simply **performs the privacy-sensitive part of the verification** from the [leaky mode](#warm-up-leaky-signatures-that-reveal-the-users-and-apps-identity) above:

1. Verify that the public inputs hash $\mathsf{pih}$ is correctly derived by hashing the inputs in $\textbf{w}\_\mathsf{pub}$ with $H\_\mathsf{zk}$ (as explained above).
2. Check the OIDC provider ID in the JWT:
- Assert $\mathsf{iss\\_val}\stackrel{?}{=}\mathsf{jwt}[\texttt{"iss"}]$
3. If using `email`-based IDs, ensure the email has been verified:
- If $\mathsf{uid\\_key}\stackrel{?}{=}\texttt{"email"}$, assert $\mathsf{jwt}[\texttt{"email\\_verified"}] \stackrel{?}{=} \texttt{"true"}$
4. Check the user’s ID in the JWT:
- Assert $\mathsf{uid\\_val}\stackrel{?}{=}\mathsf{jwt}[\mathsf{uid\\_key}]$
5. Check the address IDC uses the correct values:
- Assert $\mathsf{addr\\_idc} \stackrel{?}{=} H'(\mathsf{uid\\_key}, \mathsf{uid\\_val}, \mathsf{aud\\_val}; r)$
6. If we are doing `aud` checks (i.e., $\mathsf{skip\\_aud\\_check\\_val} = \bot$)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change the color of this bullet to highlight the changes, or put everything in bold? You can keep the old stuff uncolored/unbolded. This will avoid repeating it below like you are doing now.

- *Then:* Are we in normal mode (i.e., we are not in recovery mode $\Leftrightarrow \mathsf{override\\_aud\\_val} = \bot$)
+ *Then:* check the managing application’s ID in the JWT: assert $\mathsf{aud\\_val}\stackrel{?}{=}\mathsf{jwt}[\texttt{"aud"}]$
+ *Else:* check that the recovery service’s ID is in the JWT: assert $\mathsf{override\\_aud\\_val}\stackrel{?}{=}\mathsf{jwt}[\texttt{"aud"}]$
- *Else:* assert $\mathsf{aud\\_val}\stackrel{?}{=}\texttt{""}$ (i.e. $\mathsf{aud\\_val}$ should equal the empty string).
> *Old version:*
>
> Are we in normal mode (i.e., we are not in recovery mode $\Leftrightarrow \mathsf{override\\_aud\\_val} = \bot$)
> + *Then:* check the managing application’s ID in the JWT: assert $\mathsf{aud\\_val}\stackrel{?}{=}\mathsf{jwt}[\texttt{"aud"}]$
> + *Else:* check that the recovery service’s ID is in the JWT: assert $\mathsf{override\\_aud\\_val}\stackrel{?}{=}\mathsf{jwt}[\texttt{"aud"}]$
7. Check the EPK is committed in the JWT’s `nonce` field:
- Assert $\mathsf{jwt}[\texttt{"nonce"}] \stackrel{?}{=} H’(\mathsf{epk},\mathsf{exp\\_date};\rho)$
8. Check the EPK expiration date is not too far off into the future:
- Assert $\mathsf{exp\\_date} < \mathsf{jwt}[\texttt{"iat"}] + \mathsf{exp\\_horizon}$
9. Parse $\mathsf{extra\\_field}$ as $\mathsf{extra\\_field\\_key}$ and $\mathsf{extra\\_field\\_val}$ and assert $\mathsf{extra\\_field\\_val}\stackrel{?}{=}\mathsf{jwt}[\mathsf{extra\\_field\\_key}]$
10. Verify the OIDC signature $\sigma_\mathsf{oidc}$ under $\mathsf{jwk}$ over the JWT $\mathsf{header}$ and payload $\mathsf{jwt}$.

## Alternative solutions

The alternative is to add an additional keyless public key type where the formula to compute the IdCommitment does not contain the `aud` at all.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IdCommitment


This is the advantage of explicit type safety as a completely new validation path would be implemented. There would be no risk of such proofs being accepted for accounts that require `aud` to be present due to explicit differences in how the proof would be gated on the type of public key.

However the drawbacks include:
- We need to add a new keyless public key type, which may not be needed if we can leverage the existing design. And avoiding proliferation of keyless public key types is desirable.
heliuchuan marked this conversation as resolved.
Show resolved Hide resolved
- Requiring implementation of a new authentication path in the authenticator, which may be error prone and takes additional engineering effort.
- Requires more complex changes to the prover as it would need to support a different public inputs hash calculation in order to differentiate between accounts with and without `aud`. Or it would need to use a different circuit version entirely.

Thus if we can leverage the existing design, it would be preferable to do so.

## Specification and Implementation Details

This AIP's implementation has three parts -

1. We add an additional private input into the prover. This value will indicate whether the `aud` check is enabled.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not name this additional private input? skip_aud_checks

This will make it more clear to the reader.

Copy link
Contributor

@alinush alinush Nov 26, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But you have to adjust the language, since here you are confusingly talking about a (non-existant) dont_skip_aud_checks input.

- If it is enabled, the circuit will do the status quo set of verifications.
- If it is disabled, the circuit will use an empty `aud` private input (as provided by the prover) and skip checking the `aud` field. A valid JWT still needs to be provided and the public inputs hash calculation will still implicitly include the `aud` field via the `IdCommitment` in the `KeylessPublicKey`, but it will be set to the empty value. This means that proofs for `aud`-less accounts will be rejected if the account requires `aud` to be present, as the `IdCommitment` will be different.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again "skip checking the aud field" is not precise enough.

More precise would be: "skip matching the JWT's aud field with the aud value committed in the IdCommitment"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"the public inputs hash calculation will still implicitly include the aud field " --> Yes, but which aud field? Again, there are 3 flying around.

The same confusion later: "the account requires aud to be present"

I think you should rewrite this whole paragraph from scratch. Describe everything succinctly and clearly. Don't ever mention an unqualified aud field.


2. The prover API will also require an update to allow for indiciating whether the `aud` check is enabled. This will be done by adding a new boolean argument to the `prove` API.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned above, you should show the new API and highlight the changes. This will serve as a good reference for us too.


3. The SDK will also need to be updated to support instantiating of such accounts. This will require adding a new boolean argument to the `KeylessAccount` constructor and constructing the `KeylessPublicKey` and `AccountAddress` appropriately.

## Testing (Optional)

1. Write unit tests for the circuit to verify that it correctly handles the `aud` check.
2. Write unit tests for the SDK to verify that it correctly instantiates accounts with and without `aud` checks.
3. Do a manual end-to-end test in devnet/testnet via the SDK once the verification key is updated.
4. Write smoke tests ensuring that `aud`-less accounts are rejected if the account requires `aud` to be present.

## Security Considerations

The core security considerations are:
- Making sure the the circuit can securely support `aud`-less accounts.
- Making sure that such proofs are rejected if the account requires `aud` to be present (as encoded in the `KeylessPublicKey`s `IdCommitment`).
heliuchuan marked this conversation as resolved.
Show resolved Hide resolved

## Future Potential

This will allow onboarding more users into the Aptos blockchain via keyless accounts[^aip-61] and its extensions.

## Timeline

-Circuit changes: End of October 2024.
-Ceremony completion: End of November 2024.
-SDK update: by ceremony completion.
-Prover service update: by ceremony completion.
-Devnet verification key update: After ceremony completion.
-Devnet testing: After verification key update. Should take a few hours.
-Testnet verification key update: After devnet testing.
-Testnet testing: After testnet verification key update.
-Mainnet verification key update proposal: End of November 2024.
-Mainnet verification key update: A week after proposal submission. Estimated early December 2024.

### Suggested implementation timeline

See above.

### Suggested developer platform support timeline

Already supported via telegram.

### Suggested deployment timeline

See above.

## References

[^aip-61]: https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-61.md
[^aip-67]: https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-67.md
[^aip-75]: https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-75.md
[^aip-81]: https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-81.md
[^aip-61-recovery]: https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-61.md#recovery-service
[^jwks]: https://appleid.apple.com/.well-known/openid-configuration
[^passkeys]: https://github.com/aptos-foundation/AIPs/blob/main/aips/aip-66.md
[^ppid]: https://openid.net/specs/openid-connect-core-1_0.html#Terminology