WebAssembly / wasi-crypto

WASI Cryptography API Proposal
163 stars 25 forks source link

Keys #4

Closed jedisct1 closed 2 years ago

jedisct1 commented 4 years ago

Before jumping into API details, it may be necessary to define what a key is in the context of WASI-crypto.

From previous discussions, we may want to use a single object to represent a key. In particular, a key handle can represent a shared secret key for any encryption algorithm, as well as a secret or public key for any signature or key exchange mechanism. Keys can be directly supplied by applications, or can be referenced by identifiers, in order to support hardware and software security modules.

A key may be made of:

Possible operations on a key:

In any case, from an application standpoint, a key will always be a handle. If applicable, closing the handle will cause all data associated to a key to be immediately wiped from memory.

tniessen commented 4 years ago

Thank you for summarizing this, @jedisct1!

Importation, from a serialized representation (optional) Exportation (optional)

This is the most difficult part, in my opinion. How would users import / export DER/PEM/JWK keys? Would they have to manually parse and convert these formats to the "serialized representation", just to have it then parsed and converted to an internal representation?

Revocation (optional)

What would this do? Invalidate a key so it cannot be used anymore?

jedisct1 commented 4 years ago

At pointed out by @tniessen , should we support DER/BER?

ASN.1 is complicated, and pretty much all ASN.1 parsers have been subject to multiple vulnerabilities.

Furthermore, not all applications require this.

So I'd rather see ASN.1 available as a distinct module, that can also handle conversions from/to the serialized representation.

The serialized representation itself can be way simpler, and there is no need to reinvent what's already widely deployed. For example X25519 and Ed25519 keys are commonly packed as a 256 bit LE string representing one of the coordinates, with the sign for Ed25519. Along with the type, this is enough to efficiently export and import keys.

The ASN.1 module remains very important, and something that can be developed at the same time as the core crypto module.

jedisct1 commented 4 years ago

Revocation (optional) What would this do? Invalidate a key so it cannot be used anymore?

This is for an HSM-like environment, and will indeed invalidate the key.

jedisct1 commented 4 years ago

As a side note ,Google Tink has a pretty nice API for key management.

I'm not sure that we should go that far for a core module, but one should be able to implement something similar using what we will have.

jedisct1 commented 4 years ago

How would users import / export DER/PEM/JWK keys?

Mmm... should that be part of a WASI module at all?

Conversion from/to the serialized representation doesn't need any special capabilities or hostcalls, it can all be done in webassembly land.

jedisct1 commented 4 years ago

So, for DER/BER/PEM/JWK, one thing we can do is work on a standard WebAssembly module doing conversions.

That module will not require any capabilities at all; it will only do pure computations. Bugs in the ASN.1 parser will not affect other modules. In particular, not being part of the wasi-crypto core module reduces the risk of a parser bug leaking secrets stored in the same memory space.

tniessen commented 4 years ago

How would users import / export DER/PEM/JWK keys?

Mmm... should that be part of a WASI module at all?

That's the question... I don't know.

If we can define a serialization that is much easier than ASN.1, it might make sense to deviate from existing standards. If we come to that conclusion, I think we should develop the conversions at the same time as the serialized format, to make sure everything is working together nicely.

Or -- maybe -- we don't want a serialized form at all? Internally, we could represent keys as sets of components, and WASI could allow to construct keys based on components? I'm not sure how that would work, just adding it to the list of options. Maybe "a set of components" is more difficult to implement in WASI than a serialized form, I don't know.

jedisct1 commented 4 years ago

This is mainly just an issue for RSA. Other systems have already pretty well defined short serialized representations, not to mention symmetric keys.

I think we should develop the conversions at the same time as the serialized format, to make sure everything is working together nicely.

Absolutely.

Or -- maybe -- we don't want a serialized form at all? Internally, we could represent keys as sets of components, and WASI could allow to construct keys based on components?

