mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-28 09:11: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",
|
"criterion",
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"hex-literal",
|
"hex-literal",
|
||||||
|
"hkdf",
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
"log",
|
"log",
|
||||||
"multihash",
|
"multihash",
|
||||||
@ -2928,6 +2929,7 @@ dependencies = [
|
|||||||
"quickcheck",
|
"quickcheck",
|
||||||
"quinn",
|
"quinn",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
|
"ring",
|
||||||
"rustls 0.21.7",
|
"rustls 0.21.7",
|
||||||
"socket2 0.5.4",
|
"socket2 0.5.4",
|
||||||
"thiserror",
|
"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
|
## 0.2.3
|
||||||
|
|
||||||
- Fix [RUSTSEC-2022-0093] by updating `ed25519-dalek` to `2.0`.
|
- 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 }
|
asn1_der = { version = "0.7.6", optional = true }
|
||||||
bs58 = { version = "0.5.0", optional = true }
|
bs58 = { version = "0.5.0", optional = true }
|
||||||
ed25519-dalek = { version = "2.0", optional = true, features = ["rand_core"] }
|
ed25519-dalek = { version = "2.0", optional = true, features = ["rand_core"] }
|
||||||
|
hkdf = { version = "0.12.3", optional = true }
|
||||||
libsecp256k1 = { version = "0.7.0", optional = true }
|
libsecp256k1 = { version = "0.7.0", optional = true }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
multihash = { version = "0.19.1", optional = true }
|
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}
|
ring = { version = "0.16.9", features = ["alloc", "std"], default-features = false, optional = true}
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
secp256k1 = [ "dep:libsecp256k1", "dep:asn1_der", "dep:rand", "dep:sha2", "dep:zeroize" ]
|
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" ]
|
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" ]
|
rsa = [ "dep:ring", "dep:asn1_der", "dep:rand", "dep:zeroize" ]
|
||||||
ed25519 = [ "dep:ed25519-dalek", "dep:rand", "dep:zeroize" ]
|
ed25519 = [ "dep:ed25519-dalek", "dep:rand", "dep:zeroize", "dep:sha2", "dep:hkdf" ]
|
||||||
peerid = [ "dep:multihash", "dep:bs58", "dep:rand", "dep:thiserror", "dep:sha2" ]
|
peerid = [ "dep:multihash", "dep:bs58", "dep:rand", "dep:thiserror", "dep:sha2", "dep:hkdf" ]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
quickcheck = { workspace = true }
|
quickcheck = { workspace = true }
|
||||||
|
@ -197,6 +197,10 @@ impl SecretKey {
|
|||||||
sk_bytes.zeroize();
|
sk_bytes.zeroize();
|
||||||
Ok(SecretKey(secret))
|
Ok(SecretKey(secret))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn to_bytes(&self) -> [u8; 32] {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -342,6 +342,59 @@ impl Keypair {
|
|||||||
KeyPairInner::Ecdsa(_) => KeyType::Ecdsa,
|
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")]
|
#[cfg(feature = "ecdsa")]
|
||||||
@ -901,4 +954,11 @@ mod tests {
|
|||||||
assert_eq!(converted_pubkey, pubkey);
|
assert_eq!(converted_pubkey, pubkey);
|
||||||
assert_eq!(converted_pubkey.key_type(), KeyType::Ecdsa)
|
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
|
## 0.9.2
|
||||||
|
|
||||||
- Cut stable release.
|
- Cut stable release.
|
||||||
|
@ -25,6 +25,7 @@ rustls = { version = "0.21.7", default-features = false }
|
|||||||
thiserror = "1.0.48"
|
thiserror = "1.0.48"
|
||||||
tokio = { version = "1.32.0", default-features = false, features = ["net", "rt", "time"], optional = true }
|
tokio = { version = "1.32.0", default-features = false, features = ["net", "rt", "time"], optional = true }
|
||||||
socket2 = "0.5.4"
|
socket2 = "0.5.4"
|
||||||
|
ring = "0.16.20"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
tokio = ["dep:tokio", "if-watch/tokio", "quinn/runtime-tokio"]
|
tokio = ["dep:tokio", "if-watch/tokio", "quinn/runtime-tokio"]
|
||||||
|
@ -61,6 +61,8 @@ pub struct Config {
|
|||||||
client_tls_config: Arc<rustls::ClientConfig>,
|
client_tls_config: Arc<rustls::ClientConfig>,
|
||||||
/// TLS server config for the inner [`quinn::ServerConfig`].
|
/// TLS server config for the inner [`quinn::ServerConfig`].
|
||||||
server_tls_config: Arc<rustls::ServerConfig>,
|
server_tls_config: Arc<rustls::ServerConfig>,
|
||||||
|
/// Libp2p identity of the node.
|
||||||
|
keypair: libp2p_identity::Keypair,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Config {
|
impl Config {
|
||||||
@ -80,6 +82,7 @@ impl Config {
|
|||||||
|
|
||||||
// Ensure that one stream is not consuming the whole connection.
|
// Ensure that one stream is not consuming the whole connection.
|
||||||
max_stream_data: 10_000_000,
|
max_stream_data: 10_000_000,
|
||||||
|
keypair: keypair.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,6 +107,7 @@ impl From<Config> for QuinnConfig {
|
|||||||
max_stream_data,
|
max_stream_data,
|
||||||
support_draft_29,
|
support_draft_29,
|
||||||
handshake_timeout: _,
|
handshake_timeout: _,
|
||||||
|
keypair,
|
||||||
} = config;
|
} = config;
|
||||||
let mut transport = quinn::TransportConfig::default();
|
let mut transport = quinn::TransportConfig::default();
|
||||||
// Disable uni-directional streams.
|
// Disable uni-directional streams.
|
||||||
@ -128,7 +132,14 @@ impl From<Config> for QuinnConfig {
|
|||||||
let mut client_config = quinn::ClientConfig::new(client_tls_config);
|
let mut client_config = quinn::ClientConfig::new(client_tls_config);
|
||||||
client_config.transport_config(transport);
|
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 {
|
if !support_draft_29 {
|
||||||
endpoint_config.supported_versions(vec![1]);
|
endpoint_config.supported_versions(vec![1]);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user