Skip to content

Conversation

TimoGlastra
Copy link
Contributor

@TimoGlastra TimoGlastra commented Feb 22, 2025

Still quite some stuff to do, but wanted to open as draft so it's easy to track my progress / see the changes.

TODO:

  • signing / verification / key management interface and implementation for Node.JS, React Native Secure Env and Askar
  • dynamically selecting backends based on public key or keyId being used
    • I'm still interested to see if we can have a more efficient process for this. At least caching the results in Redis/local cache
  • 🚧 encryption / decryption interfaces (currently working on)
    • Thinking how we want to handle symmetric key creation. It's basically just bytes, so need to think whether providing the alg at creation time makes sense. For ES256 you provide a crv of P-256. For A256GCM you need to create a 256 bits key. For A128CBC-HS256 you need a 384 bits key, etc. But it's good to probably lock the key to a specific alg? that's also what askar does. Input welcome
  • 🚧 encryption / decryption implementation for Node.JS and Askar (currently working on Node.JS)
  • Replace all usages of the old wallet API with the new KMS API
    • Main thing is new API is more flexible but also more complex. Thinking if we need to keep 'simple' api based on KeyType
  • Handle old keys without keyId
    • Probably we need to assume the keyId is the publicKeyBase58 of the compressed key if not provided, as that's what we used before by default. But would be great to at some point remove implicit keyIds (I think at some point the user could provide it themselves).
  • 🚧 Allow modules to dynamically register metadata in a TenantRecord/move it to a record within the tenant storage (started on this, not finished yet)
  • Update from valibot to Zod
  • Remove old wallet API
  • Ask the backend if it can perform operations based on public key and alg
    • this will prevent e.g. the first backend from handling a verification for BLS, while it doesn't support BLS.

Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Copy link

changeset-bot bot commented Feb 22, 2025

🦋 Changeset detected

Latest commit: e6f74f2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@credo-ts/core Minor
@credo-ts/indy-sdk-to-askar-migration Minor
@credo-ts/question-answer Minor
@credo-ts/react-native Minor
@credo-ts/action-menu Minor
@credo-ts/anoncreds Minor
@credo-ts/openid4vc Minor
@credo-ts/indy-vdr Minor
@credo-ts/didcomm Minor
@credo-ts/tenants Minor
@credo-ts/askar Minor
@credo-ts/cheqd Minor
@credo-ts/drpc Minor
@credo-ts/node Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

* @throws {AskarStoreNotFoundError} if the wallet does not exist
* @throws {AskarStoreError} if another error occurs
*/
public async rotateStoreKey(options: AskarStoreRotateKeyOptions): Promise<void> {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Askar specific operations will now be part of the askar api. So there's no global rotateWalletKey anymore, as that's specific to Askars way of doing this.

}
}

public async onProvisionContext(agentContext: AgentContext) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added new module methods:

  • onProvisionContext
  • onCloseContext
  • onDeleteContext
  • onInitializeContext

this will allow for lifecycle handling of specific contexts

Copy link
Contributor

Choose a reason for hiding this comment

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

This is really great!

ProfilePerWallet = 'ProfilePerWallet',
}

export interface AskarModuleConfigStoreOptions {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wallet config is now askar specific

Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
…ore generic, support multiple backends at the same time, support generic encrypting and decryption, support symmetric keys, and enable backends that use key ids rather than the public key to identify a key. This has resulted in significant breaking changes, and all usages of the wallet api should be updated to use the new `agent.kms` APIs. In addition the wallet is not available anymore on the agentContext. If you used this, instead inject the KMS API using `agentContext.resolve(Kms.KeyManagementApi)`.

Signed-off-by: Timo Glastra <timo@animo.id>
…our of a new `PublicJwk` class, and all APIs in Credo have been updated to use the new `PublicJwk` class. Leveraging Jwk as the base for all APIs provides more flexility and makes it easier to support key types where it's not always so easy to extract the raw public key bytes. In addition all the previous Jwk relatedfunctionality has been replaced with the new KMS jwk functionalty. For example `JwaSignatureAlgorithm` is now `Kms.KnownJwaSignatureAlgorithms`.

Signed-off-by: Timo Glastra <timo@animo.id>
…that all DIDs have an associated `DidRecord` with the created role. With the new KMS API we now need to keep track of key ids for keys within a did document, and these are stored on the did document. You can import a did using `agent.dids.import` and provide the `keys` array to define the mapping between verification method and key id. If a verification method mapping to key id is not provided in the did record, we will assume the legacy key id format is used (the base58 encoded public key)

Signed-off-by: Timo Glastra <timo@animo.id>
…reference a key id. For DIDs this is extracted from the DidRecord, and for JWKs (e.g. in holder binding) this is extracted form the `kid` of the JWK. For X509 certificates you need to make sure there is a key id attached to the certificate manually for now, since we don't have a X509 record like we have a DidRecord. For x509 certificates created before 0.6 you can use the legacy key id (`certificate.keyId = certificate.publicJwk.legacyKeyId`), for certificates created after 0.6 you need to manually store the key id and set it on the certificate after decoding.

Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
…nt config, to allow for more flexibility. Instead, each module can now define their own config for the storage and kms. For askar there is a new `store` property which must be provided on the askar module config where you can set the wallet id and key. It is also possible to disable the kms or storage for askar using `enableKms` and `enableStorage`.

Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
@TimoGlastra TimoGlastra marked this pull request as ready for review May 12, 2025 15:43
@TimoGlastra TimoGlastra requested a review from a team as a code owner May 12, 2025 15:43
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Copy link
Contributor

@berendsliedrecht berendsliedrecht left a comment

Choose a reason for hiding this comment

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

611 files, Only took me half a day :).