This is also an option, but it seems a little bit more complicated to implement, to use, and more bug-prone (what if a component is missing and we don't properly check it?)

tniessen commented 4 years ago

This is mainly just an issue for RSA. Other systems have already pretty well defined short serialized representations, not to mention symmetric keys.

True, but we can also expect new algorithms with much larger key sizes to become relevant within the next few years, and at that point, converting twice might be a considerable performance impact.

On a related note: Is there already a concept of "big integers" in other WASI proposals?

jedisct1 commented 4 years ago

ASN.1 encoding doesn't directly map to limbs used for big number arithmetic either.

Even with a large key size, the time to convert from one serialized format to another should be negligible, especially if the target format is the number directly encoded as a bit string. Parsing ASN.1 and expanding the key to limbs is more expensive (and still negligible compared to the actual computation).

There isn't any concept of big integers in other WASI proposals AFAIK.

By the way, a few things would be really useful for efficient big number arithmetic in WebAssembly, in particular add with carry/sub with borrow. But that is probably out of the scope of wasi-crypto.

jedisct1 commented 4 years ago

If this is a concern, the import function can accept a format parameter.

Implementations must implement one format, but may implement more. So that ASN.1 decoding can be directly done by that module if it turns out to be a massive performance win.

However, from a security standpoint, having complex parsers in a core, critical module, may not be a good idea. So, that's something we may want to only do if really necessary.

tniessen commented 4 years ago

Even with a large key size, the time to convert from one serialized format to another should be negligible, especially if the target format is the number directly encoded as a bit string.

I agree, but future algorithms may use public keys with a size of several megabytes.

jedisct1 commented 4 years ago

I agree, but future algorithms may use public keys with a size of several megabytes.

To avoid copies when performing a function call between modules, WebAssembly is going to support sharing linear memories.

So, for very large keys, an option can be to allow the ASN.1 parser to decode the parameters into a shared linear memory. The core module can then use them directly without extra copies.

tniessen commented 4 years ago

But we would still need to copy data from ASN.1 to the serialized form, which would then be copied into an internal data structure by WASI, right? Which still means two copies?

jedisct1 commented 4 years ago

This is probably what most implementations do already. The ASN.1 parser decodes keys into bitstrings, and these bitstrings get then expanded to an internal big number type. With a shared linear memory, there will be only one copy of these strings. The serialized representation doesn't need to be fancy, and can be as close as possible as what big number implementations already decode.

The only alternative is to include an ASN.1 parser/generator in the core wasi-crypto module.

There is always a tradeoff between speed and security, but having a sandboxed ASN.1 parser is really compelling IMHO. This is a perfect example of how to leverage WebAssembly to improve application security.

ueno commented 4 years ago

Though I agree that a little of ASN.1 is inevitable when dealing with key formats, having a fully-fledged ASN.1 parser/generator seems to be beyond the scope of wasi-crypto. I'm not even sure how such API would look like, for e.g., destructuring an ASN.1 object.

On a related note, do we want to support encrypted form of serialization, such as PKCS#8 or PKCS#12?

Also, here we are mainly talking about keys for public key crypto. What about symmetric encryption keys or MAC keys: are they supposed to be in the same category?

jedisct1 commented 4 years ago

Symmetric primitives only require raw keys. We can support simple and common encodings (hex, base64) but there is probably no need for anything fancier.

Supporting encrypted keys is unlikely to be required by most applications But if this is something that we eventually want to support, or if an implementation really needs to support it, that can be done without breaking the API, by setting additional properties onto a keypair_builder object.

ueno commented 4 years ago

That sounds reasonable. I asked because some crypto libs (e.g., NSS) forbid access to the secret material in an unencrypted form when running under FIPS mode, though in practice it doesn't work well with practical applications.

tniessen commented 4 years ago

We can support simple and common encodings (hex, base64) but there is probably no need for anything fancier.

If we decide against ASN.1 (and therefore PEM), I don't think we should try to accommodate other encodings. Hex and base64 are easy to implement for users, and if we provided those encodings for keys, we would arguably also have to provide them for other inputs and outputs.

jedisct1 commented 4 years ago

We don't need a fully fledged ASN.1 parser/generator, but from a practical perspective, it may be reasonable to be able to directly support unencrypted PKCS#8. Not having PKCS#5 removes quite a lot of complexity.

But not every key can be encoded to PKCS#8; more options may still be required. EdDSA keys can be encoded as PKCS#8 but the vast majority of applications use a raw format, so being able to accept a format identifier is probably not a bad idea. It also makes the API a little bit more future proof.