feat(webrtc): certificate, can now be managed like node secret key#12621
feat(webrtc): certificate, can now be managed like node secret key#12621gab8i wants to merge 2 commits into
Conversation
The WebRTC certificate, which determines the node's WebRTC `/certhash`, can now be managed like the node secret key. A new `key generate-webrtc-certificate` subcommand generates a certificate, writes it to a file (or stdout) and prints the corresponding certhash to stderr, mirroring `key generate-node-key`. Additionally, `--experimental-webrtc` now accepts an optional certificate file path (`--experimental-webrtc=<CERT_FILE>`): the certificate is loaded from the file if it exists, or generated and persisted there otherwise. Without a path the behavior is unchanged, the certificate is loaded from or generated at `<base_path>/chains/<chain_id>/network/webrtc_certificate`.
|
/cmd prdoc |
dmitry-markin
left a comment
There was a problem hiding this comment.
Looks good with a couple of nits:
- We should use dedicated error types for
Result::Errinstead ofStringeverywhere, so it's possible to plug these types into higher-level error handling (e.g. use as[from]inthiserror::Error, use withanyhow!, etc.).#[derive(thiserror::Error)]is a convention for error types in substrate. - I don't think we need to encode data as hex on disk at all.
| /// Optionally takes a path to the WebRTC DTLS certificate file | ||
| /// (`--experimental-webrtc=<CERT_FILE>`), which determines the node's `/certhash`. | ||
| /// The certificate is loaded from the file if it exists, otherwise a new certificate | ||
| /// is generated and persisted there. It can be generated ahead of time with the | ||
| /// `key generate-webrtc-certificate` subcommand, which accepts both hex and raw | ||
| /// binary formats. When no path is given, the certificate is loaded from or | ||
| /// generated at `<base_path>/chains/<chain_id>/network/webrtc_certificate`. | ||
| #[arg(long, value_name = "CERT_FILE", num_args = 0..=1, require_equals = true)] | ||
| pub experimental_webrtc: Option<Option<PathBuf>>, |
There was a problem hiding this comment.
I would add a dedicated --webrtc-certificate=<CERT_FILE> switch from the very beginning. This way we avoid ambiguity with a switch having uncertain number of arguments, and once --experimental-webrtc is decomissioned, the --webrtc-certificate stays.
| (None, None) => { | ||
| log::warn!( | ||
| target: LOG_TARGET, | ||
| "WebRtc enabled but no networking path specified, using an ephemeral certificate" |
There was a problem hiding this comment.
nit:
| "WebRtc enabled but no networking path specified, using an ephemeral certificate" | |
| "WebRTC enabled but no networking path specified, using an ephemeral certificate" |
|
|
||
| /// Compute the `/certhash/<hash>` multiaddress component of a certificate. | ||
| pub fn certhash(certificate: &DtlsCertificate) -> String { | ||
| let hash = Code::Sha2_256.digest(certificate.as_parts().0); |
There was a problem hiding this comment.
Shouldn't this be part of DtlsCertificate on litep2p side to avoid errors?
|
|
||
| let hex = array_bytes::bytes2hex("", &encoded); | ||
| let decoded = decode_certificate(hex.as_bytes()).unwrap(); | ||
| assert_eq!(decoded.as_parts(), certificate.as_parts()); |
There was a problem hiding this comment.
This is testing array_bytes :)
| let hex = array_bytes::bytes2hex("", &encoded); | |
| let decoded = decode_certificate(hex.as_bytes()).unwrap(); | |
| assert_eq!(decoded.as_parts(), certificate.as_parts()); |
|
|
||
| #[test] | ||
| fn read_or_generate_persists_certificate() { | ||
| let dir = tempfile::tempdir().unwrap(); |
There was a problem hiding this comment.
This will likely create dir, so won't test the parent directory creation by our function.
| .ok() | ||
| .or_else(|| { | ||
| let hex = std::str::from_utf8(bytes).ok()?; | ||
| let raw = array_bytes::hex2bytes(hex.trim()).ok()?; |
There was a problem hiding this comment.
Why do we decode hex if encode_certificate is not encoding hex? Encoding/decoding should be symmetric without hidden details.
| .ok() | ||
| .or_else(|| { | ||
| let hex = std::str::from_utf8(bytes).ok()?; | ||
| let raw = array_bytes::hex2bytes(hex.trim()).ok()?; |
There was a problem hiding this comment.
Why do we need hex encoding at the file level at all and not just use binary data?
| /// The output is in raw binary format. | ||
| /// If not given, the output is written as an hex encoded string. | ||
| #[arg(long)] | ||
| bin: bool, | ||
|
|
There was a problem hiding this comment.
IMO we should drop hexifying completely and store binary data only.
Description
The WebRTC certificate, which determines the node's WebRTC
/certhash, can now be managed like the node secret key.Integration
A new
key generate-webrtc-certificatesubcommand generates a certificate, writes it to a file (or stdout) and prints the corresponding certhash to stderr, mirroringkey generate-node-key.Additionally,
--experimental-webrtcnow accepts an optional certificate file path (--experimental-webrtc=<CERT_FILE>): the certificate is loaded from the file if it exists, or generated and persisted there otherwise.Without a path the behavior is unchanged, the certificate is loaded from or generated at
<base_path>/chains/<chain_id>/network/webrtc_certificate.