Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions rsky-lexicon/src/com/atproto/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,12 @@ pub struct SignPlcOperationRequest {
pub verification_methods: Option<BTreeMap<String, String>>,
pub services: Option<JsonValue>,
}

#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GetRecommendedDidCredentialsResponse {
pub also_known_as: Vec<String>,
pub verification_methods: JsonValue,
pub rotation_keys: Vec<String>,
pub services: JsonValue,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use crate::account_manager::helpers::account::AvailabilityFlags;
use crate::account_manager::AccountManager;
use crate::apis::ApiError;
use crate::auth_verifier::AccessStandard;
use crate::config::ServerConfig;
use rocket::serde::json::Json;
use rocket::State;
use rsky_crypto::utils::encode_did_key;
use rsky_lexicon::com::atproto::identity::GetRecommendedDidCredentialsResponse;
use secp256k1::{Keypair, Secp256k1, SecretKey};
use serde_json::json;
use std::env;

#[tracing::instrument(skip_all)]
#[rocket::get("/xrpc/com.atproto.identity.getRecommendedDidCredentials")]
pub async fn get_recommended_did_credentials(
auth: AccessStandard,
cfg: &State<ServerConfig>,
) -> Result<Json<GetRecommendedDidCredentialsResponse>, ApiError> {
let requester = auth.access.credentials.unwrap().did.unwrap();
let availability_flags = AvailabilityFlags {
include_taken_down: Some(true),
include_deactivated: Some(true),
};
let account = AccountManager::get_account(&requester, Some(availability_flags))
.await?
.expect("Account not found despite valid access");

let mut also_known_as = Vec::new();
match account.handle {
None => {}
Some(res) => {
also_known_as.push("at://".to_string() + res.as_str());
}
}

let signing_key = get_public_signing_key()?;
let verification_methods = json!({
"atproto": signing_key
});

let rotation_key = get_public_rotation_key()?;
let rotation_keys = vec![rotation_key];

let services = json!({
"atproto_pds": {
"type": "AtprotoPersonalDataServer",
"endpoint": cfg.service.public_url
}
});
let response = GetRecommendedDidCredentialsResponse {
also_known_as,
verification_methods,
rotation_keys,
services,
};
Ok(Json(response))
}

fn get_public_rotation_key() -> Result<String, ApiError> {
let secp = Secp256k1::new();
let private_rotation_key = match env::var("PDS_PLC_ROTATION_KEY_K256_PRIVATE_KEY_HEX") {
Ok(res) => res,
Err(error) => {
tracing::error!("Error geting rotation private key\n{error}");
return Err(ApiError::RuntimeError);
}
};
match hex::decode(private_rotation_key.as_bytes()) {
Ok(bytes) => match SecretKey::from_slice(&bytes) {
Ok(secret_key) => {
let rotation_keypair = Keypair::from_secret_key(&secp, &secret_key);
Ok(encode_did_key(&rotation_keypair.public_key()))
}
Err(error) => {
tracing::error!("Error geting rotation secret key from bytes\n{error}");
Err(ApiError::RuntimeError)
}
},
Err(error) => {
tracing::error!("Unable to hex decode rotation key\n{error}");
Err(ApiError::RuntimeError)
}
}
}

fn get_public_signing_key() -> Result<String, ApiError> {
let secp = Secp256k1::new();
let private_signing_key = match env::var("PDS_REPO_SIGNING_KEY_K256_PRIVATE_KEY_HEX") {
Ok(res) => res,
Err(error) => {
tracing::error!("Error geting signing private key\n{error}");
return Err(ApiError::RuntimeError);
}
};
match hex::decode(private_signing_key.as_bytes()) {
Ok(bytes) => match SecretKey::from_slice(&bytes) {
Ok(secret_key) => {
let signing_keypair = Keypair::from_secret_key(&secp, &secret_key);
Ok(encode_did_key(&signing_keypair.public_key()))
}
Err(error) => {
tracing::error!("Error geting signing secret key from bytes\n{error}");
Err(ApiError::RuntimeError)
}
},
Err(error) => {
tracing::error!("Unable to hex decode signing key\n{error}");
Err(ApiError::RuntimeError)
}
}
}
1 change: 1 addition & 0 deletions rsky-pds/src/apis/com/atproto/identity/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod get_recommended_did_credentials;
pub mod resolve_handle;
pub mod sign_plc_operation;
pub mod update_handle;
1 change: 1 addition & 0 deletions rsky-pds/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ async fn rocket() -> _ {
com::atproto::admin::update_subject_status::update_subject_status,
com::atproto::identity::resolve_handle::resolve_handle,
com::atproto::identity::update_handle::update_handle,
com::atproto::identity::get_recommended_did_credentials::get_recommended_did_credentials,
com::atproto::identity::sign_plc_operation::sign_plc_operation,
com::atproto::repo::apply_writes::apply_writes,
com::atproto::repo::create_record::create_record,
Expand Down