1
0
Fork 0
mirror of https://codeberg.org/tyy/aspm synced 2024-12-22 15:59:29 -07:00

Support aspe servers in crates/asp, and remove nightly requirement

This commit is contained in:
TymanWasTaken 2023-07-01 22:55:05 -04:00
parent 19c42f6138
commit 57e73d62dd
Signed by: Ty
GPG key ID: 2813440C772555A4
8 changed files with 371 additions and 55 deletions

View file

@ -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

136
crates/asp/src/aspe/mod.rs Normal file
View file

@ -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<reqwest::Error> 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<reqwest::Error> 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<Self> {
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<String>) -> 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<String>,
) -> Result<String, AspeFetchFailure> {
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")
}
}

View file

@ -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::<AspKey>::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::<AspKey>::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::<AspKey>::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::<AspKey>::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::<AspKey>::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::<AspKey>::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"
)
}
}

View file

@ -86,27 +86,26 @@ impl AspKey {
}
pub fn generate(key_type: AspKeyType) -> Result<Self, AspKeyError> {
let result: anyhow::Result<Self> = try {
(|| -> anyhow::Result<Self> {
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<Box<dyn JwsSigner>> {

View file

@ -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";
}

View file

@ -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::<AspKey>::try_into(
Jwk::from_bytes(
r#"
{"kty":"EC",
"crv":"P-256",
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
"#,
)
.unwrap(),
)
.unwrap();
let key = TryInto::<AspKey>::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::<AspKey>::try_into(
Jwk::from_bytes(
r#"
{"kty":"EC",
"crv":"P-256",
"x":"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4",
"y":"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM",
"d":"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE"}
"#,
)
.unwrap(),
)
.unwrap();
let key = TryInto::<AspKey>::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(),

View file

@ -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 {

View file

@ -1,2 +0,0 @@
[toolchain]
channel = "nightly"