-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Review - Tests, Key and Certificate Generation, Concurrency Control, Custom TLS Verification #26
Conversation
I've come to the conclusion that Right now in PK we are still using in-memory buffers, and that's fine, but if we want flexibility, then So right now I've actually removing Note that it's easier to protect the memory of keys if they are buffers. But more difficult to do so for |
The main libraries required here are just:
That's it. The ASN1 libraries are not needed. This is because we solely rely on peculiar's webcrypto to do everything. The only reason PK is using the ASN1 libraries is to prepare to be more portable and eventually not rely on |
So in the fixtures, you generate regular keys and certs, and also you generate CAs. I'm going to do this all dynamically now. Even CA signing is just a matter of having 2 keypairs. In fact these keypairs can be different. The signature of the child cert however would be signed by the parent's private key. |
@tegefaulkes I've noticed that you don't have any tests for |
I'm creating these. |
@tegefaulkes make sure that exported types are at the bottom. |
Most tests for |
The way you do tests between client and server is that server just runs tests on starting the server, with utility functions creating the client. In this sense you are "fixing the client", and testing variation on the server. On the client, you "fix the server" and you test variations on the client. Yes they can be tested together, but it depends on how you create tests. Whenever you're testing things you must pick your independent variables and dependent variables. Ideally you fix everything except one thing. That reduces the amount of state space you need to cover. Of course the more independent variables that exist, the more multidimensional variation that can exist, that's why we use fast check too to test higher dimensional spaces of configuration. But you always start with the simplest smallest stuff first before building up to the more complex tests, because the first smaller tests are your sanity check. |
@tegefaulkes types should always be in |
Here's an interesting idea for testing UDP sockets. There's an option called Right now Anyway... this enables the ability for another socket to bind to the same address/port. Or it even enables running the Subsequently, you can now send a message to the port, and I believe the message is duplicated to both sockets. This allows you have 2 sockets that receive the same message sent to it. This can enable some interesting tests, so that one can also get a copy of the messages that is being sent to the UDP socket used by |
I'm adding But we can enable it for testing, or if there are special reasons to have this later in Polykey. |
On |
Ok the above idea means that with unicast packets, it only gets sent to the last bound socket. So I don't get both packets on both ports. Basically by doing the above, the new socket will end up taking over, and messages get sent the new socket. However IF I change So the only way to get a fan-out structure is to have all sockets bound on Now you end up getting packets on the shared sockets. |
Anyway I'm not sure if we can use this unless we are sending broadcast packets. So I'll leave that solution to be explored later. I tried also sending 2 unicast packets, both end up being sent to the same socket. I thought that there might be round-robin, but now I'm not sure. |
One of the challenges using fast check with webcrypto is that we cannot seed webcrypto API. In PK we are able to do this with Ed25519 because the private key is just a random bunch of bytes. However for RSA, ECDSA, this is not possible. So for now I don't think we will be using fast check on our keys and certificates, instead we will just always randomly generate them or use them across a number of tests. |
Therefore, rather than generating the keys and certs all the time, we can just do it once at the beginning of each test module. Something like this: let keyPairRSA: {
publicKey: JsonWebKey;
privateKey: JsonWebKey;
};
let certRSA: X509Certificate;
let keyPairRSAPEM: {
publicKey: string;
privateKey: string;
};
let certRSAPEM: string;
let keyPairECDSA: {
publicKey: JsonWebKey;
privateKey: JsonWebKey;
};
let certECDSA: X509Certificate;
let keyPairECDSAPEM: {
publicKey: string;
privateKey: string;
};
let certECDSAPEM: string;
let keyPairEd25519: {
publicKey: JsonWebKey;
privateKey: JsonWebKey;
};
let certEd25519: X509Certificate;
let keyPairEd25519PEM: {
publicKey: string;
privateKey: string;
};
let certEd25519PEM: string;
beforeAll(async () => {
keyPairRSA = await testsUtils.generateKeyPairRSA();
certRSA = await testsUtils.generateCertificate({
certId: '0',
subjectKeyPair: keyPairRSA,
issuerPrivateKey: keyPairRSA.privateKey,
duration: 60 * 60 * 24 * 365 * 10,
});
keyPairRSAPEM = await testsUtils.keyPairRSAtoPEM(keyPairRSA);
certRSAPEM = testsUtils.certToPEM(certRSA);
keyPairECDSA = await testsUtils.generateKeyPairECDSA();
certECDSA = await testsUtils.generateCertificate({
certId: '0',
subjectKeyPair: keyPairECDSA,
issuerPrivateKey: keyPairECDSA.privateKey,
duration: 60 * 60 * 24 * 365 * 10,
});
keyPairECDSAPEM = await testsUtils.keyPairECDSAtoPEM(keyPairECDSA);
certECDSAPEM = testsUtils.certToPEM(certECDSA);
keyPairEd25519 = await testsUtils.generateKeyPairEd25519();
certEd25519 = await testsUtils.generateCertificate({
certId: '0',
subjectKeyPair: keyPairEd25519,
issuerPrivateKey: keyPairEd25519.privateKey,
duration: 60 * 60 * 24 * 365 * 10,
});
keyPairEd25519PEM = await testsUtils.keyPairEd25519ToPEM(keyPairEd25519);
certEd25519PEM = testsUtils.certToPEM(certEd25519);
}); |
Reviewing Node's TLS module, I can see that you can actually set multiple keys and associated cert chains. This actually enables the TLS system to use multiple keys and cert chains, possibly using different keys. This may actually solve the #17. In the boring ssl codebase, the function set_private_key translates to calling I think this means it's possible to call this function multiple times. |
Once you can set multiple keys and certs, it's possible that it's the TLS client that figures out which key/cert to use to verify. This is something that's worth considering since we are going to enable Ed25519 here. |
So boringssl does use the same environment variables:
That does mean that without setting the CA cert deliberately, it just relies on the default ca cert location on the operating system. On NixOS, that would be dependent on the This will be important when pushing the docker container image. |
There must also be a default signature algorithm set too. I'm not sure if I can find it in the boringssl source code. By default we want to also enable This is currently set to:
I think it's better to use the new TLS v1.3 scheme names as written in the RFC and then join it together with |
It appears Node.js relies on:
Both functions don't have equivalents in the boring package. So I'm not sure if it's possible atm to supply multiple independent certificate chains atm. It seems boringssl supports it. But not the boring package itself. |
I refactored to something like this: // Setup all certificates and keys
// The below may not actually work
// We assume we can just use certificate and add them to it
// However this may not be possible
if let (Some(key), Some(cert)) = (key, cert) {
for (k, c) in key.iter().zip(cert.iter()) {
let private_key = boring::pkey::PKey::private_key_from_pem(&k)
.or_else(
|err| Err(Error::from_reason(err.to_string()))
)?;
ssl_ctx_builder.set_private_key(&private_key).or_else(
|e| Err(napi::Error::from_reason(e.to_string()))
)?;
let x509_cert_chain = boring::x509::X509::stack_from_pem(
&c.to_vec()
).or_else(
|err| Err(napi::Error::from_reason(err.to_string()))
)?;
for (i, cert) in x509_cert_chain.iter().enumerate() {
if i == 0 {
ssl_ctx_builder.set_certificate(cert,).or_else(
|err| Err(Error::from_reason(err.to_string()))
)?;
} else {
ssl_ctx_builder.add_extra_chain_cert(
cert.clone(),
).or_else(
|err| Err(Error::from_reason(err.to_string()))
)?;
}
}
}
} |
7437d80
to
997f395
Compare
Yea I'm pretty sure that the current boring crate does not provide a straight forward way to do this multiple certificate chain. Also I did find a good resource that explains what all the boringssl functions do: https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html. It's much more clearer than all the other manpages. In particular this https://commondatastorage.googleapis.com/chromium-boringssl-docs/ssl.h.html#SSL_CTX_set_cert_cb is actually one of the main ways in which one could potentially select the correct certificate. So for now I'm going to disable the ability to set multiple certificates on the Rust side. |
Ok config tests are done for now. No more |
0b0844c
to
d8e7109
Compare
I've started documenting the config more clearly. Some of the parameters of quiche requires a deeper test and review on how they work. In particular:
I think the whole timeout looping is way too complicated as it is written. It requires some refactoring similar to what happened during the tasks domain in PK. |
[ci skip]
[ci skip]
[ci skip]
[ci skip]
- Fixed up start erroring out. - Fixed up graceful TLS test. - Cleaning up. [ci skip]
[ci skip]
[ci skip]
…timeout [ci skip]
- fixed up `QUICServer.test.ts` - fixed up `QUICSocket.test.ts` - fixed up `concurrency.test.ts` [ci skip]
[ci skip]
[ci skip]
- cleaning up `QUICStream.ts` writable stream logic - cleaning up `QUICStream.ts` readable stream logic - fixing up `QUICStream` tests [ci skip]
- logic fixes for early stream ending. - fixing up tests. - fixed up remote info for stream. - fixed problem with process being held open and handling socket errors. - propagating all events to the top level client or server. - cleaning up comments and commentary. - fixed up benchmark.
20aee5c
to
ed6f3a3
Compare
9ed44df
to
cbedc26
Compare
I've fixed up the Monitor logic to clean them up when done. |
I'm merging this now. We'll do a proper review in staging. |
Great! |
Description
This is a review PR for the tests and crypto code that is being used in the tests.
Issues Fixed
dgram
module #1Tasks
tests/fixtures
in favour of auto-generated certificates and keysjs-timer
can be used for repetition (like reset the time and run again the handler). 0.5 daysjs-timer
forkeepAliveIntervalTimer
. 0.5 daysjs-timer
forconnTimeoutTimer
. 0.5 daysQUICConnection
methods there are all internal. 1 daymaxIdleTimeout
needs to default to 1 min, the timeout for starting a connection and the keep alive needs to be strictly less than themaxIdleTimeout
. IfmaxIdleTimeout
is 0 then this constraint is ignored.Final checklist