RustCrypto / traits

Collection of cryptography-related traits
593 stars 193 forks source link

Async traits #304

Open tarcieri opened 4 years ago

tarcieri commented 4 years ago

Several crates in this repo could benefit from having async equivalents. There are several use cases for async, such as communicating with network services which implement a particular cryptographic primitive (Cloud KMS, NetHSM), or things like cryptographic tokens, SmartCards, non-networked HSMs (e.g. PCI cards, USB devices), or embedded cryptographic accelerators.

Unfortunately Rust doesn't natively support async traits yet. The most commonly used solution at the moment is @dtolnay's async-trait crate. This provides an ergonomic syntax which should hopefully be easy to transition to hypothetical future natively supported async traits, however at the moment it requires std and uses Box to store Futures. There's some discussion about improving this happening on rust-internals right now.

I think it'd be useful to start defining some async traits, even if they presently depend on std. Here's a tracking list of crates that could benefit from async traits:

Others? (please leave a note in the comments if you have a use case and I'll update)

tarcieri commented 4 years ago

I would really love to find some solution for allowing an embedded cryptographic accelerator to process work in the background while the primary processor/MCU is performing other operations.

Down-the-road I think async would be great here: you could submit work asynchronously to a cryptographic accelerator, receiving a Future you can poll for completion. Unfortunately async-trait seems like a nonstarter in the embedded space, so perhaps it's worth investigating some sort of stopgap API which lets you submit work and then poll/busywait for completion.

This would be quite helpful for algorithms like AES-GCM and AES-GCM-SIV (decryption, at least), by allowing encryption to happen in parallel on an accelerator/coprocessor while authentication is handled on the main processor/MCU.

Dirbaio commented 4 years ago

In nightly it is already possible to have async traits with feature(generic_associated_types). (example). This does fully static dispatch and no heap allocations. You can even implement the traits with async blocks (instead of manually-written futures) with feature(type_alias_impl_trait) (example)

IMO it's not worth it to stand up an async traits ecosystem with the current async_trait crate or other workarounds that require alloc/dyn, only to break them later when switching to "real" async traits with generic_associated_types.

newpavlov commented 4 years ago

Another potential use-case is using Web Crypto API as a backend for some algorithms. But I also don't think we should depend on the async-trait crate. Having an unstable feature is a better option in my opinion, plus it can help with the feature development.

Another question is what to do with the software implementations? Should we in future add a wrapper which would use a thread pool to emulate async API?

tarcieri commented 4 years ago

Having an unstable feature is a better option in my opinion.

Sounds good to me! As noted above, the async-trait is a showstopper for the use cases I'm really interested in solving, so starting with a nightly-only option which can actually be used in embedded environments would be preferable to me.

Another question is what to do with the software implementations? Should we in future add a wrapper which would use a thread pool to emulate async API?

Seems pretty context-dependent to me. I think it's a problem worth leaving open-ended initially, letting people define the sync-vs-async relationships as they see fit, and then perhaps seeing if there are common patterns we could wrap up with things like blanket impls (perhaps gated with marker traits) or failing that, proc macros.

tarcieri commented 4 years ago

FYI, I spiked out an async-signature crate which uses async-trait (for now): #320

That said, I did it with the caveat that the longer-term goal is to move those traits into the signature crate proper, and when that happens, we'll use GATs. Once we have a stable GAT solution for signature, we can simply retire the async-signature crate.

leonbotros commented 2 years ago

Another potential use-case is using Web Crypto API as a backend for some algorithms. But I also don't think we should depend on the async-trait crate. Having an unstable feature is a better option in my opinion, plus it can help with the feature development.

I have this use case where I want to construct aead::stream using AES-GCM in Web Crypto (async api) when targetting wasm32. What would currently be the best workaround?

tarcieri commented 2 years ago

There's presently no async support for aead, or for that matter in the cipher crate.

As it seems GATs may soon be stable, I would suggest holding off explorations of how to asyncify those crates until GATs land.

I agree there are many uses for async aead and cipher: beyond just WebCrypto, they would be helpful for supporting cryptographic accelerators/coprocessors which can compute cryptographic functions in parallel and either be polled to completion or send an interrupt when done. This would be very helpful for pipelining work between e.g. a UHF functioning as a MAC which is executed on an MCU and e.g. AES as executed by an accelerator/coprocessor.

korken89 commented 8 months ago

Hi,

Async traits are now available since 1.75. Is there anything blocking this endeavor now?

tarcieri commented 8 months ago

Nope, and in fact the async-signature crate now makes use of AFIT with a 1.75 MSRV (I've updated the description above).

What would be helpful now that AFIT is stable is to have some concrete use cases for async traits to drive their design. An example would be embedded async users who are handling interrupts from a cryptographic coprocessor/accelerator using async.

We already have those for signatures, in that there are many external signers (e.g. KMS) where async is needed to communicate with them, but it would be helpful to have those for e.g. cipher use cases.

korken89 commented 8 months ago

What a coincidence, or maybe not! :)

I'm currently writing an DTLS 1.3 implementation for embedded (Cortex-M mainly) and for accelerating the AES128-GCM and SHA256 I was planning to use the crypto accelerator and the hash accelerator of the MCU. This is why I was looking for async traits so I can simply make the implementation work with sync implementations and interrupt driven via async.

Generally most MCUs that are communication oriented have acceleration for at least the symmetric encryption, but some also has hash accelerators. Both which generally support interrupts on completion (or error). E.g. the STM32 CRYPTO (symmetric cryptography accelerator) and HASH (MD5, SHA-1, SHA224, SHA256 accelerator) peripherals allows for fully interrupt driven operation and has async HAL support in embassy-stm32.

How could I help give some insight?

tarcieri commented 8 months ago

@korken89 perhaps you could open a PR with the traits you'd like to see?

MathiasKoch commented 8 months ago

Additionally, are there any reason the async-signature crate is not #![no_std]? or is it just an oversight?

tarcieri commented 8 months ago

@MathiasKoch I think it might be leftover from before AFIT when it was using async-trait, anyway yes it's an oversight and we should make it no_std

korken89 commented 8 months ago

I can indeed have a look at the traits! I'm getting close to finalizing the DTLS 1.3 implementation and when I'm there I'll start looking into async-ifying the APIs and seeing where one would need to add async traits.