mirror of
https://github.com/nab138/isideload.git
synced 2026-03-02 06:26:16 +01:00
Implement cert revoke and prompt modes
This commit is contained in:
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -1793,9 +1793,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rootcause"
|
name = "rootcause"
|
||||||
version = "0.11.1"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1a751633dcb95a6b1c954f0fa15c2afd9b4802640f8045432f68a1f4bde4b871"
|
checksum = "03621279b1bafd0cd806d4a4e301530bfab4a54a9a572ea45a4fe5072c3e134b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hashbrown 0.16.1",
|
"hashbrown 0.16.1",
|
||||||
"indexmap",
|
"indexmap",
|
||||||
@@ -1806,9 +1806,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rootcause-internals"
|
name = "rootcause-internals"
|
||||||
version = "0.11.1"
|
version = "0.12.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d9eeddca0d656f1a58ce3fc3f41b0b877a7e760460108712ad39b60181fdcb3e"
|
checksum = "2a6575ad7db4a6f026820de38c377b3e06fc59ceac225f868dfede39cd70e432"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"triomphe",
|
"triomphe",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -4,8 +4,11 @@ use idevice::usbmuxd::{UsbmuxdAddr, UsbmuxdConnection};
|
|||||||
use isideload::{
|
use isideload::{
|
||||||
anisette::remote_v3::RemoteV3AnisetteProvider,
|
anisette::remote_v3::RemoteV3AnisetteProvider,
|
||||||
auth::apple_account::AppleAccount,
|
auth::apple_account::AppleAccount,
|
||||||
dev::developer_session::DeveloperSession,
|
dev::{
|
||||||
sideload::{SideloaderBuilder, TeamSelection},
|
certificates::DevelopmentCertificate, developer_session::DeveloperSession,
|
||||||
|
teams::DeveloperTeam,
|
||||||
|
},
|
||||||
|
sideload::{SideloaderBuilder, TeamSelection, builder::MaxCertsBehavior},
|
||||||
};
|
};
|
||||||
|
|
||||||
use tracing::Level;
|
use tracing::Level;
|
||||||
@@ -70,8 +73,7 @@ async fn main() {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo");
|
.to_provider(UsbmuxdAddr::from_env_var().unwrap(), "isideload-demo");
|
||||||
|
|
||||||
let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string())
|
let team_selection_prompt = |teams: &Vec<DeveloperTeam>| {
|
||||||
.team_selection(TeamSelection::Prompt(|teams| {
|
|
||||||
println!("Please select a team:");
|
println!("Please select a team:");
|
||||||
for (index, team) in teams.iter().enumerate() {
|
for (index, team) in teams.iter().enumerate() {
|
||||||
println!(
|
println!(
|
||||||
@@ -88,7 +90,41 @@ async fn main() {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(teams[selection - 1].team_id.clone())
|
Some(teams[selection - 1].team_id.clone())
|
||||||
}))
|
};
|
||||||
|
|
||||||
|
let cert_selection_prompt = |certs: &Vec<DevelopmentCertificate>| {
|
||||||
|
println!("Maximum number of certificates reached. Please select certificates to revoke:");
|
||||||
|
for (index, cert) in certs.iter().enumerate() {
|
||||||
|
println!(
|
||||||
|
"({}) {}: {}",
|
||||||
|
index + 1,
|
||||||
|
cert.name.as_deref().unwrap_or("<Unnamed>"),
|
||||||
|
cert.machine_name.as_deref().unwrap_or("<No Machine Name>"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
println!("Enter the numbers of the certificates to revoke, separated by commas:");
|
||||||
|
let mut input = String::new();
|
||||||
|
std::io::stdin().read_line(&mut input).unwrap();
|
||||||
|
let selections: Vec<usize> = input
|
||||||
|
.trim()
|
||||||
|
.split(',')
|
||||||
|
.filter_map(|s| s.trim().parse::<usize>().ok())
|
||||||
|
.filter(|&n| n > 0 && n <= certs.len())
|
||||||
|
.collect();
|
||||||
|
if selections.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(
|
||||||
|
selections
|
||||||
|
.into_iter()
|
||||||
|
.map(|n| certs[n - 1].clone())
|
||||||
|
.collect::<Vec<_>>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut sideloader = SideloaderBuilder::new(dev_session, apple_id.to_string())
|
||||||
|
.team_selection(TeamSelection::Prompt(team_selection_prompt))
|
||||||
|
.max_certs_behavior(MaxCertsBehavior::Prompt(cert_selection_prompt))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let result = sideloader.install_app(&provider, app_path).await;
|
let result = sideloader.install_app(&provider, app_path).await;
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ rand = "0.10.0"
|
|||||||
uuid = {version = "1.20.0", features = ["v4"] }
|
uuid = {version = "1.20.0", features = ["v4"] }
|
||||||
tracing = "0.1.44"
|
tracing = "0.1.44"
|
||||||
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] }
|
tokio-tungstenite = { version = "0.28.0", features = ["rustls-tls-webpki-roots"] }
|
||||||
rootcause = "0.11.1"
|
rootcause = "0.12.0"
|
||||||
futures-util = "0.3.31"
|
futures-util = "0.3.31"
|
||||||
serde_json = "1.0.149"
|
serde_json = "1.0.149"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
|
|||||||
@@ -63,7 +63,6 @@ impl AppleAccount {
|
|||||||
anisette_generator: AnisetteDataGenerator,
|
anisette_generator: AnisetteDataGenerator,
|
||||||
debug: bool,
|
debug: bool,
|
||||||
) -> Result<Self, Report> {
|
) -> Result<Self, Report> {
|
||||||
info!("Initializing apple account");
|
|
||||||
if debug {
|
if debug {
|
||||||
warn!("Debug mode enabled: this is a security risk!");
|
warn!("Debug mode enabled: this is a security risk!");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
dev::{developer_session::DeveloperSession, teams::DeveloperTeam},
|
dev::{
|
||||||
|
certificates::DevelopmentCertificate, developer_session::DeveloperSession,
|
||||||
|
teams::DeveloperTeam,
|
||||||
|
},
|
||||||
sideload::sideloader::Sideloader,
|
sideload::sideloader::Sideloader,
|
||||||
util::storage::SideloadingStorage,
|
util::storage::SideloadingStorage,
|
||||||
};
|
};
|
||||||
@@ -27,10 +30,12 @@ impl Display for TeamSelection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub enum MaxCertsBehavior {
|
pub enum MaxCertsBehavior {
|
||||||
/// If the maximum number of certificates is reached, delete all existing certificates and create a new one
|
/// If the maximum number of certificates is reached, revoke certs until it is possible to create a new certificate
|
||||||
Revoke,
|
Revoke,
|
||||||
/// If the maximum number of certificates is reached, return an error instead of creating a new certificate
|
/// If the maximum number of certificates is reached, return an error instead of creating a new certificate
|
||||||
Error,
|
Error,
|
||||||
|
/// If the maximum number of certificates is reached, prompt the user to select which certificates to revoke until it is possible to create a new certificate
|
||||||
|
Prompt(fn(&Vec<DevelopmentCertificate>) -> Option<Vec<DevelopmentCertificate>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct SideloaderBuilder {
|
pub struct SideloaderBuilder {
|
||||||
@@ -69,12 +74,17 @@ impl SideloaderBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn max_certs_behavior(mut self, behavior: MaxCertsBehavior) -> Self {
|
||||||
|
self.max_certs_behavior = Some(behavior);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Sideloader {
|
pub fn build(self) -> Sideloader {
|
||||||
Sideloader::new(
|
Sideloader::new(
|
||||||
self.developer_session,
|
self.developer_session,
|
||||||
self.apple_email,
|
self.apple_email,
|
||||||
self.team_selection.unwrap_or(TeamSelection::First),
|
self.team_selection.unwrap_or(TeamSelection::First),
|
||||||
self.max_certs_behavior.unwrap_or(MaxCertsBehavior::Revoke),
|
self.max_certs_behavior.unwrap_or(MaxCertsBehavior::Error),
|
||||||
self.machine_name.unwrap_or_else(|| "isideload".to_string()),
|
self.machine_name.unwrap_or_else(|| "isideload".to_string()),
|
||||||
self.storage
|
self.storage
|
||||||
.unwrap_or_else(|| Box::new(crate::util::storage::new_storage())),
|
.unwrap_or_else(|| Box::new(crate::util::storage::new_storage())),
|
||||||
|
|||||||
@@ -11,8 +11,11 @@ use tracing::{error, info};
|
|||||||
use x509_certificate::X509Certificate;
|
use x509_certificate::X509Certificate;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
SideloadError,
|
||||||
dev::{
|
dev::{
|
||||||
certificates::CertificatesApi, developer_session::DeveloperSession, teams::DeveloperTeam,
|
certificates::{CertificatesApi, DevelopmentCertificate},
|
||||||
|
developer_session::DeveloperSession,
|
||||||
|
teams::DeveloperTeam,
|
||||||
},
|
},
|
||||||
sideload::builder::MaxCertsBehavior,
|
sideload::builder::MaxCertsBehavior,
|
||||||
util::storage::SideloadingStorage,
|
util::storage::SideloadingStorage,
|
||||||
@@ -22,6 +25,7 @@ pub struct CertificateIdentity {
|
|||||||
pub machine_id: String,
|
pub machine_id: String,
|
||||||
pub machine_name: String,
|
pub machine_name: String,
|
||||||
pub certificate: X509Certificate,
|
pub certificate: X509Certificate,
|
||||||
|
pub private_key: RsaPrivateKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CertificateIdentity {
|
impl CertificateIdentity {
|
||||||
@@ -36,23 +40,37 @@ impl CertificateIdentity {
|
|||||||
let pr = Self::retrieve_private_key(apple_email, storage).await?;
|
let pr = Self::retrieve_private_key(apple_email, storage).await?;
|
||||||
|
|
||||||
let found = Self::find_matching(&pr, machine_name, developer_session, team).await;
|
let found = Self::find_matching(&pr, machine_name, developer_session, team).await;
|
||||||
if let Ok(Some(cert)) = found {
|
if let Ok(Some((cert, x509_cert))) = found {
|
||||||
info!("Found matching certificate");
|
info!("Found matching certificate");
|
||||||
return Ok(cert);
|
return Ok(Self {
|
||||||
|
machine_id: cert.machine_id.clone().unwrap_or_default(),
|
||||||
|
machine_name: cert.machine_name.clone().unwrap_or_default(),
|
||||||
|
certificate: x509_cert,
|
||||||
|
private_key: pr,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(e) = found {
|
if let Err(e) = found {
|
||||||
error!("Failed to check for matching certificate: {:?}", e);
|
error!("Failed to check for matching certificate: {:?}", e);
|
||||||
}
|
}
|
||||||
info!("Requesting new certificate");
|
info!("Requesting new certificate");
|
||||||
Self::request_certificate(
|
let (cert, x509_cert) = Self::request_certificate(
|
||||||
&pr,
|
&pr,
|
||||||
machine_name.to_string(),
|
machine_name.to_string(),
|
||||||
developer_session,
|
developer_session,
|
||||||
team,
|
team,
|
||||||
max_certs_behavior,
|
max_certs_behavior,
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
|
|
||||||
|
info!("Successfully obtained certificate");
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
machine_id: cert.machine_id.clone().unwrap_or_default(),
|
||||||
|
machine_name: cert.machine_name.clone().unwrap_or_default(),
|
||||||
|
certificate: x509_cert,
|
||||||
|
private_key: pr,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn retrieve_private_key(
|
async fn retrieve_private_key(
|
||||||
@@ -63,16 +81,16 @@ impl CertificateIdentity {
|
|||||||
hasher.update(apple_email.as_bytes());
|
hasher.update(apple_email.as_bytes());
|
||||||
let email_hash = hex::encode(hasher.finalize());
|
let email_hash = hex::encode(hasher.finalize());
|
||||||
|
|
||||||
let private_key = storage.retrieve(&format!("{}/key", email_hash))?;
|
let private_key = storage.retrieve_data(&format!("{}/key", email_hash))?;
|
||||||
if private_key.is_some() {
|
if private_key.is_some() {
|
||||||
return Ok(RsaPrivateKey::from_pkcs8_pem(&private_key.unwrap())?);
|
return Ok(RsaPrivateKey::from_pkcs8_der(&private_key.unwrap())?);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
let private_key = RsaPrivateKey::new(&mut rng, 2048)?;
|
let private_key = RsaPrivateKey::new(&mut rng, 2048)?;
|
||||||
storage.store(
|
storage.store_data(
|
||||||
&format!("{}/key", email_hash),
|
&format!("{}/key", email_hash),
|
||||||
&private_key.to_pkcs8_pem(Default::default())?.to_string(),
|
&private_key.to_pkcs8_der()?.as_bytes(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(private_key)
|
Ok(private_key)
|
||||||
@@ -83,7 +101,7 @@ impl CertificateIdentity {
|
|||||||
machine_name: &str,
|
machine_name: &str,
|
||||||
developer_session: &mut DeveloperSession,
|
developer_session: &mut DeveloperSession,
|
||||||
team: &DeveloperTeam,
|
team: &DeveloperTeam,
|
||||||
) -> Result<Option<Self>, Report> {
|
) -> Result<Option<(DevelopmentCertificate, X509Certificate)>, Report> {
|
||||||
let public_key_der = private_key
|
let public_key_der = private_key
|
||||||
.to_public_key()
|
.to_public_key()
|
||||||
.to_pkcs1_der()?
|
.to_pkcs1_der()?
|
||||||
@@ -103,11 +121,7 @@ impl CertificateIdentity {
|
|||||||
X509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?;
|
X509Certificate::from_der(cert.cert_content.as_ref().unwrap().as_ref())?;
|
||||||
|
|
||||||
if public_key_der == x509_cert.public_key_data().as_ref() {
|
if public_key_der == x509_cert.public_key_data().as_ref() {
|
||||||
return Ok(Some(Self {
|
return Ok(Some((cert.clone(), x509_cert)));
|
||||||
machine_id: cert.machine_id.clone().unwrap_or_default(),
|
|
||||||
machine_name: cert.machine_name.clone().unwrap_or_default(),
|
|
||||||
certificate: x509_cert,
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,21 +134,29 @@ impl CertificateIdentity {
|
|||||||
developer_session: &mut DeveloperSession,
|
developer_session: &mut DeveloperSession,
|
||||||
team: &DeveloperTeam,
|
team: &DeveloperTeam,
|
||||||
max_certs_behavior: &MaxCertsBehavior,
|
max_certs_behavior: &MaxCertsBehavior,
|
||||||
) -> Result<Self, Report> {
|
) -> Result<(DevelopmentCertificate, X509Certificate), Report> {
|
||||||
let csr = Self::build_csr(private_key).context("Failed to generate CSR")?;
|
let csr = Self::build_csr(private_key).context("Failed to generate CSR")?;
|
||||||
|
|
||||||
let request = developer_session
|
let mut i = 0;
|
||||||
.submit_development_csr(team, csr, machine_name, None)
|
let mut existing_certs: Option<Vec<DevelopmentCertificate>> = None;
|
||||||
.await?;
|
|
||||||
|
|
||||||
// TODO: Handle max certs behavior properly instead of just always revoking
|
while i < 4 {
|
||||||
|
i += 1;
|
||||||
|
|
||||||
|
let result = developer_session
|
||||||
|
.submit_development_csr(team, csr.clone(), machine_name.clone(), None)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
match result {
|
||||||
|
Ok(request) => {
|
||||||
let apple_certs = developer_session.list_ios_certs(team).await?;
|
let apple_certs = developer_session.list_ios_certs(team).await?;
|
||||||
|
|
||||||
let apple_cert = apple_certs
|
let apple_cert = apple_certs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|c| c.certificate_id == Some(request.cert_request_id.clone()))
|
.find(|c| c.certificate_id == Some(request.cert_request_id.clone()))
|
||||||
.ok_or_else(|| report!("Failed to find certificate after submitting CSR"))?;
|
.ok_or_else(|| {
|
||||||
|
report!("Failed to find certificate after submitting CSR")
|
||||||
|
})?;
|
||||||
|
|
||||||
let x509_cert = X509Certificate::from_der(
|
let x509_cert = X509Certificate::from_der(
|
||||||
apple_cert
|
apple_cert
|
||||||
@@ -144,11 +166,45 @@ impl CertificateIdentity {
|
|||||||
.as_ref(),
|
.as_ref(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
return Ok((apple_cert.clone(), x509_cert));
|
||||||
machine_id: apple_cert.machine_id.clone().unwrap_or_default(),
|
}
|
||||||
machine_name: apple_cert.machine_name.clone().unwrap_or_default(),
|
Err(e) => {
|
||||||
certificate: x509_cert,
|
let error = e
|
||||||
})
|
.iter_reports()
|
||||||
|
.find_map(|node| node.downcast_current_context::<SideloadError>());
|
||||||
|
if let Some(SideloadError::DeveloperError(code, _)) = error {
|
||||||
|
if *code == 7460 {
|
||||||
|
if existing_certs.is_none() {
|
||||||
|
existing_certs = Some(
|
||||||
|
developer_session
|
||||||
|
.list_ios_certs(team)
|
||||||
|
.await?
|
||||||
|
.iter()
|
||||||
|
.filter(|c| c.serial_number.is_some())
|
||||||
|
.cloned()
|
||||||
|
.collect(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Self::revoke_others(
|
||||||
|
developer_session,
|
||||||
|
team,
|
||||||
|
max_certs_behavior,
|
||||||
|
SideloadError::DeveloperError(
|
||||||
|
*code,
|
||||||
|
"Maximum number of certificates reached".to_string(),
|
||||||
|
),
|
||||||
|
&mut existing_certs.as_mut().unwrap(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
} else {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(report!("Reached max attempts to request certificate"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_csr(private_key: &RsaPrivateKey) -> Result<String, Report> {
|
fn build_csr(private_key: &RsaPrivateKey) -> Result<String, Report> {
|
||||||
@@ -169,4 +225,51 @@ impl CertificateIdentity {
|
|||||||
|
|
||||||
Ok(params.serialize_request(&subject_key)?.pem()?)
|
Ok(params.serialize_request(&subject_key)?.pem()?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn revoke_others(
|
||||||
|
developer_session: &mut DeveloperSession,
|
||||||
|
team: &DeveloperTeam,
|
||||||
|
max_certs_behavior: &MaxCertsBehavior,
|
||||||
|
error: SideloadError,
|
||||||
|
existing_certs: &mut Vec<DevelopmentCertificate>,
|
||||||
|
) -> Result<(), Report> {
|
||||||
|
match max_certs_behavior {
|
||||||
|
MaxCertsBehavior::Revoke => {
|
||||||
|
if let Some(cert) = existing_certs.pop() {
|
||||||
|
info!(
|
||||||
|
"Revoking certificate with name: {:?} ({:?})",
|
||||||
|
cert.name, cert.machine_name
|
||||||
|
);
|
||||||
|
developer_session
|
||||||
|
.revoke_development_cert(team, &cert.serial_number.unwrap(), None)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
error!("No more certificates to revoke but still hitting max certs error");
|
||||||
|
return Err(error.into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
MaxCertsBehavior::Error => Err(error.into()),
|
||||||
|
MaxCertsBehavior::Prompt(prompt_fn) => {
|
||||||
|
let certs_to_revoke = prompt_fn(existing_certs);
|
||||||
|
if certs_to_revoke.is_none() {
|
||||||
|
error!("User did not select any certificates to revoke");
|
||||||
|
return Err(error.into());
|
||||||
|
}
|
||||||
|
for cert in certs_to_revoke.unwrap() {
|
||||||
|
info!(
|
||||||
|
"Revoking certificate with name: {}",
|
||||||
|
cert.machine_name
|
||||||
|
.unwrap_or(cert.machine_id.unwrap_or_default())
|
||||||
|
);
|
||||||
|
let serial_number = cert.serial_number.clone();
|
||||||
|
developer_session
|
||||||
|
.revoke_development_cert(team, &cert.serial_number.unwrap(), None)
|
||||||
|
.await?;
|
||||||
|
existing_certs.retain(|c| c.serial_number != serial_number);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,32 @@ use crate::util::storage::SideloadingStorage;
|
|||||||
use keyring::Entry;
|
use keyring::Entry;
|
||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
|
|
||||||
pub struct KeyringStorage {}
|
pub struct KeyringStorage {
|
||||||
|
pub service_name: String,
|
||||||
|
}
|
||||||
|
|
||||||
impl KeyringStorage {
|
impl KeyringStorage {
|
||||||
pub fn new() -> Self {
|
pub fn new(service_name: String) -> Self {
|
||||||
KeyringStorage {}
|
KeyringStorage { service_name }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for KeyringStorage {
|
||||||
|
fn default() -> Self {
|
||||||
|
KeyringStorage {
|
||||||
|
service_name: "isideload".to_string(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SideloadingStorage for KeyringStorage {
|
impl SideloadingStorage for KeyringStorage {
|
||||||
fn store(&self, key: &str, value: &str) -> Result<(), Report> {
|
fn store(&self, key: &str, value: &str) -> Result<(), Report> {
|
||||||
Entry::new("isideload", key)?.set_password(value)?;
|
Entry::new(&self.service_name, key)?.set_password(value)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn retrieve(&self, key: &str) -> Result<Option<String>, Report> {
|
fn retrieve(&self, key: &str) -> Result<Option<String>, Report> {
|
||||||
let entry = Entry::new("isideload", key)?;
|
let entry = Entry::new(&self.service_name, key)?;
|
||||||
match entry.get_password() {
|
match entry.get_password() {
|
||||||
Ok(password) => Ok(Some(password)),
|
Ok(password) => Ok(Some(password)),
|
||||||
Err(keyring::Error::NoEntry) => Ok(None),
|
Err(keyring::Error::NoEntry) => Ok(None),
|
||||||
@@ -26,11 +36,25 @@ impl SideloadingStorage for KeyringStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn delete(&self, key: &str) -> Result<(), Report> {
|
fn delete(&self, key: &str) -> Result<(), Report> {
|
||||||
let entry = Entry::new("isideload", key)?;
|
let entry = Entry::new(&self.service_name, key)?;
|
||||||
match entry.delete_credential() {
|
match entry.delete_credential() {
|
||||||
Ok(()) => Ok(()),
|
Ok(()) => Ok(()),
|
||||||
Err(keyring::Error::NoEntry) => Ok(()),
|
Err(keyring::Error::NoEntry) => Ok(()),
|
||||||
Err(e) => Err(e.into()),
|
Err(e) => Err(e.into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> {
|
||||||
|
Entry::new(&self.service_name, key)?.set_secret(value)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_data(&self, key: &str) -> Result<Option<Vec<u8>>, Report> {
|
||||||
|
let entry = Entry::new(&self.service_name, key)?;
|
||||||
|
match entry.get_secret() {
|
||||||
|
Ok(secret) => Ok(Some(secret)),
|
||||||
|
Err(keyring::Error::NoEntry) => Ok(None),
|
||||||
|
Err(e) => Err(e.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,24 @@
|
|||||||
use std::{collections::HashMap, sync::Mutex};
|
use std::{collections::HashMap, sync::Mutex};
|
||||||
|
|
||||||
|
use base64::prelude::*;
|
||||||
use rootcause::prelude::*;
|
use rootcause::prelude::*;
|
||||||
|
|
||||||
pub trait SideloadingStorage: Send + Sync {
|
pub trait SideloadingStorage: Send + Sync {
|
||||||
fn store(&self, key: &str, value: &str) -> Result<(), Report>;
|
fn store(&self, key: &str, value: &str) -> Result<(), Report>;
|
||||||
fn retrieve(&self, key: &str) -> Result<Option<String>, Report>;
|
fn retrieve(&self, key: &str) -> Result<Option<String>, Report>;
|
||||||
|
|
||||||
|
fn store_data(&self, key: &str, value: &[u8]) -> Result<(), Report> {
|
||||||
|
self.store(key, &BASE64_STANDARD.encode(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn retrieve_data(&self, key: &str) -> Result<Option<Vec<u8>>, Report> {
|
||||||
|
if let Some(value) = self.retrieve(key)? {
|
||||||
|
Ok(Some(BASE64_STANDARD.decode(value)?))
|
||||||
|
} else {
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn delete(&self, key: &str) -> Result<(), Report> {
|
fn delete(&self, key: &str) -> Result<(), Report> {
|
||||||
self.store(key, "")
|
self.store(key, "")
|
||||||
}
|
}
|
||||||
@@ -13,7 +27,7 @@ pub trait SideloadingStorage: Send + Sync {
|
|||||||
pub fn new_storage() -> impl SideloadingStorage {
|
pub fn new_storage() -> impl SideloadingStorage {
|
||||||
#[cfg(feature = "keyring-storage")]
|
#[cfg(feature = "keyring-storage")]
|
||||||
{
|
{
|
||||||
crate::util::keyring_storage::KeyringStorage::new()
|
crate::util::keyring_storage::KeyringStorage::default()
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "keyring-storage"))]
|
#[cfg(not(feature = "keyring-storage"))]
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user