From 0418c02ebe921018d0ab105a6bb7ab4af40d32d8 Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Wed, 14 Feb 2024 16:04:16 -0500 Subject: [PATCH 1/2] fix: update ed25519 to v2 --- examples/http_client.rs | 8 ++++---- examples/webhook.rs | 12 ++++++------ examples/websocket_client.rs | 4 ++-- relay_client/src/http.rs | 10 +++++----- relay_rpc/Cargo.toml | 4 ++-- relay_rpc/src/auth.rs | 10 +++++----- relay_rpc/src/domain.rs | 16 ++++++++-------- relay_rpc/src/jwt.rs | 16 ++++++++-------- relay_rpc/src/rpc/watch.rs | 20 +++++++++----------- 9 files changed, 49 insertions(+), 51 deletions(-) diff --git a/examples/http_client.rs b/examples/http_client.rs index 1febf6c..04d1823 100644 --- a/examples/http_client.rs +++ b/examples/http_client.rs @@ -1,7 +1,7 @@ use { relay_client::{http::Client, ConnectionOptions}, relay_rpc::{ - auth::{ed25519_dalek::Keypair, rand, AuthToken}, + auth::{ed25519_dalek::SigningKey, rand, AuthToken}, domain::Topic, }, std::{sync::Arc, time::Duration}, @@ -20,7 +20,7 @@ struct Args { project_id: String, } -fn create_conn_opts(key: &Keypair, address: &str, project_id: &str) -> ConnectionOptions { +fn create_conn_opts(key: &SigningKey, address: &str, project_id: &str) -> ConnectionOptions { let aud = Url::parse(address) .unwrap() .origin() @@ -39,10 +39,10 @@ fn create_conn_opts(key: &Keypair, address: &str, project_id: &str) -> Connectio async fn main() -> anyhow::Result<()> { let args = Args::from_args(); - let key1 = Keypair::generate(&mut rand::thread_rng()); + let key1 = SigningKey::generate(&mut rand::thread_rng()); let client1 = Client::new(&create_conn_opts(&key1, &args.address, &args.project_id))?; - let key2 = Keypair::generate(&mut rand::thread_rng()); + let key2 = SigningKey::generate(&mut rand::thread_rng()); let client2 = Client::new(&create_conn_opts(&key2, &args.address, &args.project_id))?; let topic = Topic::generate(); diff --git a/examples/webhook.rs b/examples/webhook.rs index 36cf6de..b6380a0 100644 --- a/examples/webhook.rs +++ b/examples/webhook.rs @@ -4,7 +4,7 @@ use { ConnectionOptions, }, relay_rpc::{ - auth::{ed25519_dalek::Keypair, rand, AuthToken}, + auth::{ed25519_dalek::SigningKey, rand, AuthToken}, domain::{DecodedClientId, Topic}, jwt::VerifyableClaims, rpc, @@ -35,7 +35,7 @@ struct Args { webhook_server_port: u16, } -fn create_conn_opts(key: &Keypair, address: &str, project_id: &str) -> ConnectionOptions { +fn create_conn_opts(key: &SigningKey, address: &str, project_id: &str) -> ConnectionOptions { let aud = Url::parse(address) .unwrap() .origin() @@ -122,7 +122,7 @@ async fn main() -> anyhow::Result<()> { // Give time for the server to start up. tokio::time::sleep(Duration::from_millis(500)).await; - let publisher_key = Keypair::generate(&mut rand::thread_rng()); + let publisher_key = SigningKey::generate(&mut rand::thread_rng()); let publisher = Client::new(&create_conn_opts( &publisher_key, &args.address, @@ -130,10 +130,10 @@ async fn main() -> anyhow::Result<()> { ))?; println!( "[publisher] client id: {}", - DecodedClientId::from(publisher_key.public_key()).to_did_key() + DecodedClientId::from(publisher_key.verifying_key()).to_did_key() ); - let subscriber_key = Keypair::generate(&mut rand::thread_rng()); + let subscriber_key = SigningKey::generate(&mut rand::thread_rng()); let subscriber = Client::new(&create_conn_opts( &subscriber_key, &args.address, @@ -141,7 +141,7 @@ async fn main() -> anyhow::Result<()> { ))?; println!( "[subscriber] client id: {}", - DecodedClientId::from(subscriber_key.public_key()).to_did_key() + DecodedClientId::from(subscriber_key.verifying_key()).to_did_key() ); let topic = Topic::generate(); diff --git a/examples/websocket_client.rs b/examples/websocket_client.rs index 871ebdc..aea2b4e 100644 --- a/examples/websocket_client.rs +++ b/examples/websocket_client.rs @@ -5,7 +5,7 @@ use { ConnectionOptions, }, relay_rpc::{ - auth::{ed25519_dalek::Keypair, rand, AuthToken}, + auth::{ed25519_dalek::SigningKey, rand, AuthToken}, domain::Topic, }, std::{sync::Arc, time::Duration}, @@ -59,7 +59,7 @@ impl ConnectionHandler for Handler { } fn create_conn_opts(address: &str, project_id: &str) -> ConnectionOptions { - let key = Keypair::generate(&mut rand::thread_rng()); + let key = SigningKey::generate(&mut rand::thread_rng()); let auth = AuthToken::new("http://example.com") .aud(address) diff --git a/relay_client/src/http.rs b/relay_client/src/http.rs index 2e3df43..26ec4d2 100644 --- a/relay_client/src/http.rs +++ b/relay_client/src/http.rs @@ -6,7 +6,7 @@ use { }, http::{HeaderMap, StatusCode}, relay_rpc::{ - auth::ed25519_dalek::Keypair, + auth::ed25519_dalek::SigningKey, domain::{DecodedClientId, SubscriptionId, Topic}, jwt::{self, JwtError, VerifyableClaims}, rpc::{self, Receipt, RequestPayload}, @@ -168,7 +168,7 @@ impl Client { pub async fn watch_register( &self, request: WatchRegisterRequest, - keypair: &Keypair, + keypair: &SigningKey, ) -> Response { let iat = chrono::Utc::now().timestamp(); let ttl_sec: i64 = request @@ -180,7 +180,7 @@ impl Client { let claims = rpc::WatchRegisterClaims { basic: jwt::JwtBasicClaims { - iss: DecodedClientId::from_key(&keypair.public_key()).into(), + iss: DecodedClientId::from_key(&keypair.verifying_key()).into(), aud: self.origin.clone(), iat, sub: request.service_url, @@ -212,13 +212,13 @@ impl Client { pub async fn watch_unregister( &self, request: WatchUnregisterRequest, - keypair: &Keypair, + keypair: &SigningKey, ) -> Response { let iat = chrono::Utc::now().timestamp(); let claims = rpc::WatchUnregisterClaims { basic: jwt::JwtBasicClaims { - iss: DecodedClientId::from_key(&keypair.public_key()).into(), + iss: DecodedClientId::from_key(&keypair.verifying_key()).into(), aud: self.origin.clone(), iat, sub: request.service_url, diff --git a/relay_rpc/Cargo.toml b/relay_rpc/Cargo.toml index e8c8fc1..f25c10d 100644 --- a/relay_rpc/Cargo.toml +++ b/relay_rpc/Cargo.toml @@ -30,8 +30,8 @@ serde = { version = "1.0", features = ["derive", "rc"] } serde-aux = { version = "4.1", default-features = false } serde_json = "1.0" thiserror = "1.0" -ed25519-dalek = { git = "https://github.com/dalek-cryptography/ed25519-dalek.git", rev = "7529d65" } -rand = "0.7" +ed25519-dalek = { version = "2.1.1", features = ["rand_core"] } +rand = "0.8" chrono = { version = "0.4", default-features = false, features = [ "std", "clock", diff --git a/relay_rpc/src/auth.rs b/relay_rpc/src/auth.rs index f95fc79..88a45d5 100644 --- a/relay_rpc/src/auth.rs +++ b/relay_rpc/src/auth.rs @@ -4,7 +4,7 @@ use { jwt::{JwtBasicClaims, JwtHeader}, }, chrono::{DateTime, Utc}, - ed25519_dalek::{ed25519::signature::Signature, Keypair, Signer}, + ed25519_dalek::{Signer, SigningKey}, serde::{Deserialize, Serialize}, std::{fmt::Display, time::Duration}, }; @@ -80,7 +80,7 @@ impl AuthToken { self } - pub fn as_jwt(&self, key: &Keypair) -> Result { + pub fn as_jwt(&self, key: &SigningKey) -> Result { let iat = self.iat.unwrap_or_else(Utc::now); let aud = self.aud.as_deref().unwrap_or(DEFAULT_TOKEN_AUD); @@ -89,7 +89,7 @@ impl AuthToken { } pub fn encode_auth_token( - key: &Keypair, + key: &SigningKey, sub: impl Into, aud: impl Into, iat: DateTime, @@ -105,7 +105,7 @@ pub fn encode_auth_token( let claims = { let data = JwtBasicClaims { - iss: DecodedClientId::from_key(&key.public_key()).into(), + iss: DecodedClientId::from_key(&key.verifying_key()).into(), sub: sub.into(), aud: aud.into(), iat: iat.timestamp(), @@ -121,7 +121,7 @@ pub fn encode_auth_token( let signature = { let data = key.sign(message.as_bytes()); - encoder.encode(data.as_bytes()) + encoder.encode(&data.to_bytes()) }; Ok(SerializedAuthToken(format!("{message}.{signature}"))) diff --git a/relay_rpc/src/domain.rs b/relay_rpc/src/domain.rs index 146a198..63299a0 100644 --- a/relay_rpc/src/domain.rs +++ b/relay_rpc/src/domain.rs @@ -9,7 +9,7 @@ use { new_type, }, derive_more::{AsMut, AsRef}, - ed25519_dalek::PublicKey, + ed25519_dalek::VerifyingKey, serde::{Deserialize, Serialize}, serde_aux::prelude::deserialize_number_from_string, std::{str::FromStr, sync::Arc}, @@ -82,7 +82,7 @@ pub struct DidKey( #[serde(with = "crate::serde_helpers::client_id_as_did_key")] pub DecodedClientId, ); -impl From for PublicKey { +impl From for VerifyingKey { fn from(val: DidKey) -> Self { val.0.as_public_key() } @@ -119,24 +119,24 @@ impl DecodedClientId { } #[inline] - pub fn from_key(key: &PublicKey) -> Self { + pub fn from_key(key: &VerifyingKey) -> Self { Self(*key.as_bytes()) } #[inline] - pub fn as_public_key(&self) -> PublicKey { + pub fn as_public_key(&self) -> VerifyingKey { // We know that the length is correct, so we can just unwrap. - PublicKey::from_bytes(&self.0).unwrap() + VerifyingKey::from_bytes(&self.0).unwrap() } } -impl From for DecodedClientId { - fn from(key: PublicKey) -> Self { +impl From for DecodedClientId { + fn from(key: VerifyingKey) -> Self { Self::from_key(&key) } } -impl From for PublicKey { +impl From for VerifyingKey { fn from(val: DecodedClientId) -> Self { val.as_public_key() } diff --git a/relay_rpc/src/jwt.rs b/relay_rpc/src/jwt.rs index 406c467..3eab799 100644 --- a/relay_rpc/src/jwt.rs +++ b/relay_rpc/src/jwt.rs @@ -1,7 +1,7 @@ use { crate::domain::DidKey, chrono::Utc, - ed25519_dalek::{ed25519::signature::Signature, Keypair, PublicKey, Signer}, + ed25519_dalek::{Signer, SigningKey}, serde::{de::DeserializeOwned, Deserialize, Serialize}, std::collections::HashSet, }; @@ -103,12 +103,11 @@ pub trait VerifyableClaims: Serialize + DeserializeOwned { /// Encodes the claims into a JWT string, signing it with the provided key. /// Returns an error if the provided key does not match the public key in /// the claims (`iss`), or if serialization fails. - fn encode(&self, key: &Keypair) -> Result { - let public_key = PublicKey::from_bytes(self.basic().iss.as_ref()) - .map_err(|_| JwtError::InvalidKeypair)?; + fn encode(&self, key: &SigningKey) -> Result { + let public_key = self.basic().iss.0.as_public_key(); // Make sure the keypair matches the public key in the claims. - if public_key != key.public_key() { + if public_key != key.verifying_key() { return Err(JwtError::InvalidKeypair); } @@ -116,7 +115,7 @@ pub trait VerifyableClaims: Serialize + DeserializeOwned { let header = encoder.encode(serde_json::to_string(&JwtHeader::default())?.as_bytes()); let claims = encoder.encode(serde_json::to_string(self)?.as_bytes()); let message = format!("{header}.{claims}"); - let signature = encoder.encode(key.sign(message.as_bytes()).as_bytes()); + let signature = encoder.encode(&key.sign(message.as_bytes()).to_bytes()); Ok(format!("{message}.{signature}")) } @@ -240,7 +239,8 @@ mod test { domain::ClientId, jwt::{JwtBasicClaims, JwtError, VerifyableClaims, JWT_VALIDATION_TIME_LEEWAY_SECS}, }, - ed25519_dalek::Keypair, + ed25519_dalek::SigningKey, + rand::rngs::OsRng, std::{collections::HashSet, time::Duration}, }; @@ -283,7 +283,7 @@ mod test { let jwt = Jwt("eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJkaWQ6a2V5Ono2TWtvZEhad25lVlJTaHRhTGY4SktZa3hwREdwMXZHWm5wR21kQnBYOE0yZXh4SGwiLCJzdWIiOiJjNDc5ZmU1ZGM0NjRlNzcxZTc4YjE5M2QyMzlhNjViNThkMjc4Y2FkMWMzNGJmYjBiNTcxNmU1YmI1MTQ5MjhlIiwiYXVkIjoid3NzOi8vcmVsYXkud2FsbGV0Y29ubmVjdC5jb20iLCJpYXQiOjE2NTY5MTAwOTcsImV4cCI6NDgxMjY3MDA5N30.nLdxz4f6yJ8HsWZJUvpSHjFjoat4PfJav-kyqdHj6JXcX5SyDvp3QNB9doyzRWb9jpbA36Av0qn4kqLl-pGuBg".to_owned()); assert!(matches!(jwt.decode(&aud), Err(JwtError::Serialization(..)))); - let keypair = Keypair::generate(&mut rand::thread_rng()); + let keypair = SigningKey::generate(&mut OsRng); let sub: String = "test".to_owned(); // IAT in future. diff --git a/relay_rpc/src/rpc/watch.rs b/relay_rpc/src/rpc/watch.rs index 40909dd..26686b3 100644 --- a/relay_rpc/src/rpc/watch.rs +++ b/relay_rpc/src/rpc/watch.rs @@ -125,25 +125,23 @@ mod test { super::*, crate::{auth::RELAY_WEBSOCKET_ADDRESS, domain::DecodedClientId}, chrono::DateTime, - ed25519_dalek::Keypair, + ed25519_dalek::SigningKey, }; - const KEYPAIR: [u8; 64] = [ + const KEYPAIR: [u8; 32] = [ 215, 142, 127, 216, 153, 183, 205, 110, 103, 118, 181, 195, 60, 71, 5, 221, 100, 196, 207, - 81, 229, 11, 116, 121, 235, 104, 1, 121, 25, 18, 218, 83, 216, 230, 100, 248, 132, 110, 55, - 65, 221, 87, 66, 160, 36, 95, 116, 86, 169, 49, 107, 17, 13, 50, 22, 147, 199, 109, 125, - 155, 89, 190, 186, 171, + 81, 229, 11, 116, 121, 235, 104, 1, 121, 25, 18, 218, 83, ]; #[test] fn watch_register_jwt() { - let key = Keypair::from_bytes(&KEYPAIR).unwrap(); + let key = SigningKey::from_bytes(&KEYPAIR); let iat = DateTime::parse_from_rfc3339("2000-01-01T00:00:00Z").unwrap(); let exp = DateTime::parse_from_rfc3339("3000-01-01T00:00:00Z").unwrap(); let claims = WatchRegisterClaims { basic: JwtBasicClaims { - iss: DecodedClientId::from_key(&key.public_key()).into(), + iss: DecodedClientId::from_key(&key.verifying_key()).into(), aud: RELAY_WEBSOCKET_ADDRESS.to_owned(), sub: "https://example.com".to_owned(), iat: iat.timestamp(), @@ -172,13 +170,13 @@ mod test { #[test] fn watch_unregister_jwt() { - let key = Keypair::from_bytes(&KEYPAIR).unwrap(); + let key = SigningKey::from_bytes(&KEYPAIR); let iat = DateTime::parse_from_rfc3339("2000-01-01T00:00:00Z").unwrap(); let exp = DateTime::parse_from_rfc3339("3000-01-01T00:00:00Z").unwrap(); let claims = WatchUnregisterClaims { basic: JwtBasicClaims { - iss: DecodedClientId::from_key(&key.public_key()).into(), + iss: DecodedClientId::from_key(&key.verifying_key()).into(), aud: RELAY_WEBSOCKET_ADDRESS.to_owned(), sub: "https://example.com".to_owned(), iat: iat.timestamp(), @@ -205,14 +203,14 @@ mod test { #[test] fn watch_event_jwt() { - let key = Keypair::from_bytes(&KEYPAIR).unwrap(); + let key = SigningKey::from_bytes(&KEYPAIR); let iat = DateTime::parse_from_rfc3339("2000-01-01T00:00:00Z").unwrap(); let exp = DateTime::parse_from_rfc3339("3000-01-01T00:00:00Z").unwrap(); let topic = Topic::from("474e88153f4db893de42c35e1891dc0e37a02e11961385de0475460fb48b8639"); let claims = WatchEventClaims { basic: JwtBasicClaims { - iss: DecodedClientId::from_key(&key.public_key()).into(), + iss: DecodedClientId::from_key(&key.verifying_key()).into(), aud: RELAY_WEBSOCKET_ADDRESS.to_owned(), sub: "https://example.com".to_owned(), iat: iat.timestamp(), From e96f98a601c08dd3a5b826f5920ed13c5f41596a Mon Sep 17 00:00:00 2001 From: Chris Smith Date: Thu, 15 Feb 2024 11:43:43 -0500 Subject: [PATCH 2/2] fix: remove rand and chrono re-export --- Cargo.toml | 1 + examples/http_client.rs | 2 +- examples/webhook.rs | 2 +- examples/websocket_client.rs | 2 +- relay_rpc/src/auth.rs | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8b29593..134428a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,6 +29,7 @@ tokio = { version = "1.22", features = ["full"] } url = "2.3" warp = { version = "0.3", default-features = false } serde_json = "1.0" +rand = "0.8.5" [[example]] name = "websocket_client" diff --git a/examples/http_client.rs b/examples/http_client.rs index 04d1823..32e2743 100644 --- a/examples/http_client.rs +++ b/examples/http_client.rs @@ -1,7 +1,7 @@ use { relay_client::{http::Client, ConnectionOptions}, relay_rpc::{ - auth::{ed25519_dalek::SigningKey, rand, AuthToken}, + auth::{ed25519_dalek::SigningKey, AuthToken}, domain::Topic, }, std::{sync::Arc, time::Duration}, diff --git a/examples/webhook.rs b/examples/webhook.rs index b6380a0..eba14f5 100644 --- a/examples/webhook.rs +++ b/examples/webhook.rs @@ -4,7 +4,7 @@ use { ConnectionOptions, }, relay_rpc::{ - auth::{ed25519_dalek::SigningKey, rand, AuthToken}, + auth::{ed25519_dalek::SigningKey, AuthToken}, domain::{DecodedClientId, Topic}, jwt::VerifyableClaims, rpc, diff --git a/examples/websocket_client.rs b/examples/websocket_client.rs index aea2b4e..562006b 100644 --- a/examples/websocket_client.rs +++ b/examples/websocket_client.rs @@ -5,7 +5,7 @@ use { ConnectionOptions, }, relay_rpc::{ - auth::{ed25519_dalek::SigningKey, rand, AuthToken}, + auth::{ed25519_dalek::SigningKey, AuthToken}, domain::Topic, }, std::{sync::Arc, time::Duration}, diff --git a/relay_rpc/src/auth.rs b/relay_rpc/src/auth.rs index 88a45d5..a1128a5 100644 --- a/relay_rpc/src/auth.rs +++ b/relay_rpc/src/auth.rs @@ -1,3 +1,4 @@ +pub use ed25519_dalek; use { crate::{ domain::DecodedClientId, @@ -8,7 +9,6 @@ use { serde::{Deserialize, Serialize}, std::{fmt::Display, time::Duration}, }; -pub use {chrono, ed25519_dalek, rand}; #[cfg(feature = "cacao")] pub mod cacao;