h2o / picotls

TLS 1.3 implementation in C (master supports RFC8446 as well as draft-26, -27, -28)
538 stars 142 forks source link

Adding a new crypto engine #484

Closed sshock closed 1 year ago

sshock commented 1 year ago

Hi, is there any guide for how to add a new crypto engine?

It looks like it should be relatively straightforward, but I'm a bit lost already just in my aead_setup_crypto() method, because I've got a key and a 12-byte IV, but the engine is wanting a key plus a 4-byte nonce.

RFC 4106 describes a Nonce as a 4-byte salt and a 8-byte IV. I think the 4-byte salt is what I need to give the engine here.

Since the picotls IV is 12 bytes, it almost makes me wonder if that IV is actually a 4-byte salt + 8-byte IV, but I don't see any other engines using it like that. In fact, they all save the 12-byte IV to a static_iv and use it later in aead_do_encrypt_init() to generate a different IV with ptls_aead__build_iv().

So I'm totally lost as to what this 4-byte salt should be or where I would get it from.

Also, I noticed a ctr_cipher and ecb_cipher inside the ptls_aead_algorithm_t struct, but it looks like those aren't needed / aren't even used?

Any guidance on this would be greatly appreciated, thanks!

kazuho commented 1 year ago

The following assumes that we are talking about AES-GCM, as you are referring to RFC 4106.

AEAD API of picotls was designed to support TLS 1.3, therefore it assumes 12-byte static IV + 64-bit nonce (see RFC 8446 section 5.3).

So I'm totally lost as to what this 4-byte salt should be or where I would get it from.

12-byte static IV is provided to the AEAD engine via the 4th argument of setup_crypto callback. It is the responsibility of each AEAD engine to retain the static IV.

Then, for each invocation of do_encrypt_v or do_decrypt, the engine should generate a per-record nonce by XORing the static IV and the provided nonce.

Among the encrypt / decrypt callbacks, the other one that each engine must supply is do_encrypt, however this can be set to ptls_aead__do_encrypt that calls do_encrypt_v.

Also, I noticed a ctr_cipher and ecb_cipher inside the ptls_aead_algorithm_t struct, but it looks like those aren't needed / aren't even used?

AEAD engines to be used together with QUIC version 1 must providectr_cipher, as the header protection of QUIC (RFC 9001 section 5.4) uses the underlying CTR cipher of each AEAD algorithm (the document talks about ECB for AES-GCM, but it is actually designed so that underlying CTR mode of an AEAD cipher can be used in an algorithm-agnostic manner).

Also, to support multipath QUIC, do_set_iv and do_get_iv callbacks exist, allowing users to change the entire IV. This is necessary because multipath QUIC needs to apply XOR to the entire 12-byte space of a static IV, the first 4 bytes being the path ID and the remaining 8 bytes being the packet number.

sshock commented 1 year ago

Thanks for all the helpful info. I think my problem is that instead of using a regular "gcm(aes)" algorithm, I am trying to use "rfc4106(gcm(aes))", which is tailored for use with IPSEC and has subtle differences like requiring a 4-byte salt/nonce to be provided with the key and having an 8-byte IV instead of 12-byte IV.

Also, I noticed a ctr_cipher and ecb_cipher inside the ptls_aead_algorithm_t struct, but it looks like those aren't needed / aren't even used?

AEAD engines to be used together with QUIC version 1 must providectr_cipher, as the header protection of QUIC (RFC 9001 section 5.4) uses the underlying CTR cipher of each AEAD algorithm (the document talks about ECB for AES-GCM, but it is actually designed so that underlying CTR mode of an AEAD cipher can be used in an algorithm-agnostic manner).

Ok, yeah although I don't see ecb_cipher or ctr_cipher used directly by picotls itself, I do see in the ngtcp2 quic library for example that it does reference ctr methods like ptls_openssl_aes128ctr and ptls_openssl_aes256ctr (though interestingly it just refers to these directly, not via the ctr_cipher member of the ptls_aead_algorithm_t).

kazuho commented 1 year ago

Thanks for all the helpful info. I think my problem is that instead of using a regular "gcm(aes)" algorithm, I am trying to use "rfc4106(gcm(aes))", which is tailored for use with IPSEC and has subtle differences like requiring a 4-byte salt/nonce to be provided with the key and having an 8-byte IV instead of 12-byte IV.

I see.

So if I understand correctly, in RFC 4106, nonce is constructed as a concatenation of

whereas in case of RFC 8446, nonce is constructed as a XOR of

Considering these differences, when using picotls for RFC 4106 style construction, what users should do is:

By using the AEAD engines as such, there is no tweaks required inside picotls to support RFC 4106. All the engines (including new ones) can support both RFC 4106 and 8446.

sshock commented 1 year ago

By using the AEAD engines as such, there is no tweaks required inside picotls to support RFC 4106. All the engines (including new ones) can support both RFC 4106 and 8446.

Awesome, thanks!