use crate::*;
use aes_gcm::{aead::Aead, Aes256Gcm, Key, KeyInit, Nonce};
use rand::rngs::OsRng;
use rsa::{pkcs8::ToPublicKey, PaddingScheme, RsaPrivateKey, RsaPublicKey};
use serde::Deserialize;
use serde_json;
use serde_json::json;
use std::collections::HashMap;
use std::result::Result;
#[derive(Debug, Deserialize)]
pub struct Secrets {
pub keys: HashMap<String, String>,
}
#[derive(Debug, Clone, Eq, PartialEq, Deserialize)]
struct EncryptedSecretsData {
key: String,
nonce: String,
data: String,
}
fn handle_reqwest_err(e: reqwest::Error) -> OnDemandError {
let status = e.status().unwrap_or(reqwest::StatusCode::default());
OnDemandError::CustomError {
message: format!(
"reqwest_error: code = {}, message = {}",
status,
status.canonical_reason().unwrap_or("Unknown")
),
source: std::sync::Arc::new(e),
}
}
pub async fn fetch_secrets(fn_authority: &str, url: Option<&str>) -> Result<Secrets, OnDemandError> {
let secrets_server_url = match url {
Some(value) => value,
None => "https://api.secrets.switchboard.xyz/",
};
let mut os_rng = OsRng::default();
let priv_key = RsaPrivateKey::new(&mut os_rng, 2048).map_err(|_| OnDemandError::KeyParseError)?;
let pub_key = RsaPublicKey::from(&priv_key)
.to_public_key_der()
.map_err(|_| OnDemandError::KeyParseError)?;
let secrets_quote = Gramine::generate_quote(pub_key.as_ref()).map_err(|_| OnDemandError::SgxError)?;
let payload = json!({
"user_pubkey": fn_authority,
"ciphersuite": "ed25519",
"encryption_key": pub_key.to_pem().as_str(),
"quote": &secrets_quote,
});
let response = reqwest::Client::new()
.post(secrets_server_url)
.json(&payload)
.send()
.await
.map_err(handle_reqwest_err)?
.error_for_status()
.map_err(handle_reqwest_err)?;
let encrypted_data = response
.json::<EncryptedSecretsData>()
.await
.map_err(handle_reqwest_err)?;
let key = match base64::decode(encrypted_data.key) {
Ok(value) => value,
Err(err) => {
let error_msg = format!("Base64DecodeError: {:#?}", err);
return Err(OnDemandError::CustomMessage(error_msg));
}
};
let key = match priv_key.decrypt(PaddingScheme::PKCS1v15Encrypt, &key) {
Ok(value) => Key::<Aes256Gcm>::clone_from_slice(&value),
Err(err) => {
let error_msg = format!("DecryptKeyError: {:#?}", err);
return Err(OnDemandError::CustomMessage(error_msg));
}
};
let nonce = match base64::decode(encrypted_data.nonce) {
Ok(value) => Nonce::clone_from_slice(&value),
Err(err) => {
let error_msg = format!("Base64DecodeError: {:#?}", err);
return Err(OnDemandError::CustomMessage(error_msg));
}
};
let data = match base64::decode(encrypted_data.data) {
Ok(value) => value,
Err(err) => {
let error_msg = format!("Base64DecodeError: {:#?}", err);
return Err(OnDemandError::CustomMessage(error_msg));
}
};
let data = match Aes256Gcm::new(&key).decrypt(&nonce, data.as_ref()) {
Ok(value) => value,
Err(err) => {
let error_msg = format!("Aes256GcmError: {:#?}", err);
return Err(OnDemandError::CustomMessage(error_msg));
}
};
let keys: HashMap<String, String> = serde_json::from_slice(&data)?;
Ok(Secrets { keys })
}