feat:tofu certificate validation#193
feat:tofu certificate validation#193lucasdbr05 wants to merge 5 commits intobitcoindevkit:masterfrom
Conversation
tnull
left a comment
There was a problem hiding this comment.
Took an initial quick look and added some comments (though have yet to do a full review). Though before I proceed, please rewrite the commit history to avoid changing the approach mid-history. Basically, you should try to avoid to touch the same code paths in following commits as far as possible.
Given the code structure and verbosity I'm also suspecting that some form of AI agent was involved here. If this is indeed the case, please note that it is best practice to disclose such use in the PR and commit descriptions.
|
|
||
| /// A trait for storing and retrieving TOFU (Trust On First Use) certificate data. | ||
| /// Implementors of this trait are responsible for persisting certificate data and retrieving it based on the host. | ||
| pub trait TofuStore: Send + Sync + Debug { |
There was a problem hiding this comment.
Why does this need to be Send + Sync?
There was a problem hiding this comment.
I did it this way to ensure that the Client remains thread-safe for concurrent applications, so I designed it to allow the store to be shared and safely accessed across multiple threads via an Arc.
There was a problem hiding this comment.
I guess? Though the Rust compiler should usually infer the bounds when needed.
First of all, thanks for the feedback. When rewriting the history, the best approach would be to avoid keeping commits that record my refactor from persistence-based usage to the trait-based approach, right? |
Yes, this would be preferable. Basically, you could just do a |
oleonardolima
left a comment
There was a problem hiding this comment.
I agree with tnull's comments above, you should narrow it down to few commits, for example: feat(tofu): add tofu store mod; feat(client): add new tofu method/feature, docs(example): add new tofu example.
Also, it's best if it's added under a new feature, and by a separate constructor. I don't think many changes to already-existing methods are needed, maybe it's something remaining from previous changes you did.
It's failing in CI, please make sure that everything is building successfully and passing CI too.
Trust On First Use (TOFU) is a security model where a client trusts a certificate upon the first connection and subsequent connections are verified against that initially stored record. - Introduce the trait to manage certificates - Add module and export the trait in the library root
- Update signature to accept an optional - Implement for both OpenSSL and Rustls - Add custom certificate verifier for Rustls (with AI help) - Extend enum with error variants specifics to TOFU - Support TOFU validation in proxy SSL connections
Implemented a dedicated constructor in the to handle Trust On First Use (TOFU) certificate validation. - New method to initialize a client with TOFU from config - Update existing constructors to support the revised SSL backend signatures
- Implement for testing purposes - Add comprehensive tests covering first-use storage, certificate matching/replacement, and large payloads NOTE: Unit tests and supporting mock implementation were created with AI assistance.
- Provide an usage example for the trait - Demonstrate how to initialize a using - Include a sample in-memory store implementation for demonstration purposes
9178ff9 to
42639ae
Compare
| /// Retrieves the certificate for the given host. | ||
| /// Returns `Ok(Some(cert))` if a certificate is found, `Ok(None)` if no certificate | ||
| /// is stored for this host, or an error if the operation fails. | ||
| fn get_certificate(&self, host: &str) -> Result<Option<Vec<u8>>, Box<dyn Send + Sync + Error>>; |
There was a problem hiding this comment.
Can we avoid the Box<dyn ..> error and simply make this a std::io::Error?
| pub use config::{Config, ConfigBuilder, Socks5Config}; | ||
| pub use types::*; | ||
|
|
||
| mod tofu; |
There was a problem hiding this comment.
I guess we want to feature-gate the whole module?
| } | ||
|
|
||
| /// Creates a new SSL client with TOFU from an existing TcpStream | ||
| fn new_ssl_with_tofu_from_stream<A: ToSocketAddrsDomain>( |
There was a problem hiding this comment.
Can we DRY this up with new_ssl_from_stream?
| pub struct TofuVerifier { | ||
| provider: CryptoProvider, | ||
| host: String, | ||
| tofu_store: std::sync::Arc<dyn TofuStore>, |
There was a problem hiding this comment.
nit: Just import std::sync::Arc above.
| _now: UnixTime, | ||
| ) -> Result<ServerCertVerified, rustls::Error> { | ||
| // Verify using TOFU | ||
| self.verify_tofu(end_entity.as_ref()) |
There was a problem hiding this comment.
Shouldn't we also offer a mode where the TofuVerifier just wraps the rustls verifier, i.e., we'd still also validate the certificate as usual if validate_domain is set? Or do we think that wouldn't ever be used, and doing only TOFU would suffice?
| } | ||
|
|
||
| /// Creates a new SSL client with TOFU from an existing TcpStream | ||
| fn new_ssl_with_tofu_from_stream( |
There was a problem hiding this comment.
It seems this could also get somewhat DRYed up?
|
|
||
| /// Constructor that supports TOFU (Trust On First Use) certificate validation. | ||
| /// Only works with SSL connections. | ||
| pub fn from_config_with_tofu( |
There was a problem hiding this comment.
Same here, please DRY this up, do the additional thing, and then call in to Self::from_config. If that isn't easily possible, extract the common behavior into an _inner method and reuse that by both constructors (similarly elsewhere) to keep the code DRY.
What does this merge request do?
This feature (see the reference issue #176) adds SSL certificate validation based on Trust On First Use (TOFU), storing the certificate on the first connection and verifying its consistency on subsequent connections.
It is implemented via the
TofuStoretrait, which allows customizable certificate persistence.Clientconfiguration SSL client initialization flow to accept a TofuStore via ConfigBuilder.examplesdirectory and scripts to demonstrate how TOFU can be integrated and used in practice.