app_store_server_library/
signed_data_verifier.rs1use base64::engine::general_purpose::STANDARD;
2use base64::{DecodeError, Engine};
3
4use crate::chain_verifier::{ChainVerifier, ChainVerifierError};
5use crate::primitives::app_transaction::AppTransaction;
6use crate::primitives::environment::Environment;
7use crate::primitives::jws_renewal_info_decoded_payload::JWSRenewalInfoDecodedPayload;
8use crate::primitives::jws_transaction_decoded_payload::JWSTransactionDecodedPayload;
9use crate::primitives::response_body_v2_decoded_payload::ResponseBodyV2DecodedPayload;
10use crate::utils::{base64_url_to_base64, StringExt};
11use jsonwebtoken::{Algorithm, DecodingKey, Validation};
12use serde::de::DeserializeOwned;
13use crate::chain_verifier::ChainVerificationFailureReason::InvalidChainLength;
14
15#[derive(thiserror::Error, Debug)]
16pub enum SignedDataVerifierError {
17 #[error("VerificationFailure")]
18 VerificationFailure,
19
20 #[error("InvalidAppIdentifier")]
21 InvalidAppIdentifier,
22
23 #[error("InvalidEnvironment")]
24 InvalidEnvironment,
25
26 #[error("InternalChainVerifierError")]
27 InternalChainVerifierError(#[from] ChainVerifierError),
28
29 #[error("InternalDecodeError: [{0}]")]
30 InternalDecodeError(#[from] DecodeError),
31
32 #[error("InternalDeserializationError: [{0}]")]
33 InternalDeserializationError(#[from] serde_json::Error),
34
35 #[error("InternalJWTError: [{0}]")]
36 InternalJWTError(#[from] jsonwebtoken::errors::Error),
37}
38
39const EXPECTED_CHAIN_LENGTH: usize = 3;
40
41pub struct SignedDataVerifier {
44 environment: Environment,
45 bundle_id: String,
46 app_apple_id: Option<i64>,
47 chain_verifier: ChainVerifier,
48}
49
50impl SignedDataVerifier {
51 pub fn new(
64 root_certificates: Vec<Vec<u8>>,
65 environment: Environment,
66 bundle_id: String,
67 app_apple_id: Option<i64>,
68 ) -> Self {
69 let chain_verifier = ChainVerifier::new(root_certificates);
70
71 SignedDataVerifier {
72 environment,
73 bundle_id,
74 app_apple_id,
75 chain_verifier
76 }
77 }
78}
79
80impl SignedDataVerifier {
81 pub fn verify_and_decode_renewal_info(
96 &self,
97 signed_renewal_info: &str,
98 ) -> Result<JWSRenewalInfoDecodedPayload, SignedDataVerifierError> {
99 Ok(self.decode_signed_object(signed_renewal_info)?)
100 }
101
102 pub fn verify_and_decode_signed_transaction(
117 &self,
118 signed_transaction: &str,
119 ) -> Result<JWSTransactionDecodedPayload, SignedDataVerifierError> {
120 let decoded_signed_tx: JWSTransactionDecodedPayload = self.decode_signed_object(signed_transaction)?;
121
122 if decoded_signed_tx.bundle_id.as_ref() != Some(&self.bundle_id) {
123 return Err(SignedDataVerifierError::InvalidAppIdentifier);
124 }
125
126 if decoded_signed_tx.environment.as_ref() != Some(&self.environment) {
127 return Err(SignedDataVerifierError::InvalidEnvironment);
128 }
129
130 Ok(decoded_signed_tx)
131 }
132
133 pub fn verify_and_decode_notification(
148 &self,
149 signed_payload: &str,
150 ) -> Result<ResponseBodyV2DecodedPayload, SignedDataVerifierError> {
151 let decoded_signed_notification: ResponseBodyV2DecodedPayload = self.decode_signed_object(signed_payload)?;
152
153 let bundle_id;
154 let app_apple_id;
155 let environment;
156
157 if let Some(data) = &decoded_signed_notification.data {
158 bundle_id = data.bundle_id.clone();
159 app_apple_id = data.app_apple_id.clone();
160 environment = data.environment.clone();
161 } else if let Some(summary) = &decoded_signed_notification.summary {
162 bundle_id = summary.bundle_id.clone();
163 app_apple_id = summary.app_apple_id.clone();
164 environment = summary.environment.clone();
165 } else if let Some(external_purchase_token) = &decoded_signed_notification.external_purchase_token {
166 bundle_id = external_purchase_token
167 .bundle_id
168 .clone();
169 app_apple_id = external_purchase_token
170 .app_apple_id
171 .clone();
172
173 if let Some(external_purchase_id) = &external_purchase_token.external_purchase_id {
174 if external_purchase_id.starts_with("SANDBOX") {
175 environment = Some(Environment::Sandbox)
176 } else {
177 environment = Some(Environment::Production)
178 }
179 } else {
180 environment = Some(Environment::Production)
181 }
182 } else {
183 bundle_id = None;
184 app_apple_id = None;
185 environment = None;
186 }
187
188 self.verify_notification_app_identifier_and_environment(bundle_id, app_apple_id, environment)?;
189
190 Ok(decoded_signed_notification)
191 }
192
193 fn verify_notification_app_identifier_and_environment(
194 &self,
195 bundle_id: Option<String>,
196 app_apple_id: Option<i64>,
197 environment: Option<Environment>,
198 ) -> Result<(), SignedDataVerifierError> {
199 if let Some(bundle_id) = bundle_id {
200 if bundle_id != self.bundle_id {
201 return Err(SignedDataVerifierError::InvalidAppIdentifier);
202 }
203 }
204
205 if self.environment == Environment::Production && self.app_apple_id != app_apple_id {
206 return Err(SignedDataVerifierError::InvalidAppIdentifier);
207 }
208
209 if let Some(environment) = environment {
210 if self.environment != Environment::LocalTesting && self.environment != environment {
211 return Err(SignedDataVerifierError::InvalidEnvironment);
212 }
213 }
214
215 Ok(())
216 }
217
218 pub fn verify_and_decode_app_transaction(
233 &self,
234 signed_app_transaction: &str,
235 ) -> Result<AppTransaction, SignedDataVerifierError> {
236 let decoded_app_transaction: AppTransaction = self.decode_signed_object(signed_app_transaction)?;
237
238 if decoded_app_transaction
239 .bundle_id
240 .as_ref()
241 != Some(&self.bundle_id)
242 {
243 return Err(SignedDataVerifierError::InvalidAppIdentifier);
244 }
245
246 if decoded_app_transaction
247 .receipt_type
248 .as_ref()
249 != Some(&self.environment)
250 {
251 return Err(SignedDataVerifierError::InvalidEnvironment);
252 }
253
254 Ok(decoded_app_transaction)
255 }
256
257 fn decode_signed_object<T: DeserializeOwned>(&self, signed_obj: &str) -> Result<T, SignedDataVerifierError> {
259 if self.environment == Environment::Xcode || self.environment == Environment::LocalTesting {
262 const EXPECTED_JWT_SEGMENTS: usize = 3;
263
264 let body_segments: Vec<&str> = signed_obj.split('.').collect();
265
266 if body_segments.len() != EXPECTED_JWT_SEGMENTS {
267 return Err(SignedDataVerifierError::VerificationFailure);
268 }
269
270 let _ = jsonwebtoken::decode_header(&signed_obj)?;
271 let body_base64 = base64_url_to_base64(body_segments[1]);
272 let body_data = STANDARD.decode(body_base64)?;
273 let decoded_body = serde_json::from_slice(&body_data)?;
274 return Ok(decoded_body);
275 }
276
277 let header = jsonwebtoken::decode_header(signed_obj)?;
278
279 let Some(x5c) = header.x5c else {
280 return Err(SignedDataVerifierError::VerificationFailure);
281 };
282
283 if x5c.is_empty() {
284 return Err(SignedDataVerifierError::VerificationFailure);
285 }
286
287 let x5c: Result<Vec<Vec<u8>>, DecodeError> = x5c
288 .iter()
289 .map(|c| c.as_der_bytes())
290 .collect();
291 let chain = x5c?;
292
293 if header.alg != Algorithm::ES256 {
294 return Err(SignedDataVerifierError::VerificationFailure);
295 }
296
297 let pub_key = self.verify_chain(&chain, None)?;
298 let pub_key = &pub_key[pub_key.len() - 65..];
299
300 let decoding_key = DecodingKey::from_ec_der(pub_key);
301 let claims: [&str; 0] = [];
302
303 let mut validator = Validation::new(Algorithm::ES256);
304 validator.validate_exp = false;
305 validator.set_required_spec_claims(&claims);
306
307 let payload = jsonwebtoken::decode::<T>(signed_obj, &decoding_key, &validator)?;
308 Ok(payload.claims)
309 }
310
311 fn verify_chain(&self, chain: &Vec<Vec<u8>>, effective_date: Option<u64>) -> Result<Vec<u8>, ChainVerifierError> {
312 if chain.len() != EXPECTED_CHAIN_LENGTH {
313 return Err(ChainVerifierError::VerificationFailure(InvalidChainLength))
314 }
315
316 let leaf = &chain[0];
317 let intermediate = &chain[1];
318
319 Ok(self.chain_verifier.verify(leaf, intermediate, effective_date)?)
320 }
321}
322
323#[cfg(test)]
324mod tests {
325 use crate::chain_verifier::ChainVerificationFailureReason::InvalidChainLength;
326 use super::*;
327
328 #[test]
329 fn test_invalid_chain_length() -> Result<(), ChainVerifierError> {
330 let root = Vec::new();
331 let leaf = Vec::new();
332 let intermediate = Vec::new();
333
334 let chain = vec![leaf, intermediate, Vec::new(), Vec::new()];
335 let verifier = SignedDataVerifier::new(vec![root], Environment::Production, "com.example".into(), Some(1234));
336 let public_key = verifier.verify_chain(&chain, None);
337
338 assert!(
339 matches!(
340 public_key.expect_err("Expect error"),
341 ChainVerifierError::VerificationFailure(InvalidChainLength)
342 )
343 );
344 Ok(())
345 }
346}