mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-28 17:21:34 +00:00
feat(quic): support stateless resets
Resolves #3419. Pull-Request: #4554.
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -2642,6 +2642,7 @@ dependencies = [
|
||||
"criterion",
|
||||
"ed25519-dalek",
|
||||
"hex-literal",
|
||||
"hkdf",
|
||||
"libsecp256k1",
|
||||
"log",
|
||||
"multihash",
|
||||
@ -2928,6 +2929,7 @@ dependencies = [
|
||||
"quickcheck",
|
||||
"quinn",
|
||||
"rand 0.8.5",
|
||||
"ring",
|
||||
"rustls 0.21.7",
|
||||
"socket2 0.5.4",
|
||||
"thiserror",
|
||||
|
@ -1,3 +1,10 @@
|
||||
## 0.2.4 - unreleased
|
||||
|
||||
- Implement `Keypair::derive_secret`, to deterministically derive a new secret from the embedded secret key.
|
||||
See [PR 4554].
|
||||
|
||||
[PR 4554]: https://github.com/libp2p/rust-libp2p/pull/4554
|
||||
|
||||
## 0.2.3
|
||||
|
||||
- Fix [RUSTSEC-2022-0093] by updating `ed25519-dalek` to `2.0`.
|
||||
|
@ -15,6 +15,7 @@ categories = ["cryptography"]
|
||||
asn1_der = { version = "0.7.6", optional = true }
|
||||
bs58 = { version = "0.5.0", optional = true }
|
||||
ed25519-dalek = { version = "2.0", optional = true, features = ["rand_core"] }
|
||||
hkdf = { version = "0.12.3", optional = true }
|
||||
libsecp256k1 = { version = "0.7.0", optional = true }
|
||||
log = "0.4"
|
||||
multihash = { version = "0.19.1", optional = true }
|
||||
@ -32,11 +33,11 @@ zeroize = { version = "1.6", optional = true }
|
||||
ring = { version = "0.16.9", features = ["alloc", "std"], default-features = false, optional = true}
|
||||
|
||||
[features]
|
||||
secp256k1 = [ "dep:libsecp256k1", "dep:asn1_der", "dep:rand", "dep:sha2", "dep:zeroize" ]
|
||||
ecdsa = [ "dep:p256", "dep:rand", "dep:void", "dep:zeroize", "dep:sec1" ]
|
||||
secp256k1 = [ "dep:libsecp256k1", "dep:asn1_der", "dep:rand", "dep:sha2", "dep:hkdf", "dep:zeroize" ]
|
||||
ecdsa = [ "dep:p256", "dep:rand", "dep:void", "dep:zeroize", "dep:sec1", "dep:sha2", "dep:hkdf" ]
|
||||
rsa = [ "dep:ring", "dep:asn1_der", "dep:rand", "dep:zeroize" ]
|
||||
ed25519 = [ "dep:ed25519-dalek", "dep:rand", "dep:zeroize" ]
|
||||
peerid = [ "dep:multihash", "dep:bs58", "dep:rand", "dep:thiserror", "dep:sha2" ]
|
||||
ed25519 = [ "dep:ed25519-dalek", "dep:rand", "dep:zeroize", "dep:sha2", "dep:hkdf" ]
|
||||
peerid = [ "dep:multihash", "dep:bs58", "dep:rand", "dep:thiserror", "dep:sha2", "dep:hkdf" ]
|
||||
|
||||
[dev-dependencies]
|
||||
quickcheck = { workspace = true }
|
||||
|
@ -197,6 +197,10 @@ impl SecretKey {
|
||||
sk_bytes.zeroize();
|
||||
Ok(SecretKey(secret))
|
||||
}
|
||||
|
||||
pub(crate) fn to_bytes(&self) -> [u8; 32] {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -342,6 +342,59 @@ impl Keypair {
|
||||
KeyPairInner::Ecdsa(_) => KeyType::Ecdsa,
|
||||
}
|
||||
}
|
||||
|
||||
/// Deterministically derive a new secret from this [`Keypair`], taking into account the provided domain.
|
||||
///
|
||||
/// This works for all key types except RSA where it returns `None`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// # fn main() {
|
||||
/// # use libp2p_identity as identity;
|
||||
///
|
||||
/// let key = identity::Keypair::generate_ed25519();
|
||||
///
|
||||
/// let new_key = key.derive_secret(b"my encryption key").expect("can derive secret for ed25519");
|
||||
/// # }
|
||||
/// ```
|
||||
#[allow(unused_variables, unreachable_code)]
|
||||
pub fn derive_secret(&self, domain: &[u8]) -> Option<[u8; 32]> {
|
||||
#[cfg(any(
|
||||
feature = "ecdsa",
|
||||
feature = "secp256k1",
|
||||
feature = "ed25519",
|
||||
feature = "rsa"
|
||||
))]
|
||||
return Some(
|
||||
hkdf::Hkdf::<sha2::Sha256>::extract(None, &[domain, &self.secret()?].concat())
|
||||
.0
|
||||
.into(),
|
||||
);
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Return the secret key of the [`Keypair`].
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn secret(&self) -> Option<[u8; 32]> {
|
||||
match self.keypair {
|
||||
#[cfg(feature = "ed25519")]
|
||||
KeyPairInner::Ed25519(ref inner) => Some(inner.secret().to_bytes()),
|
||||
#[cfg(all(feature = "rsa", not(target_arch = "wasm32")))]
|
||||
KeyPairInner::Rsa(_) => return None,
|
||||
#[cfg(feature = "secp256k1")]
|
||||
KeyPairInner::Secp256k1(ref inner) => Some(inner.secret().to_bytes()),
|
||||
#[cfg(feature = "ecdsa")]
|
||||
KeyPairInner::Ecdsa(ref inner) => Some(
|
||||
inner
|
||||
.secret()
|
||||
.to_bytes()
|
||||
.try_into()
|
||||
.expect("Ecdsa's private key should be 32 bytes"),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "ecdsa")]
|
||||
@ -901,4 +954,11 @@ mod tests {
|
||||
assert_eq!(converted_pubkey, pubkey);
|
||||
assert_eq!(converted_pubkey.key_type(), KeyType::Ecdsa)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "ecdsa")]
|
||||
fn test_secret_from_ecdsa_private_key() {
|
||||
let keypair = Keypair::generate_ecdsa();
|
||||
assert!(keypair.derive_secret(b"domain separator!").is_some())
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,9 @@
|
||||
## 0.9.3 - unreleased
|
||||
|
||||
- Support QUIC stateless resets for supported `libp2p_identity::Keypair`s. See [PR 4554].
|
||||
|
||||
[PR 4554]: https://github.com/libp2p/rust-libp2p/pull/4554
|
||||
|
||||
## 0.9.2
|
||||
|
||||
- Cut stable release.
|
||||
|
@ -25,6 +25,7 @@ rustls = { version = "0.21.7", default-features = false }
|
||||
thiserror = "1.0.48"
|
||||
tokio = { version = "1.32.0", default-features = false, features = ["net", "rt", "time"], optional = true }
|
||||
socket2 = "0.5.4"
|
||||
ring = "0.16.20"
|
||||
|
||||
[features]
|
||||
tokio = ["dep:tokio", "if-watch/tokio", "quinn/runtime-tokio"]
|
||||
|
@ -61,6 +61,8 @@ pub struct Config {
|
||||
client_tls_config: Arc<rustls::ClientConfig>,
|
||||
/// TLS server config for the inner [`quinn::ServerConfig`].
|
||||
server_tls_config: Arc<rustls::ServerConfig>,
|
||||
/// Libp2p identity of the node.
|
||||
keypair: libp2p_identity::Keypair,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@ -80,6 +82,7 @@ impl Config {
|
||||
|
||||
// Ensure that one stream is not consuming the whole connection.
|
||||
max_stream_data: 10_000_000,
|
||||
keypair: keypair.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,6 +107,7 @@ impl From<Config> for QuinnConfig {
|
||||
max_stream_data,
|
||||
support_draft_29,
|
||||
handshake_timeout: _,
|
||||
keypair,
|
||||
} = config;
|
||||
let mut transport = quinn::TransportConfig::default();
|
||||
// Disable uni-directional streams.
|
||||
@ -128,7 +132,14 @@ impl From<Config> for QuinnConfig {
|
||||
let mut client_config = quinn::ClientConfig::new(client_tls_config);
|
||||
client_config.transport_config(transport);
|
||||
|
||||
let mut endpoint_config = quinn::EndpointConfig::default();
|
||||
let mut endpoint_config = keypair
|
||||
.derive_secret(b"libp2p quic stateless reset key")
|
||||
.map(|secret| {
|
||||
let reset_key = Arc::new(ring::hmac::Key::new(ring::hmac::HMAC_SHA256, &secret));
|
||||
quinn::EndpointConfig::new(reset_key)
|
||||
})
|
||||
.unwrap_or_default();
|
||||
|
||||
if !support_draft_29 {
|
||||
endpoint_config.supported_versions(vec![1]);
|
||||
}
|
||||
|
Reference in New Issue
Block a user