Implement Rust-owned Android local and HTTPS onboarding (#95)
This commit is contained in:
1 parent
66b1d4a4c4
commit
36e5f55283
46 files changed
+3002
-79
No files matched your search
@@ -266,6 +266,31 @@ impl KeyStore {
|
||||
Ok(store)
|
||||
}
|
||||
|
||||
/// Merge prepared key material while retaining existing private keys.
|
||||
pub(crate) fn extend_from_path(&mut self, path: &Path) -> Result<(), CryptoError> {
|
||||
let loaded = match Self::load(path) {
|
||||
Ok(loaded) => loaded,
|
||||
Err(CryptoError::NoKeyMaterial) => return Ok(()),
|
||||
Err(error) => return Err(error),
|
||||
};
|
||||
if self.keys.len() + loaded.keys.len() > MAX_IMPORTED_KEYS {
|
||||
return Err(CryptoError::TooManyKeys);
|
||||
}
|
||||
for (fingerprint, material) in loaded.keys {
|
||||
if let Some(existing) = self.keys.get_mut(&fingerprint) {
|
||||
if existing.public.primary_key != material.public.primary_key {
|
||||
return Err(CryptoError::DuplicateFingerprint { fingerprint });
|
||||
}
|
||||
if existing.secret.is_none() {
|
||||
existing.secret = material.secret;
|
||||
}
|
||||
} else {
|
||||
self.keys.insert(fingerprint, material);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Transactionally import one armored or binary stream containing public and/or secret keys.
|
||||
pub fn import(&mut self, bytes: &[u8]) -> Result<Vec<KeyInfo>, CryptoError> {
|
||||
if bytes.len() as u64 > MAX_KEY_FILE_BYTES {
|
||||
|
||||
@@ -1466,6 +1466,50 @@ impl GitRepository {
|
||||
credentials: &impl GitRemoteCredentialProvider,
|
||||
transport: &impl GitFetchTransport,
|
||||
control: &GitOperationControl,
|
||||
) -> Result<Vec<String>, GitError> {
|
||||
let branches = Self::probe_remote_with_transport(
|
||||
parent,
|
||||
identity,
|
||||
configured,
|
||||
credentials,
|
||||
transport,
|
||||
control,
|
||||
)?;
|
||||
if branches.is_empty() {
|
||||
return Err(GitError::InvalidRepository(
|
||||
"the remote has no branches".to_owned(),
|
||||
));
|
||||
}
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
/// Validate HTTPS authentication before saving onboarding state, including an empty remote.
|
||||
pub(crate) fn validate_remote_with_transport(
|
||||
parent: &Path,
|
||||
identity: GitIdentity,
|
||||
configured: &GitRemote,
|
||||
credentials: &impl GitRemoteCredentialProvider,
|
||||
transport: &impl GitFetchTransport,
|
||||
control: &GitOperationControl,
|
||||
) -> Result<(), GitError> {
|
||||
Self::probe_remote_with_transport(
|
||||
parent,
|
||||
identity,
|
||||
configured,
|
||||
credentials,
|
||||
transport,
|
||||
control,
|
||||
)
|
||||
.map(|_| ())
|
||||
}
|
||||
|
||||
fn probe_remote_with_transport(
|
||||
parent: &Path,
|
||||
identity: GitIdentity,
|
||||
configured: &GitRemote,
|
||||
credentials: &impl GitRemoteCredentialProvider,
|
||||
transport: &impl GitFetchTransport,
|
||||
control: &GitOperationControl,
|
||||
) -> Result<Vec<String>, GitError> {
|
||||
control.report(GitProgressPhase::Validating)?;
|
||||
ensure_remote_transport_available(configured.endpoint())?;
|
||||
@@ -1648,11 +1692,6 @@ impl GitRepository {
|
||||
.collect::<Result<Vec<_>, GitError>>()?;
|
||||
branches.sort();
|
||||
branches.dedup();
|
||||
if branches.is_empty() {
|
||||
return Err(GitError::InvalidRepository(
|
||||
"the remote has no branches".to_owned(),
|
||||
));
|
||||
}
|
||||
Ok(branches)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
//! Storage-owned first-run setup for the native mobile applications.
|
||||
|
||||
pub mod android;
|
||||
|
||||
use std::{
|
||||
error::Error,
|
||||
fmt, fs,
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -532,7 +532,7 @@ fn regular_bytes(
|
||||
}
|
||||
Ok(Some(bytes))
|
||||
}
|
||||
fn exclusive_lock(parent: &Dir, name: &str) -> Result<fs::File, PrivateStorageError> {
|
||||
pub(crate) fn exclusive_lock(parent: &Dir, name: &str) -> Result<fs::File, PrivateStorageError> {
|
||||
if let Ok(metadata) = parent.symlink_metadata(name)
|
||||
&& (metadata.file_type().is_symlink() || !metadata.is_file())
|
||||
{
|
||||
|
||||
@@ -1010,6 +1010,32 @@ struct SecretRecord {
|
||||
fn encode_record(
|
||||
reference: &SecretReference,
|
||||
value: &SecretBytes,
|
||||
) -> Result<SecretBytes, SecretStoreError> {
|
||||
encode_record_with_owner(reference, value, None)
|
||||
}
|
||||
|
||||
/// A unique protected owner distinguishes newly created credentials during crash rollback.
|
||||
pub(crate) fn encode_onboarding_record(
|
||||
reference: &SecretReference,
|
||||
value: &SecretBytes,
|
||||
owner: &[u8],
|
||||
) -> Result<SecretBytes, SecretStoreError> {
|
||||
if owner.is_empty()
|
||||
|| owner.len() > 64
|
||||
|| !matches!(
|
||||
reference.kind,
|
||||
SecretReferenceKind::HttpsGitCredential { .. }
|
||||
)
|
||||
{
|
||||
return Err(SecretStoreError::InvalidReference);
|
||||
}
|
||||
encode_record_with_owner(reference, value, Some(owner))
|
||||
}
|
||||
|
||||
fn encode_record_with_owner(
|
||||
reference: &SecretReference,
|
||||
value: &SecretBytes,
|
||||
owner: Option<&[u8]>,
|
||||
) -> Result<SecretBytes, SecretStoreError> {
|
||||
validate_secret(value)?;
|
||||
let mut encoded = Vec::with_capacity(RECORD_MAGIC.len() + value.expose().len() + 1024);
|
||||
@@ -1025,7 +1051,10 @@ fn encode_record(
|
||||
application_id,
|
||||
account,
|
||||
} => {
|
||||
encoded.push(2);
|
||||
encoded.push(if owner.is_some() { 4 } else { 2 });
|
||||
if let Some(owner) = owner {
|
||||
write_field(&mut encoded, owner)?;
|
||||
}
|
||||
write_field(&mut encoded, server_id.as_bytes())?;
|
||||
write_field(&mut encoded, application_id.as_bytes())?;
|
||||
write_field(&mut encoded, account.as_bytes())?;
|
||||
@@ -1063,7 +1092,14 @@ fn decode_record(encoded: SecretBytes) -> Result<SecretRecord, SecretStoreError>
|
||||
SecretReference::openpgp_passphrase(read_text(fingerprint)?)
|
||||
.map_err(|_| SecretStoreError::Corrupted)?
|
||||
}
|
||||
2 => {
|
||||
2 | 4 => {
|
||||
if kind == 4 {
|
||||
let (owner, rest) = read_field(remainder)?;
|
||||
if owner.is_empty() || owner.len() > 64 {
|
||||
return Err(SecretStoreError::Corrupted);
|
||||
}
|
||||
remainder = rest;
|
||||
}
|
||||
let (server_id, rest) = read_field(remainder)?;
|
||||
let (application_id, rest) = read_field(rest)?;
|
||||
let (account, rest) = read_field(rest)?;
|
||||
|
||||
Reference in new issue
Block a user