KDFs icon indicating copy to clipboard operation
KDFs copied to clipboard

Abstract Hkdf and HkdfExtract over hmac::Mac

Open ia0 opened this issue 2 years ago • 7 comments
trafficstars

I might be missing something about the rational behind the HmacImpl sealed trait (like some implicit invariant), but I'm wondering why Hkdf and HkdfExtract are not parametrized by hmac::Mac instead. This trait already provides OutputSizeUser.

This would be useful when someone has both a Digest and a Mac hardware implementation and would like to compute HKDF using that Mac hardware implementation instead of using the SimpleHmac software implementation on top of the Digest hardware implementation.

ia0 avatar May 17 '23 14:05 ia0

Sounds good to me. It would be nice to be able to support hardware accelerators.

tarcieri avatar May 20 '23 17:05 tarcieri

Note that HKDF is defined in terms of HMAC. So I don't think it will be correct to use the Mac trait here. At the very least, we would need a trait for a MAC function which can be initialized with keys of any length. We also use the sealed trait to hide these unwieldy bounds, but it should be solved in future releases (see https://github.com/RustCrypto/MACs/issues/104). Right now we rely on block-level API in our implementation to reduce size of the Hkdf struct, to support slice-based hardware implementations we would need a bit of redesign.

AFAIK HMAC is usually supported in HSM-like hardware to protect keys from leaking, not for efficiency sake. Can you provide an example of hardware which you would like to target?

newpavlov avatar May 20 '23 20:05 newpavlov

Good point, an HmacMarker would be needed to replace the sealed trait (similar to the existing HashMarker and MacMarker), or the sealed trait could be renamed and exposed.

I'm not following the block-level API comment. The code only seems to use slices. However something to note is that the current implementation optimizes by reusing the core. An implementation that doesn't permit this (hardware implementations usually don't) would just define the core to be the pseudo random key and not precompute anything.

I sadly cannot provide an example hardware. But yes this is not about performance.

(I'm currently on vacations for 3 weeks so might not be able to reply until then.)

ia0 avatar May 21 '23 09:05 ia0

AFAIK HMAC is usually supported in HSM-like hardware to protect keys from leaking, not for efficiency sake.

When I say "cryptographic accelerator" I mean TPM/SEP-like cryptographic coprocessor, and yes one of its main purposes of the kind I have in mind is to airgap and provide access control around keys including preventing exfiltration of the raw key material, but also such coprocessors include fixed functions of various cryptographic algorithms that run faster than the software equivalent on an MCU, so it's a little of both. I have one device that works this way with a baked-in device unique hardware key, however we did not wrap the HMAC functionality it provides, but instead we wrapped the device unique keyed AES functionality and use that to store the encryption of the base derivation key which we use to initialize HKDF, which works too.

But also, I maintain crates like yubikey and yubihsm which also provide hardware-backed HMAC, though these do not provide a trait-based API like we're discussing. FWIW here's an example of the current HMAC-based API:

https://docs.rs/yubihsm/latest/yubihsm/client/struct.Client.html#method.sign_hmac

It would need a sort of proxy type which knows the key ID a priori to implement a Mac-like API.

At the very least, we would need a trait for a MAC function which can be initialized with keys of any length.

Well it'd be really nice to completely decouple KeyInit and Mac so the latter can be used with ^^^ devices which don't accept a raw key at all, but rather a "handle" to a key stored in hardware.

tarcieri avatar May 21 '23 23:05 tarcieri

@ia0

I'm not following the block-level API comment.

See how the Hkdf struct is defined. It uses I::Core, which does have any internal buffers. In the case of hardware-based implementation we would need to use some kind of handle there.

@tarcieri

FWIW here's an example of the current HMAC-based API:

Note that for HKDF we need an update-based API. If YubiHSM provides only whole message signing API, then a wrapper would need to assemble a full message on heap, which will be somewhat inefficient.

newpavlov avatar May 22 '23 06:05 newpavlov

See how the Hkdf struct is defined. It uses I::Core, which does have any internal buffers.

It's probably simpler if I write a PR when I'm back in 3 weeks. There looks like there is a misunderstanding on what is being achieved here, since your comments don't seem to reply to what I say. Code is probably easier to understand.

ia0 avatar May 22 '23 10:05 ia0

Sorry for the very long delay, but I finally got time to get to this. I've created #82 to show an example of what I mean. Here are the main points:

  • [library] Expose the sealed trait as HmacImpl removing one hop (this is just for illustration, of course documentation and other things need to be addressed)
  • [test] I chose as example a hardware that can run a single HMAC instance which is what I have
  • Note that the current HMAC (and hash) APIs don't support errors which is not great when implementing them with hardware. Hardware can always fail in particular when it has security protection and detects glitch attempts (changes in temperature or voltage for example). But I have an idea to work around this limitation: https://github.com/google/wasefire/issues/176

ia0 avatar Jun 29 '23 21:06 ia0