Open sinui0 opened 8 months ago
Seems like CryptoProvider
does indeed expose enough details for our TLSNotary protocol.
For reference here's an example implementation of CryptoProvider
https://github.com/rustls/rustls/blob/7d998cd7efb6ef9d4350aa397d974db72b7e9ef5/provider-example/src/lib.rs#L21
pub fn provider() -> CryptoProvider {
CryptoProvider {
cipher_suites: ALL_CIPHER_SUITES.to_vec(),
kx_groups: kx::ALL_KX_GROUPS.to_vec(),
signature_verification_algorithms: verify::ALGORITHMS,
secure_random: &Provider,
key_provider: &Provider,
}
}
Here's how the needed TLS details can be exposed to the TLSNotary protocol.
For kx_groups
we can implement crypto::SupportedKxGroup
whose start
method returns impl ActiveKeyExchange
whose complete
method exposes peer_pub_key
which is the server ephemeral public key.
With peer_pub_key
exposed, we can run MPC key exchange.
The complete
method will return a dummy SharedSecret
(recall that our MPC TLS client doesn't know the full shared secret, so we need to return a dummy value)
For signature_verification_algorithms
's WebPkiSupportedAlgorithms::mapping
we can implement SignatureVerificationAlgorithm
whose verify_signature
method will be invoked by rustls to check the server signature over the key exchange params.
fn verify_signature(
&self,
public_key: &[u8],
message: &[u8],
signature: &[u8],
) -> Result<(), InvalidSignature>;
Here message
will be a concatenation of client_random
+server_random
+peer_pub_key
( from Step 1).
For cipher_suites
's field aead_alg
we can implement Tls12AeadAlgorithm
so that its encrypter
method returns our custom MPC AEAD encrypter which implements MessageEncrypter
.
We will ignore the encrypter
args key
and iv
(since those will be dummy keys derived from a dummy SharedSecret
in Step 1). Our MPC AEAD encrypter will use its own key references from the MPC VM.
(The above approach also applies to AEAD decrypter
).
verify_data
(consult https://tls12.xargs.org/#client-handshake-finished)Since rustls
has a dummy MasterSecret
derived from SharedSecret
(from Step 1), it will be unable to compute the correct verify_data
for the Client Finished message. (recall that in our MPC TLS the MasterSecret
is secret-shared and is not known to any party).
Luckily, the CryptoProvider
trait allows us to "intercept" the call to the PRF when verify_data
is being computed and to return the correct verify_data
computed in MPC.
To accomplish this, for cipher_suites
's field prf_provider
we can implement crypto::tls12::Prf
whose method
fn for_secret(&self, output: &mut [u8], secret: &[u8], label: &[u8], seed: &[u8]);
allows us to check if label
== client_finished
and then to return the correct verify_data
computed in MPC.
(The above approach also applies to computing verify_data
for Server Finished).
hi! @sinui0 @themighty1 what is the details about this issue? I am part of the PSE 2024 core program
Newer releases of
rustls
have introduced abstraction layers for implementing custom cryptography backends for the client. Right now we have our own heavily modified fork which does the same and adds async internals.We should really investigate whether we can ditch our fork and switch back to upstream now that this abstraction layer exists. Can we implement our own
CryptoProvider
? Or do we still need finer control over the client? Can we avoid needing async by running the blocking client on a dedicated thread/web worker?The benefits of not having to maintain our own client and test suite are numerous.