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

Trusted Keys mongo database cache is misbehaving #340

Open
Zicchio opened this issue Jan 28, 2025 · 3 comments
Open

Trusted Keys mongo database cache is misbehaving #340

Zicchio opened this issue Jan 28, 2025 · 3 comments
Labels
bug Something isn't working trust

Comments

@Zicchio
Copy link
Collaborator

Zicchio commented Jan 28, 2025

When recoving trusted keys from a credential issuer, in the original deisgn the keys where cached at the database level as a FALLBACK strategy for situations where they cannot be remotedly fetched (for example, the issuer is temporarely offline ad the /.well-known/openid-federation or /.well-known/jwt-vc-issuer endpoint).

However, the current implementation does not use the cache as fallback, but as a primary recovery strategy. As such, if the creddential issuer changes signing key, we are unable to find the new remote key until the cache is manually flushed from the mongo database.

This is the bad code: when looking at the trusted material, first we look at the DB (line 85) and then interrogate the trust evaluation mechanism only when this is not possible

def _get_trust_source(self, issuer: str) -> TrustSourceData:
"""
Retrieve the trust source from the database or extract it from the trust handlers.
:param issuer: The issuer
:type issuer: str
:returns: The trust source
:rtype: TrustSourceData
"""
trust_source = self._retrieve_trust_source(issuer)
if not trust_source:
trust_source = self._upsert_source_trust_materials(issuer, trust_source)
return trust_source

@Zicchio Zicchio added bug Something isn't working trust labels Jan 28, 2025
@italia italia deleted a comment Jan 28, 2025
@peppelinux
Copy link
Member

The issuer's data is requested from the DB (upsert) based on an obtained credential. Ideally, the key ID can be provided in advance to the call to facilitate the update in a single step.

  1. I obtain the presented credential, apply the parsing class, and retrieve the cryptographic material.
  2. I establish trust with the issuer and pass the cryptographic material obtained from the credential into the method call to obtain the cryptographic material.
  3. If it matches, it's okay.
  4. If it matches but the statement is expired, update the statement.
  5. If it differs, perform an HTTP fetch of the statement, update, and return.

Evaluate and open an issue to consolidate this approach.

@Zicchio
Copy link
Collaborator Author

Zicchio commented Jan 29, 2025

A furhter issue emerged with the cache.
The keys in the mongo cache are not associated to the trust evaluation mechanism that produced them. This could raise some security issue in the future.
For example, if key K (already cached) is eventually no longer trusted by the trust evaluation mechanism T that initially produced it (maybe because the key rotated or because it was compromised), then we are not able to say "hey T please remove K from the cache" or "hey T please flush your own keys from the cache" because we do not know in the first place which keys are owned by T in the cache. Without such mechanism, attackers would be able to present tokens signed with key known to be compomised and we would not realize it.

IMO this problem should also be considered when considering a new solution for the cache usage.

@Zicchio
Copy link
Collaborator Author

Zicchio commented Jan 29, 2025

1. I obtain the presented credential, apply the parsing class, and retrieve the cryptographic material.

It is not clear to me how the parser should be able to retrieve the cryptographic material. Do you mean the cryptographic material from the cache?
If it a self contained token, so something like x5c or jwk header can be used, then ok. If we have an identifier like kid then the parser does not know where to look for (likely we need to make a GET request so some well-known endpoint, or something equivalent). The problem "where to look for" is currently delegated to the TrustHandlers/TrustEvaluator, that is, it is the trust evaluation mechanism responsability to retrieve the cryprographic material (his choice if he does from the cache or from a remote source).

2. I establish trust with the issuer and pass the cryptographic material obtained from the credential into the method call to obtain the cryptographic material. 

Assuming we have the cryptographic material, then we would need to develop a method like "is this key valid according to your own trust mechanism", right?
This initially make sense but has some unexpected side effect because the token verifier has to be aware of the fact we could have B class keys (possible stale) coming from the cache vs A class keys that are fresh and produced the trust evaluation mehcanism. This is clear when [... goes on after bullet 5]

3. If it matches, it's okay.

4. If it matches but the statement is expired, update the statement.

5. If it differs, perform an HTTP fetch of the statement, update, and return.

[...] we try to think what happens here. The token verifier now should be aware of the nuances of the token cache and is the token verifier responsability to update the cache.
This is doable but I'd like to think first about a solution that clearly separate responsabilities so that the token verifier does not need to be aware of the fact that we might have a cache of keys that he should manage and update.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working trust
Projects
Status: Todo
Development

No branches or pull requests

2 participants