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;
use base64::{engine::general_purpose::STANDARD as base64, Engine as _};
use std::sync::Arc;
use sha2::Sha256;
use sha2::Digest;
#[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) -> SbError {
let status = e.status().unwrap_or(reqwest::StatusCode::default());
SbError::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(
cluster: &str,
fn_authority: &str,
url: Option<&str>,
feed: &str,
oracle: Pubkey,
secrets_signer: Arc<libsecp256k1::SecretKey>,
) -> Result<Secrets, SbError> {
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(|_| SbError::KeyParseError)?;
let pub_key = RsaPublicKey::from(&priv_key)
.to_public_key_der()
.map_err(|_| SbError::KeyParseError)?;
let encryption_key = pub_key.to_pem().as_str().to_string();
let encrypt_key_hash = &Sha256::digest(encryption_key.clone())[..];
let msg = libsecp256k1::Message::parse(encrypt_key_hash.try_into().unwrap());
let (sig, recovery_id) = libsecp256k1::sign(&msg, &secrets_signer);
let payload = json!({
"user_pubkey": fn_authority,
"cluster": cluster,
"ciphersuite": "ed25519",
"encryption_key": encryption_key,
"feed_id": feed.to_string(),
"oracle": oracle.to_string(),
"key_signature": base64.encode(sig.serialize()),
"recovery_id": recovery_id.serialize(),
});
println!("url: {:#?}", secrets_server_url);
println!("Payload: {:#?}", payload);
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)?;
println!("Response: {:#?}", response);
let encrypted_data = response
.json::<EncryptedSecretsData>()
.await
.map_err(handle_reqwest_err)?;
println!("Encrypted Data: {:#?}", encrypted_data);
let key = match base64.decode(encrypted_data.key) {
Ok(value) => value,
Err(err) => {
let error_msg = format!("Base64DecodeError: {:#?}", err);
return Err(SbError::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(SbError::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(SbError::CustomMessage(error_msg));
}
};
let data = match base64.decode(encrypted_data.data) {
Ok(value) => value,
Err(err) => {
let error_msg = format!("Base64DecodeError: {:#?}", err);
return Err(SbError::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(SbError::CustomMessage(error_msg));
}
};
let keys: HashMap<String, String> = serde_json::from_slice(&data)?;
Ok(Secrets { keys })
}