I think there are a lot of good, and necessary, changes here. The main things I think can be better DX-wise, are the import key API to also accept privatekey bytes instead of converting it manually before to a jwk, using keyType instead of kty (curve instead of crv, etc.) when importing a key. This makes more sense to me as we pass them outside of the jwk object/class I think. (If this is incorrect, feel free to ignore, its hard to remember the details haha). And the randomBytes API might need an async counterpart for the use of remote CSPRNGs, or just any async one.

*
* @default 'kdf:argon2i:mod'
*/
keyDerivationMethod?: `${KdfMethod.Argon2IInt}` | `${KdfMethod.Argon2IMod}` | `${KdfMethod.Raw}`
Copy link
Contributor

Choose a reason for hiding this comment

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

I think if we add the None here, we resolve #2277

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it ok to do this in a follow up PR? It will require additional tests to make sure it works and don't want to expand the scope any further

Copy link
Contributor

@genaris genaris left a comment

Choose a reason for hiding this comment

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

Amazing work, @TimoGlastra ! I think it is a big step towards making the framework more flexible and moving away from legacy Askar storage

@@ -91,6 +91,9 @@ export async function getConnectionlessOutboundMessageContext(
`Creating outbound message context for message ${message.id} using connection-less exchange`
)

// FIXME: we should remove support for the flow where no out of band record is used.
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree but we should deprecate connections API first I guess. If we do a refactor merging functionalities from connections and oob for 0.6, probably it is safe to remove this support in 0.7

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can already deprecate this flow i think? This is where you receive a connectionless legacy message by calling receiveMessage on the message receiver directly, and thus OOB is skipped.

This flow is already deprecated for quite a while, and you should just use agent.oob.recevieInvitationFromUrl instead.

}
}

public async onProvisionContext(agentContext: AgentContext) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This is really great!

@TimoGlastra
Copy link
Contributor Author

are the import key API to also accept privatekey bytes instead of converting it manually before to a jwk, using keyType instead of kty (curve instead of crv, etc.) when importing a key

I tried to stay as consistent with JWK apis to not create inconsistencies, because if we do it for importKey, we should do it for createKey, and there I wanted to stay aligned with JWK so there's no possibility of inconsistency.

Regarding the private key, i wanted to be careful of this, as private key is often not enough. The encoding of the private key matters, and this can differ between algorithms. Do you have any suggestions on how to consistently transform from a private key to private jwk? I'm not 100% sure which encoding askar expects, and whether that will work for all key types (like RSA?).

For example if you look at the importKey from webcrypto, you can 4 formatS:

For private keys you can only use RAW for AES or HMAC secret keys. Otherwise you need to use another format. So we'd need to add a importKey with format and then support different format types. We could e.g. support SPKI because of the ASN.1 support we have, but still not sure about RAW. I would rather add better conversion utils outside of the specific crypto backends, but it can be quite complex to do the transformation without a crypto implementation.

So input welcome, but I'd rather move this to a follow up PR if we have a concrete plan

Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Timo Glastra <timo@animo.id>
@TimoGlastra TimoGlastra merged commit e936068 into openwallet-foundation:main May 15, 2025
20 checks passed
@TimoGlastra
Copy link
Contributor Author

I will address the remaining taks in follow up PR:

  • move the generic JWK stuff to jose directory
  • generalize the public key extraction, with a format
  • add unencrypted askar support
  • export from @credo-ts/core/kms
  • add pre-hashes algorithms, for X509 signing
  • remove usages of verkey completely

@ajile-in
Copy link
Contributor

This is really amazing work @TimoGlastra

genaris pushed a commit to genaris/credo-ts that referenced this pull request Oct 9, 2025
…oundation#2203)

Signed-off-by: Timo Glastra <timo@animo.id>
Signed-off-by: Ariel Gentile <gentilester@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants