diff --git a/Cargo.toml b/Cargo.toml index 30f61a3..6a337b6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ name = "aspm" authors = ["Ty"] description = "A tool to manage ariadne signature profiles, implementing v0 of the specification" +repository = "https://codeberg.org/tyy/aspm" version = "0.1.0" edition = "2021" @@ -25,3 +26,6 @@ sea-orm = { version = "0.11.3", features = ["sqlx-sqlite", "runtime-tokio-native tokio = { version = "1.29.1", features = ["macros", "rt-multi-thread"] } sea-orm-migration = "0.11.3" async-trait = "0.1.68" + +[profile.release] +strip = true diff --git a/crates/asp/src/aspe/mod.rs b/crates/asp/src/aspe/mod.rs new file mode 100644 index 0000000..1a5e064 --- /dev/null +++ b/crates/asp/src/aspe/mod.rs @@ -0,0 +1,136 @@ +pub mod requests; + +use reqwest::{ + header::{self, HeaderValue}, + StatusCode, +}; +pub use url::Host; + +/// An ASPE-compatible server +pub struct AspeServer { + host: Host, + client: reqwest::Client, +} + +pub enum AspeRequestFailure { + /// Signals that the request failed because of invalid data being provided + BadRequest, + /// Signals that the request failed because the provided data was too large + TooLarge, + /// Signals that the request failed because of a rate-limit + RateLimited, + /// Signals that the request failed for some unknown reason + Unknown(reqwest::Error), +} + +impl From for AspeRequestFailure { + fn from(value: reqwest::Error) -> Self { + if !value.is_status() { + return Self::Unknown(value); + } else { + match value.status().unwrap() { + StatusCode::BAD_REQUEST => Self::BadRequest, + StatusCode::PAYLOAD_TOO_LARGE => Self::TooLarge, + StatusCode::TOO_MANY_REQUESTS => Self::RateLimited, + _ => Self::Unknown(value), + } + } + } +} + +pub enum AspeFetchFailure { + /// Signals that the request failed because the requested profile did not exist + NotFound, + /// Signals that the request failed because the provided data was too large + TooLarge, + /// Signals that the request failed because of a rate-limit + RateLimited, + /// Signals that the request failed for some unknown reason + Unknown(reqwest::Error), +} + +impl From for AspeFetchFailure { + fn from(value: reqwest::Error) -> Self { + if !value.is_status() { + return Self::Unknown(value); + } else { + match value.status().unwrap() { + StatusCode::NOT_FOUND => Self::NotFound, + StatusCode::PAYLOAD_TOO_LARGE => Self::TooLarge, + StatusCode::TOO_MANY_REQUESTS => Self::RateLimited, + _ => Self::Unknown(value), + } + } + } +} + +impl AspeServer { + /// Creates a new [AspeServer] instance given the specified domain. A domain and ONLY a domain should be provided (no scheme, no path, etc). + pub fn new(host: Host) -> anyhow::Result { + Ok(Self { + host, + client: reqwest::Client::builder() + .user_agent(format!( + "asp-rs/{version} ({repo})", + version = env!("CARGO_PKG_VERSION"), + repo = env!("CARGO_PKG_REPOSITORY") + )) + .https_only(true) + .build()?, + }) + } + + /// POSTs a request JWS to the aspe server. This will return a result with the [Err] variant containing an enum detailing specifically why the request failed. + pub async fn post_request(&self, jws: impl Into) -> Result<(), AspeRequestFailure> { + self.client + .post(format!( + "https://{host}/.well-known/aspe/post/", + host = self.host + )) + .header( + header::CONTENT_TYPE, + HeaderValue::from_static("application/asp+jwt; charset=UTF-8"), + ) + .body(jws.into()) + .send() + .await?; + + Ok(()) + } + + pub async fn fetch_profile( + &self, + fingerprint: impl Into, + ) -> Result { + let res = self + .client + .get(format!( + "https://{host}/.well-known/aspe/id/{fingerprint}", + host = self.host, + fingerprint = fingerprint.into() + )) + .header( + header::ACCEPT, + HeaderValue::from_static("application/asp+jwt; charset=UTF-8"), + ) + .send() + .await? + .text_with_charset("utf-8") + .await?; + + Ok(res) + } +} + +#[cfg(test)] +mod tests { + // TODO: After an implementation of an ASPE server is created, mock it for testing + + use super::{AspeServer, Host}; + + #[test] + fn building_aspe_server_succeeds() { + let result = AspeServer::new(Host::Domain(String::from("example.com"))); + assert!(result.is_ok(), "Constructing an AspeServer should succeed") + } +} diff --git a/crates/asp/src/aspe/requests.rs b/crates/asp/src/aspe/requests.rs new file mode 100644 index 0000000..3e04759 --- /dev/null +++ b/crates/asp/src/aspe/requests.rs @@ -0,0 +1,184 @@ +use serde::{Deserialize, Serialize}; + +use crate::utils::jwt::JwtSerializable; + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum AspeRequestType { + Request, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(tag = "http://ariadne.id/action", rename_all = "camelCase")] +pub enum AspeRequestVariant { + Create { + #[serde(rename = "http://ariadne.id/profile_jws")] + profile_jws: String, + }, + Update { + #[serde(rename = "http://ariadne.id/profile_jws")] + profile_jws: String, + #[serde(rename = "http://ariadne.id/aspe_uri")] + aspe_uri: String, + }, + Delete { + #[serde(rename = "http://ariadne.id/aspe_uri")] + aspe_uri: String, + }, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq)] +pub struct AspeRequest { + #[serde(rename = "http://ariadne.id/version")] + pub version: u8, + #[serde(rename = "http://ariadne.id/type")] + pub r#type: AspeRequestType, + #[serde(flatten)] + pub request: AspeRequestVariant, +} + +impl JwtSerializable for AspeRequest {} + +#[cfg(test)] +mod tests { + use josekit::jwk::Jwk; + + use crate::{keys::AspKey, utils::jwt::JwtSerialize}; + + use super::{AspeRequest, AspeRequestType, AspeRequestVariant}; + + #[test] + fn serialize_create_request_jwt() { + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); + let profile = crate::test_constants::PROFILE; + + let request = AspeRequest { + version: 0, + r#type: AspeRequestType::Request, + request: AspeRequestVariant::Create { + profile_jws: profile.to_string(), + }, + }; + let encoded = request.encode_and_sign(&key); + assert!( + encoded.is_ok(), + "ASPE request JWS should sign and encode successfully" + ); + } + + #[test] + fn deserialize_create_request_jwt() { + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); + + let decoded = AspeRequest::decode_and_verify(crate::test_constants::CREATE_REQUEST, &key); + assert!( + decoded.is_ok(), + "ASPE request JWS should verify and decode successfully" + ); + let decoded = decoded.unwrap(); + + assert_eq!( + *decoded, + AspeRequest { + version: 0, + r#type: AspeRequestType::Request, + request: AspeRequestVariant::Create { + profile_jws: crate::test_constants::PROFILE.to_string(), + }, + }, + "Decoded create request should be correct" + ) + } + + #[test] + fn serialize_update_request_jwt() { + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); + + let request = AspeRequest { + version: 0, + r#type: AspeRequestType::Request, + request: AspeRequestVariant::Update { + profile_jws: crate::test_constants::PROFILE.to_string(), + aspe_uri: crate::test_constants::ASPE_URI.to_string(), + }, + }; + let encoded = dbg!(request.encode_and_sign(&key)); + assert!( + encoded.is_ok(), + "ASPE request JWS should sign and encode successfully" + ); + } + + #[test] + fn deserialize_update_request_jwt() { + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); + + let decoded = AspeRequest::decode_and_verify(crate::test_constants::UPDATE_REQUEST, &key); + assert!( + decoded.is_ok(), + "ASPE request JWS should verify and decode successfully" + ); + let decoded = decoded.unwrap(); + + assert_eq!( + *decoded, + AspeRequest { + version: 0, + r#type: AspeRequestType::Request, + request: AspeRequestVariant::Update { + profile_jws: crate::test_constants::PROFILE.to_string(), + aspe_uri: crate::test_constants::ASPE_URI.to_string(), + }, + }, + "Decoded update request should be correct" + ) + } + + #[test] + fn serialize_delete_request_jwt() { + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); + + let request = AspeRequest { + version: 0, + r#type: AspeRequestType::Request, + request: AspeRequestVariant::Delete { + aspe_uri: crate::test_constants::ASPE_URI.to_string(), + }, + }; + let encoded = dbg!(request.encode_and_sign(&key)); + assert!( + encoded.is_ok(), + "ASPE request JWS should sign and encode successfully" + ); + } + + #[test] + fn deserialize_delete_request_jwt() { + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); + + let decoded = AspeRequest::decode_and_verify(crate::test_constants::DELETE_REQUEST, &key); + assert!( + decoded.is_ok(), + "ASPE request JWS should verify and decode successfully" + ); + let decoded = decoded.unwrap(); + + assert_eq!( + *decoded, + AspeRequest { + version: 0, + r#type: AspeRequestType::Request, + request: AspeRequestVariant::Delete { + aspe_uri: crate::test_constants::ASPE_URI.to_string(), + }, + }, + "Decoded delete request should be correct" + ) + } +} diff --git a/crates/asp/src/keys/mod.rs b/crates/asp/src/keys/mod.rs index cf4d130..cad277c 100644 --- a/crates/asp/src/keys/mod.rs +++ b/crates/asp/src/keys/mod.rs @@ -86,27 +86,26 @@ impl AspKey { } pub fn generate(key_type: AspKeyType) -> Result { - let result: anyhow::Result = try { + (|| -> anyhow::Result { match key_type { AspKeyType::Ed25519 => { let jwk = Jwk::generate_ed_key(EdCurve::Ed25519)?; - Self { + Ok(Self { key_type, fingerprint: jwk.get_fingerprint()?, jwk, - } + }) } AspKeyType::ES256 => { let jwk = Jwk::generate_ec_key(EcCurve::P256)?; - Self { + Ok(Self { key_type, fingerprint: jwk.get_fingerprint()?, jwk, - } + }) } } - }; - result.or(Err(AspKeyError::GenerationError)) + })().or(Err(AspKeyError::GenerationError)) } pub fn create_signer(&self) -> anyhow::Result> { diff --git a/crates/asp/src/lib.rs b/crates/asp/src/lib.rs index e052a39..7f6ab67 100644 --- a/crates/asp/src/lib.rs +++ b/crates/asp/src/lib.rs @@ -1,5 +1,22 @@ -#![feature(try_blocks)] - +pub mod aspe; pub mod keys; pub mod profiles; pub mod utils; + +#[cfg(test)] +pub(crate) mod test_constants { + // NOTE: This key is taken from the example keys in RFC 7517 + pub(crate) static KEY: &'static str = r#" + {"kty":"EC", + "crv":"P-256", + "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", + "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", + "d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"} + "#; + pub(crate) static ASPE_URI: &'static str = "aspe:example.com:452JFAI6B3KOLKBAUX3MC73DAU"; + + pub(crate) static PROFILE: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicHJvZmlsZSIsImh0dHA6Ly9hcmlhZG5lLmlkL25hbWUiOiJFeGFtcGxlIG5hbWUiLCJodHRwOi8vYXJpYWRuZS5pZC9jbGFpbXMiOlsiZG5zOmV4YW1wbGUuY29tP3R5cGU9VFhUIiwiaHR0cHM6Ly9naXQuZXhhbXBsZS5jb20vZXhhbXBsZS9mb3JnZWpvX3Byb29mIl0sImh0dHA6Ly9hcmlhZG5lLmlkL2NvbG9yIjoiI0E0MzRFQiJ9.u5AbAqRpyXetXwU_QqpZrieNzwZGCRZ0tFTL4FoIwPRiZZ9iIGBnqs7PWbsd0iHQpYT_Q7s1GmwggGssM9ttxQ"; + pub(crate) static CREATE_REQUEST: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicmVxdWVzdCIsImh0dHA6Ly9hcmlhZG5lLmlkL2FjdGlvbiI6ImNyZWF0ZSIsImh0dHA6Ly9hcmlhZG5lLmlkL3Byb2ZpbGVfandzIjoiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalExTWtwR1FVazJRak5MVDB4TFFrRlZXRE5OUXpjelJFRlZJaXdpYW5kcklqcDdJbXQwZVNJNklrVkRJaXdpWTNKMklqb2lVQzB5TlRZaUxDSjRJam9pVFV0Q1ExUk9TV05MVlZORWFXa3hNWGxUY3pNMU1qWnBSRm80UVdsVWJ6ZFVkVFpMVUVGeGRqZEVOQ0lzSW5raU9pSTBSWFJzTmxOU1Z6SlphVXhWY2s0MWRtWjJWa2gxYUhBM2VEaFFlR3gwYlZkWGJHSmlUVFJKUm5sTkluMTkuZXlKb2RIUndPaTh2WVhKcFlXUnVaUzVwWkM5MlpYSnphVzl1SWpvd0xDSm9kSFJ3T2k4dllYSnBZV1J1WlM1cFpDOTBlWEJsSWpvaWNISnZabWxzWlNJc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyNWhiV1VpT2lKRmVHRnRjR3hsSUc1aGJXVWlMQ0pvZEhSd09pOHZZWEpwWVdSdVpTNXBaQzlqYkdGcGJYTWlPbHNpWkc1ek9tVjRZVzF3YkdVdVkyOXRQM1I1Y0dVOVZGaFVJaXdpYUhSMGNITTZMeTluYVhRdVpYaGhiWEJzWlM1amIyMHZaWGhoYlhCc1pTOW1iM0puWldwdlgzQnliMjltSWwwc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyTnZiRzl5SWpvaUkwRTBNelJGUWlKOS51NUFiQXFScHlYZXRYd1VfUXFwWnJpZU56d1pHQ1JaMHRGVEw0Rm9Jd1BSaVpaOWlJR0JucXM3UFdic2QwaUhRcFlUX1E3czFHbXdnZ0dzc005dHR4USJ9.f8NdVzrjCZKT2R5MzUZkgcnNIJWo6ftQj6MCvXF5cgpjYt3suTqOGoBs6EKvtsgVGs12uS4ZxNnVAnFMsKKGlQ"; + pub(crate) static UPDATE_REQUEST: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicmVxdWVzdCIsImh0dHA6Ly9hcmlhZG5lLmlkL2FjdGlvbiI6InVwZGF0ZSIsImh0dHA6Ly9hcmlhZG5lLmlkL3Byb2ZpbGVfandzIjoiZXlKMGVYQWlPaUpLVjFRaUxDSmhiR2NpT2lKRlV6STFOaUlzSW10cFpDSTZJalExTWtwR1FVazJRak5MVDB4TFFrRlZXRE5OUXpjelJFRlZJaXdpYW5kcklqcDdJbXQwZVNJNklrVkRJaXdpWTNKMklqb2lVQzB5TlRZaUxDSjRJam9pVFV0Q1ExUk9TV05MVlZORWFXa3hNWGxUY3pNMU1qWnBSRm80UVdsVWJ6ZFVkVFpMVUVGeGRqZEVOQ0lzSW5raU9pSTBSWFJzTmxOU1Z6SlphVXhWY2s0MWRtWjJWa2gxYUhBM2VEaFFlR3gwYlZkWGJHSmlUVFJKUm5sTkluMTkuZXlKb2RIUndPaTh2WVhKcFlXUnVaUzVwWkM5MlpYSnphVzl1SWpvd0xDSm9kSFJ3T2k4dllYSnBZV1J1WlM1cFpDOTBlWEJsSWpvaWNISnZabWxzWlNJc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyNWhiV1VpT2lKRmVHRnRjR3hsSUc1aGJXVWlMQ0pvZEhSd09pOHZZWEpwWVdSdVpTNXBaQzlqYkdGcGJYTWlPbHNpWkc1ek9tVjRZVzF3YkdVdVkyOXRQM1I1Y0dVOVZGaFVJaXdpYUhSMGNITTZMeTluYVhRdVpYaGhiWEJzWlM1amIyMHZaWGhoYlhCc1pTOW1iM0puWldwdlgzQnliMjltSWwwc0ltaDBkSEE2THk5aGNtbGhaRzVsTG1sa0wyTnZiRzl5SWpvaUkwRTBNelJGUWlKOS51NUFiQXFScHlYZXRYd1VfUXFwWnJpZU56d1pHQ1JaMHRGVEw0Rm9Jd1BSaVpaOWlJR0JucXM3UFdic2QwaUhRcFlUX1E3czFHbXdnZ0dzc005dHR4USIsImh0dHA6Ly9hcmlhZG5lLmlkL2FzcGVfdXJpIjoiYXNwZTpleGFtcGxlLmNvbTo0NTJKRkFJNkIzS09MS0JBVVgzTUM3M0RBVSJ9.044vzbhefes8bFJFrXLwU2RNYhNvK_rNDDqM7NjEaC8alyFl-5Fh_Obj-pIKUkcxD-HL27y2objt_-lbDqvw4g"; + pub(crate) static DELETE_REQUEST: &'static str = "eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicmVxdWVzdCIsImh0dHA6Ly9hcmlhZG5lLmlkL2FjdGlvbiI6ImRlbGV0ZSIsImh0dHA6Ly9hcmlhZG5lLmlkL2FzcGVfdXJpIjoiYXNwZTpleGFtcGxlLmNvbTo0NTJKRkFJNkIzS09MS0JBVVgzTUM3M0RBVSJ9.DJNuN-wTXxOW3VZHcN_tlUIFOHfI0GeD_uzs1RplwsGTBe0Z4KpIojEQ85N7tSnuLxuGlsR8kd1SrbcvxhkWaw"; +} diff --git a/crates/asp/src/profiles/mod.rs b/crates/asp/src/profiles/mod.rs index b123851..2dc8ccc 100644 --- a/crates/asp/src/profiles/mod.rs +++ b/crates/asp/src/profiles/mod.rs @@ -3,21 +3,14 @@ use serde::{Deserialize, Serialize}; use serde_email::Email; use url::Url; -use crate::utils::jwt::JwtSerializable; - -#[derive(Serialize, Deserialize, Debug, PartialEq)] -#[serde(rename_all = "camelCase")] -pub enum AspType { - Profile, - Request, -} +use crate::utils::jwt::{AspJwsType, JwtSerializable}; #[derive(Serialize, Deserialize, Debug, PartialEq)] pub struct AriadneSignatureProfile { #[serde(rename = "http://ariadne.id/version")] pub version: u8, #[serde(rename = "http://ariadne.id/type")] - pub r#type: AspType, + pub r#type: AspJwsType, #[serde(rename = "http://ariadne.id/name")] pub name: String, #[serde(rename = "http://ariadne.id/claims")] @@ -53,30 +46,21 @@ mod tests { use hex_color::HexColor; use josekit::jwk::Jwk; - use crate::{keys::AspKey, utils::jwt::JwtSerialize}; + use crate::{ + keys::AspKey, + utils::jwt::{AspJwsType, JwtSerialize}, + }; - use super::{AriadneSignatureProfile, AspType}; + use super::AriadneSignatureProfile; #[test] fn serializing_profile_succeeds() { - // NOTE: This key is taken from the example keys in RFC 7517 - let key = TryInto::::try_into( - Jwk::from_bytes( - r#" - {"kty":"EC", - "crv":"P-256", - "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"} - "#, - ) - .unwrap(), - ) - .unwrap(); + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); let profile = AriadneSignatureProfile { version: 0, - r#type: AspType::Profile, + r#type: AspJwsType::Profile, name: "Example name".to_string(), claims: vec![ "dns:example.com?type=TXT".to_string(), @@ -94,31 +78,18 @@ mod tests { #[test] fn deserializing_profile_succeeds() { - // NOTE: This key is taken from the example keys in RFC 7517 - let key = TryInto::::try_into( - Jwk::from_bytes( - r#" - {"kty":"EC", - "crv":"P-256", - "x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4", - "y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM", - "d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"} - "#, - ) - .unwrap(), - ) - .unwrap(); + let key = TryInto::::try_into(Jwk::from_bytes(crate::test_constants::KEY).unwrap()) + .unwrap(); - let jwt = r"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsImtpZCI6IjQ1MkpGQUk2QjNLT0xLQkFVWDNNQzczREFVIiwiandrIjp7Imt0eSI6IkVDIiwiY3J2IjoiUC0yNTYiLCJ4IjoiTUtCQ1ROSWNLVVNEaWkxMXlTczM1MjZpRFo4QWlUbzdUdTZLUEFxdjdENCIsInkiOiI0RXRsNlNSVzJZaUxVck41dmZ2Vkh1aHA3eDhQeGx0bVdXbGJiTTRJRnlNIn19.eyJodHRwOi8vYXJpYWRuZS5pZC92ZXJzaW9uIjowLCJodHRwOi8vYXJpYWRuZS5pZC90eXBlIjoicHJvZmlsZSIsImh0dHA6Ly9hcmlhZG5lLmlkL25hbWUiOiJFeGFtcGxlIG5hbWUiLCJodHRwOi8vYXJpYWRuZS5pZC9jbGFpbXMiOlsiZG5zOmV4YW1wbGUuY29tP3R5cGU9VFhUIiwiaHR0cHM6Ly9naXQuZXhhbXBsZS5jb20vZXhhbXBsZS9mb3JnZWpvX3Byb29mIl0sImh0dHA6Ly9hcmlhZG5lLmlkL2NvbG9yIjoiI0E0MzRFQiJ9.u5AbAqRpyXetXwU_QqpZrieNzwZGCRZ0tFTL4FoIwPRiZZ9iIGBnqs7PWbsd0iHQpYT_Q7s1GmwggGssM9ttxQ"; - - let profile = AriadneSignatureProfile::decode_and_verify(jwt, &key); + let profile = + AriadneSignatureProfile::decode_and_verify(crate::test_constants::PROFILE, &key); assert!(profile.is_ok(), "Profile should parse and verify correctly"); let profile = profile.unwrap(); assert_eq!( *profile, AriadneSignatureProfile { version: 0, - r#type: AspType::Profile, + r#type: AspJwsType::Profile, name: "Example name".to_string(), claims: vec![ "dns:example.com?type=TXT".to_string(), diff --git a/crates/asp/src/utils/jwt.rs b/crates/asp/src/utils/jwt.rs index 0f1eced..4478829 100644 --- a/crates/asp/src/utils/jwt.rs +++ b/crates/asp/src/utils/jwt.rs @@ -8,6 +8,13 @@ use thiserror::Error; use crate::keys::{AspKey, JwsHeaderExt}; +#[derive(Serialize, Deserialize, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum AspJwsType { + Profile, + Request, +} + pub trait JwtSerializable {} pub trait JwtSerialize { diff --git a/rust-toolchain.toml b/rust-toolchain.toml deleted file mode 100644 index 5d56faf..0000000 --- a/rust-toolchain.toml +++ /dev/null @@ -1,2 +0,0 @@ -[toolchain] -channel = "nightly"