mirror of
https://github.com/block/buzz.git
synced 2026-11-09 10:51:42 +00:00
refactor(push): require validated App Attest bypass token
Co-authored-by: Tom Brow <tomb@block.xyz> Signed-off-by: Tom Brow <tomb@block.xyz>
This commit is contained in:
committed by
npub12wpjffj7q5qjsky5jvk4ldwlxmse5xll3d8gytk4wqd0c5y7jvwspg37n6
parent
5d236db269
commit
421912de36
@@ -45,6 +45,14 @@ impl AppAttestVerifier {
|
||||
apple_root_cert_pem,
|
||||
})
|
||||
}
|
||||
#[cfg(all(test, feature = "dev-app-attest-bypass"))]
|
||||
pub(crate) fn for_policy_test() -> Self {
|
||||
Self {
|
||||
app_id: "policy-test".to_owned(),
|
||||
apple_root_cert_pem: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
/// `client_data` is the exact canonical enrollment transcript represented by
|
||||
/// the challenge string passed to `attestKey`; callers must include every
|
||||
/// authority-bearing enrollment field in it.
|
||||
|
||||
@@ -3,6 +3,23 @@
|
||||
use crate::app_attest::{
|
||||
AppAttestError, AppAttestVerifier, VerifiedAssertion, VerifiedAttestation,
|
||||
};
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
#[derive(Clone)]
|
||||
pub struct DevelopmentAppAttestPolicy {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
pub struct DevelopmentAppAttestBypass {
|
||||
_private: (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
impl DevelopmentAppAttestBypass {
|
||||
pub(crate) fn enabled() -> Self {
|
||||
Self { _private: () }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(
|
||||
not(feature = "dev-app-attest-bypass"),
|
||||
@@ -20,7 +37,7 @@ let _ = AppAttestPolicy::Development;
|
||||
pub enum AppAttestPolicy {
|
||||
Apple(AppAttestVerifier),
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
Development,
|
||||
Development(DevelopmentAppAttestPolicy),
|
||||
}
|
||||
|
||||
impl AppAttestPolicy {
|
||||
@@ -29,8 +46,23 @@ impl AppAttestPolicy {
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
pub fn development() -> Self {
|
||||
Self::Development
|
||||
pub fn from_config(
|
||||
bypass: Option<DevelopmentAppAttestBypass>,
|
||||
apple: AppAttestVerifier,
|
||||
) -> Self {
|
||||
if bypass.is_some() {
|
||||
tracing::warn!(
|
||||
"DEVELOPMENT APP ATTEST BYPASS ACTIVE; Apple attestation and assertion verification are disabled"
|
||||
);
|
||||
Self::development()
|
||||
} else {
|
||||
Self::apple(apple)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
fn development() -> Self {
|
||||
Self::Development(DevelopmentAppAttestPolicy { _private: () })
|
||||
}
|
||||
|
||||
pub fn verify_attestation(
|
||||
@@ -44,7 +76,7 @@ impl AppAttestPolicy {
|
||||
verifier.verify_attestation(attestation_b64, key_id_b64, client_data)
|
||||
}
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
Self::Development => {
|
||||
Self::Development(_) => {
|
||||
crate::dev_app_attest::verify_attestation(attestation_b64, key_id_b64, client_data)
|
||||
}
|
||||
}
|
||||
@@ -69,7 +101,7 @@ impl AppAttestPolicy {
|
||||
stored_challenge,
|
||||
),
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
Self::Development => crate::dev_app_attest::verify_assertion(
|
||||
Self::Development(_) => crate::dev_app_attest::verify_assertion(
|
||||
assertion_b64,
|
||||
client_data,
|
||||
public_key,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
use crate::app_attest_policy::DevelopmentAppAttestBypass;
|
||||
use base64::{engine::general_purpose::STANDARD, Engine as _};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
@@ -76,6 +78,12 @@ fn parse_keyring(
|
||||
Ok(keys)
|
||||
}
|
||||
impl Config {
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
pub fn dev_app_attest_bypass(&self) -> Option<DevelopmentAppAttestBypass> {
|
||||
self.dev_app_attest_bypass
|
||||
.then(DevelopmentAppAttestBypass::enabled)
|
||||
}
|
||||
|
||||
pub fn from_env() -> Result<Self, ConfigError> {
|
||||
Self::from_map(&std::env::vars().collect())
|
||||
}
|
||||
@@ -206,6 +214,8 @@ impl Config {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
use crate::app_attest_policy::AppAttestPolicy;
|
||||
|
||||
fn base() -> HashMap<String, String> {
|
||||
HashMap::from([
|
||||
@@ -442,6 +452,34 @@ mod tests {
|
||||
assert!(Config::from_map(&env).unwrap().dev_app_attest_bypass);
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
#[test]
|
||||
fn dev_app_attest_bypass_selects_development_policy_from_validated_config() {
|
||||
let mut env = base();
|
||||
env.insert("BUZZ_PUSH_DEV_APP_ATTEST_BYPASS".into(), "true".into());
|
||||
env.insert(
|
||||
"BUZZ_PUSH_ENABLED_PROFILES".into(),
|
||||
"buzz-ios-sandbox".into(),
|
||||
);
|
||||
let config = Config::from_map(&env).unwrap();
|
||||
let apple = crate::app_attest::AppAttestVerifier::for_policy_test();
|
||||
|
||||
let policy = AppAttestPolicy::from_config(config.dev_app_attest_bypass(), apple);
|
||||
|
||||
assert!(matches!(policy, AppAttestPolicy::Development(_)));
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
#[test]
|
||||
fn disabled_bypass_selects_apple_policy() {
|
||||
let config = Config::from_map(&base()).unwrap();
|
||||
let apple = crate::app_attest::AppAttestVerifier::for_policy_test();
|
||||
|
||||
let policy = AppAttestPolicy::from_config(config.dev_app_attest_bypass(), apple);
|
||||
|
||||
assert!(matches!(policy, AppAttestPolicy::Apple(_)));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn malformed_or_empty_keyrings_fail_startup() {
|
||||
for (variable, value) in [
|
||||
|
||||
@@ -35,6 +35,8 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
return Ok(());
|
||||
}
|
||||
let c = Config::from_env()?;
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
let dev_app_attest_bypass = c.dev_app_attest_bypass();
|
||||
let metrics_handle = buzz_push_gateway::metrics::install()?;
|
||||
let transport = Arc::new(ApnsTransport::certificate(
|
||||
&fs::read(&c.apns_cert_path)?,
|
||||
@@ -76,17 +78,15 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}
|
||||
}
|
||||
});
|
||||
let apple_app_attest =
|
||||
AppAttestVerifier::new(c.app_attest_app_id, fs::read(&c.app_attest_root_cert_path)?)?;
|
||||
let apple_app_attest = AppAttestVerifier::new(
|
||||
c.app_attest_app_id.clone(),
|
||||
fs::read(&c.app_attest_root_cert_path)?,
|
||||
)?;
|
||||
#[cfg(feature = "dev-app-attest-bypass")]
|
||||
let app_attest = if c.dev_app_attest_bypass {
|
||||
tracing::warn!(
|
||||
"DEVELOPMENT APP ATTEST BYPASS ACTIVE; Apple attestation and assertion verification are disabled"
|
||||
);
|
||||
Arc::new(AppAttestPolicy::development())
|
||||
} else {
|
||||
Arc::new(AppAttestPolicy::apple(apple_app_attest))
|
||||
};
|
||||
let app_attest = Arc::new(AppAttestPolicy::from_config(
|
||||
dev_app_attest_bypass,
|
||||
apple_app_attest,
|
||||
));
|
||||
#[cfg(not(feature = "dev-app-attest-bypass"))]
|
||||
let app_attest = Arc::new(AppAttestPolicy::apple(apple_app_attest));
|
||||
let accepting = Arc::new(AtomicBool::new(true));
|
||||
|
||||
Reference in New Issue
Block a user