1use {once_cell::sync::Lazy, std::ops::Deref, x509_certificate::CapturedX509Certificate};
16
17static APPLE_INC_ROOT_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
19 CapturedX509Certificate::from_der(
20 include_bytes!("apple-certs/AppleIncRootCertificate.cer").to_vec(),
21 )
22 .unwrap()
23});
24
25static APPLE_COMPUTER_INC_ROOT_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
27 CapturedX509Certificate::from_der(
28 include_bytes!("apple-certs/AppleComputerRootCertificate.cer").to_vec(),
29 )
30 .unwrap()
31});
32
33static APPLE_ROOT_CA_G2_ROOT_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
35 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleRootCA-G2.cer").to_vec())
36 .unwrap()
37});
38
39static APPLE_ROOT_CA_G3_ROOT_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
41 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleRootCA-G3.cer").to_vec())
42 .unwrap()
43});
44
45static APPLE_IST_CA_2_G1_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
47 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleISTCA2G1.cer").to_vec())
48 .unwrap()
49});
50
51static APPLE_IST_CA_8_G1_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
53 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleISTCA8G1.cer").to_vec())
54 .unwrap()
55});
56
57static APPLICATION_INTEGRATION_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
59 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleAAICA.cer").to_vec())
60 .unwrap()
61});
62
63static APPLICATION_INTEGRATION_2_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
65 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleAAI2CA.cer").to_vec())
66 .unwrap()
67});
68
69static APPLICATION_INTEGRATION_G3_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
71 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleAAICAG3.cer").to_vec())
72 .unwrap()
73});
74
75static APPLE_APPLICATION_INTEGRATION_CA_5_G1_CERTIFICATE: Lazy<CapturedX509Certificate> =
77 Lazy::new(|| {
78 CapturedX509Certificate::from_der(
79 include_bytes!("apple-certs/AppleApplicationIntegrationCA5G1.cer").to_vec(),
80 )
81 .unwrap()
82 });
83
84static APPLE_APPLICATION_INTEGRATION_CA_7_G1_CERTIFICATE: Lazy<CapturedX509Certificate> =
86 Lazy::new(|| {
87 CapturedX509Certificate::from_der(
88 include_bytes!("apple-certs/AppleApplicationIntegrationCA7G1.cer").to_vec(),
89 )
90 .unwrap()
91 });
92
93static DEVELOPER_AUTHENTICATION_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
95 CapturedX509Certificate::from_der(include_bytes!("apple-certs/DevAuthCA.cer").to_vec()).unwrap()
96});
97
98static DEVELOPER_ID_G1_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
100 CapturedX509Certificate::from_der(include_bytes!("apple-certs/DeveloperIDCA.cer").to_vec())
101 .unwrap()
102});
103
104static DEVELOPER_ID_G2_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
106 CapturedX509Certificate::from_der(include_bytes!("apple-certs/DeveloperIDG2CA.cer").to_vec())
107 .unwrap()
108});
109
110static SOFTWARE_UPDATE_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
112 CapturedX509Certificate::from_der(
113 include_bytes!("apple-certs/AppleSoftwareUpdateCertificationAuthority.cer").to_vec(),
114 )
115 .unwrap()
116});
117
118static TIMESTAMP_CERTIFICATE: Lazy<CapturedX509Certificate> = Lazy::new(|| {
120 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleTimestampCA.cer").to_vec())
121 .unwrap()
122});
123
124static WORLD_WIDE_DEVELOPER_RELATIONS_G1_CERTIFICATE: Lazy<CapturedX509Certificate> =
126 Lazy::new(|| {
127 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCA.cer").to_vec())
128 .unwrap()
129 });
130
131static WORLD_WIDE_DEVELOPER_RELATIONS_G2_CERTIFICATE: Lazy<CapturedX509Certificate> =
133 Lazy::new(|| {
134 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG2.cer").to_vec())
135 .unwrap()
136 });
137
138static WORLD_WIDE_DEVELOPER_RELATIONS_G3_CERTIFICATE: Lazy<CapturedX509Certificate> =
140 Lazy::new(|| {
141 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG3.cer").to_vec())
142 .unwrap()
143 });
144
145static WORLD_WIDE_DEVELOPER_RELATIONS_G4_CERTIFICATE: Lazy<CapturedX509Certificate> =
147 Lazy::new(|| {
148 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG4.cer").to_vec())
149 .unwrap()
150 });
151
152static WORLD_WIDE_DEVELOPER_RELATIONS_G5_CERTIFICATE: Lazy<CapturedX509Certificate> =
154 Lazy::new(|| {
155 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG5.cer").to_vec())
156 .unwrap()
157 });
158
159static WORLD_WIDE_DEVELOPER_RELATIONS_G6_CERTIFICATE: Lazy<CapturedX509Certificate> =
161 Lazy::new(|| {
162 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG6.cer").to_vec())
163 .unwrap()
164 });
165
166static WORLD_WIDE_DEVELOPER_RELATIONS_G7_CERTIFICATE: Lazy<CapturedX509Certificate> =
168 Lazy::new(|| {
169 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG7.cer").to_vec())
170 .unwrap()
171 });
172
173static WORLD_WIDE_DEVELOPER_RELATIONS_G8_CERTIFICATE: Lazy<CapturedX509Certificate> =
175 Lazy::new(|| {
176 CapturedX509Certificate::from_der(include_bytes!("apple-certs/AppleWWDRCAG8.cer").to_vec())
177 .unwrap()
178 });
179
180static KNOWN_CERTIFICATES: Lazy<Vec<&CapturedX509Certificate>> = Lazy::new(|| {
182 vec![
183 APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.deref(),
185 APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.deref(),
186 APPLE_INC_ROOT_CERTIFICATE.deref(),
187 APPLE_COMPUTER_INC_ROOT_CERTIFICATE.deref(),
188 APPLE_IST_CA_2_G1_CERTIFICATE.deref(),
189 APPLE_IST_CA_8_G1_CERTIFICATE.deref(),
190 APPLICATION_INTEGRATION_CERTIFICATE.deref(),
191 APPLICATION_INTEGRATION_2_CERTIFICATE.deref(),
192 APPLICATION_INTEGRATION_G3_CERTIFICATE.deref(),
193 APPLE_APPLICATION_INTEGRATION_CA_5_G1_CERTIFICATE.deref(),
194 APPLE_APPLICATION_INTEGRATION_CA_7_G1_CERTIFICATE.deref(),
195 DEVELOPER_AUTHENTICATION_CERTIFICATE.deref(),
196 DEVELOPER_ID_G1_CERTIFICATE.deref(),
197 DEVELOPER_ID_G2_CERTIFICATE.deref(),
198 SOFTWARE_UPDATE_CERTIFICATE.deref(),
199 TIMESTAMP_CERTIFICATE.deref(),
200 WORLD_WIDE_DEVELOPER_RELATIONS_G1_CERTIFICATE.deref(),
201 WORLD_WIDE_DEVELOPER_RELATIONS_G2_CERTIFICATE.deref(),
202 WORLD_WIDE_DEVELOPER_RELATIONS_G3_CERTIFICATE.deref(),
203 WORLD_WIDE_DEVELOPER_RELATIONS_G4_CERTIFICATE.deref(),
204 WORLD_WIDE_DEVELOPER_RELATIONS_G5_CERTIFICATE.deref(),
205 WORLD_WIDE_DEVELOPER_RELATIONS_G6_CERTIFICATE.deref(),
206 WORLD_WIDE_DEVELOPER_RELATIONS_G7_CERTIFICATE.deref(),
207 WORLD_WIDE_DEVELOPER_RELATIONS_G8_CERTIFICATE.deref(),
208 ]
209});
210
211static KNOWN_ROOTS: Lazy<Vec<&CapturedX509Certificate>> = Lazy::new(|| {
212 vec![
213 APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.deref(),
214 APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.deref(),
215 APPLE_INC_ROOT_CERTIFICATE.deref(),
216 APPLE_COMPUTER_INC_ROOT_CERTIFICATE.deref(),
217 ]
218});
219
220#[derive(Clone, Copy, Debug, Eq, PartialEq)]
230pub enum KnownCertificate {
231 AppleComputerIncRoot,
235
236 AppleRootCa,
240
241 AppleRootCaG2Root,
245
246 AppleRootCaG3Root,
250
251 AppleIstCa2G1,
255
256 AppleIstCa8G1,
260
261 ApplicationIntegration,
265
266 ApplicationIntegration2,
270
271 ApplicationIntegrationG3,
275
276 AppleApplicationIntegrationCa5G1,
280
281 AppleApplicationIntegrationCa7G1,
285
286 DeveloperAuthentication,
290
291 DeveloperIdG1,
295
296 DeveloperIdG2,
300
301 SoftwareUpdate,
305
306 Timestamp,
310
311 WwdrG1,
315
316 WwdrG2,
320
321 WwdrG3,
325
326 WwdrG4,
330
331 WwdrG5,
335
336 WwdrG6,
340
341 WwdrG7,
345
346 WwdrG8,
350}
351
352impl Deref for KnownCertificate {
353 type Target = CapturedX509Certificate;
354
355 fn deref(&self) -> &Self::Target {
356 match self {
357 Self::AppleComputerIncRoot => APPLE_COMPUTER_INC_ROOT_CERTIFICATE.deref(),
358 Self::AppleRootCa => APPLE_INC_ROOT_CERTIFICATE.deref(),
359 Self::AppleRootCaG2Root => APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.deref(),
360 Self::AppleRootCaG3Root => APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.deref(),
361 Self::AppleIstCa2G1 => APPLE_IST_CA_2_G1_CERTIFICATE.deref(),
362 Self::AppleIstCa8G1 => APPLE_IST_CA_8_G1_CERTIFICATE.deref(),
363 Self::ApplicationIntegration => APPLICATION_INTEGRATION_CERTIFICATE.deref(),
364 Self::ApplicationIntegration2 => APPLICATION_INTEGRATION_2_CERTIFICATE.deref(),
365 Self::ApplicationIntegrationG3 => APPLICATION_INTEGRATION_G3_CERTIFICATE.deref(),
366 Self::AppleApplicationIntegrationCa5G1 => {
367 APPLE_APPLICATION_INTEGRATION_CA_5_G1_CERTIFICATE.deref()
368 }
369 Self::AppleApplicationIntegrationCa7G1 => {
370 APPLE_APPLICATION_INTEGRATION_CA_7_G1_CERTIFICATE.deref()
371 }
372 Self::DeveloperAuthentication => DEVELOPER_AUTHENTICATION_CERTIFICATE.deref(),
373 Self::DeveloperIdG1 => DEVELOPER_ID_G1_CERTIFICATE.deref(),
374 Self::DeveloperIdG2 => DEVELOPER_ID_G2_CERTIFICATE.deref(),
375 Self::SoftwareUpdate => SOFTWARE_UPDATE_CERTIFICATE.deref(),
376 Self::Timestamp => TIMESTAMP_CERTIFICATE.deref(),
377 Self::WwdrG1 => WORLD_WIDE_DEVELOPER_RELATIONS_G1_CERTIFICATE.deref(),
378 Self::WwdrG2 => WORLD_WIDE_DEVELOPER_RELATIONS_G2_CERTIFICATE.deref(),
379 Self::WwdrG3 => WORLD_WIDE_DEVELOPER_RELATIONS_G3_CERTIFICATE.deref(),
380 Self::WwdrG4 => WORLD_WIDE_DEVELOPER_RELATIONS_G4_CERTIFICATE.deref(),
381 Self::WwdrG5 => WORLD_WIDE_DEVELOPER_RELATIONS_G5_CERTIFICATE.deref(),
382 Self::WwdrG6 => WORLD_WIDE_DEVELOPER_RELATIONS_G6_CERTIFICATE.deref(),
383 Self::WwdrG7 => WORLD_WIDE_DEVELOPER_RELATIONS_G7_CERTIFICATE.deref(),
384 Self::WwdrG8 => WORLD_WIDE_DEVELOPER_RELATIONS_G8_CERTIFICATE.deref(),
385 }
386 }
387}
388
389impl AsRef<CapturedX509Certificate> for KnownCertificate {
390 fn as_ref(&self) -> &CapturedX509Certificate {
391 self.deref()
392 }
393}
394
395impl TryFrom<&CapturedX509Certificate> for KnownCertificate {
396 type Error = &'static str;
397
398 fn try_from(cert: &CapturedX509Certificate) -> Result<Self, Self::Error> {
399 let want = cert.constructed_data();
400
401 match cert.constructed_data() {
402 _ if APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.constructed_data() == want => {
403 Ok(Self::AppleRootCaG3Root)
404 }
405 _ if APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.constructed_data() == want => {
406 Ok(Self::AppleRootCaG2Root)
407 }
408 _ if APPLE_INC_ROOT_CERTIFICATE.constructed_data() == want => Ok(Self::AppleRootCa),
409 _ if APPLE_COMPUTER_INC_ROOT_CERTIFICATE.constructed_data() == want => {
410 Ok(Self::AppleComputerIncRoot)
411 }
412 _ if APPLE_IST_CA_2_G1_CERTIFICATE.constructed_data() == want => {
413 Ok(Self::AppleIstCa2G1)
414 }
415 _ if APPLE_IST_CA_8_G1_CERTIFICATE.constructed_data() == want => {
416 Ok(Self::AppleIstCa8G1)
417 }
418 _ if APPLICATION_INTEGRATION_CERTIFICATE.constructed_data() == want => {
419 Ok(Self::ApplicationIntegration)
420 }
421 _ if APPLICATION_INTEGRATION_2_CERTIFICATE.constructed_data() == want => {
422 Ok(Self::ApplicationIntegration2)
423 }
424 _ if APPLICATION_INTEGRATION_G3_CERTIFICATE.constructed_data() == want => {
425 Ok(Self::ApplicationIntegrationG3)
426 }
427 _ if APPLE_APPLICATION_INTEGRATION_CA_5_G1_CERTIFICATE.constructed_data() == want => {
428 Ok(Self::AppleApplicationIntegrationCa5G1)
429 }
430 _ if APPLE_APPLICATION_INTEGRATION_CA_7_G1_CERTIFICATE.constructed_data() == want => {
431 Ok(Self::AppleApplicationIntegrationCa7G1)
432 }
433 _ if DEVELOPER_AUTHENTICATION_CERTIFICATE.constructed_data() == want => {
434 Ok(Self::DeveloperAuthentication)
435 }
436 _ if DEVELOPER_ID_G1_CERTIFICATE.constructed_data() == want => Ok(Self::DeveloperIdG1),
437 _ if DEVELOPER_ID_G2_CERTIFICATE.constructed_data() == want => Ok(Self::DeveloperIdG2),
438 _ if SOFTWARE_UPDATE_CERTIFICATE.constructed_data() == want => Ok(Self::SoftwareUpdate),
439 _ if TIMESTAMP_CERTIFICATE.constructed_data() == want => Ok(Self::Timestamp),
440 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G1_CERTIFICATE.constructed_data() == want => {
441 Ok(Self::WwdrG1)
442 }
443 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G2_CERTIFICATE.constructed_data() == want => {
444 Ok(Self::WwdrG2)
445 }
446 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G3_CERTIFICATE.constructed_data() == want => {
447 Ok(Self::WwdrG3)
448 }
449 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G4_CERTIFICATE.constructed_data() == want => {
450 Ok(Self::WwdrG4)
451 }
452 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G5_CERTIFICATE.constructed_data() == want => {
453 Ok(Self::WwdrG5)
454 }
455 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G6_CERTIFICATE.constructed_data() == want => {
456 Ok(Self::WwdrG6)
457 }
458 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G7_CERTIFICATE.constructed_data() == want => {
459 Ok(Self::WwdrG7)
460 }
461 _ if WORLD_WIDE_DEVELOPER_RELATIONS_G8_CERTIFICATE.constructed_data() == want => {
462 Ok(Self::WwdrG8)
463 }
464 _ => Err("certificate not found"),
465 }
466 }
467}
468
469impl KnownCertificate {
470 pub fn all() -> &'static [&'static CapturedX509Certificate] {
475 KNOWN_CERTIFICATES.deref().as_ref()
476 }
477
478 pub fn all_roots() -> &'static [&'static CapturedX509Certificate] {
480 KNOWN_ROOTS.deref()
481 }
482}
483
484#[cfg(test)]
485mod test {
486 use {
487 super::*,
488 crate::certificate::{AppleCertificate, CertificateAuthorityExtension},
489 };
490
491 #[test]
492 fn all() {
493 for cert in KnownCertificate::all() {
494 assert!(cert.subject_common_name().is_some());
495 assert!(KnownCertificate::try_from(*cert).is_ok());
496 }
497 }
498
499 #[test]
500 fn apple_root_ca() {
501 assert!(APPLE_INC_ROOT_CERTIFICATE.is_apple_root_ca());
502 assert!(!APPLE_INC_ROOT_CERTIFICATE.is_apple_intermediate_ca());
503 assert!(APPLE_COMPUTER_INC_ROOT_CERTIFICATE.is_apple_root_ca());
504 assert!(!APPLE_COMPUTER_INC_ROOT_CERTIFICATE.is_apple_intermediate_ca());
505 assert!(APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.is_apple_root_ca());
506 assert!(!APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.is_apple_intermediate_ca());
507 assert!(APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.is_apple_root_ca());
508 assert!(!APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.is_apple_intermediate_ca());
509
510 assert!(!WORLD_WIDE_DEVELOPER_RELATIONS_G3_CERTIFICATE.is_apple_root_ca());
511 assert!(WORLD_WIDE_DEVELOPER_RELATIONS_G3_CERTIFICATE.is_apple_intermediate_ca());
512
513 let wanted = [APPLE_INC_ROOT_CERTIFICATE.deref(),
514 APPLE_COMPUTER_INC_ROOT_CERTIFICATE.deref(),
515 APPLE_ROOT_CA_G2_ROOT_CERTIFICATE.deref(),
516 APPLE_ROOT_CA_G3_ROOT_CERTIFICATE.deref()];
517
518 for cert in KnownCertificate::all() {
519 if wanted.contains(cert) {
520 continue;
521 }
522
523 assert!(!cert.is_apple_root_ca());
524 assert!(cert.is_apple_intermediate_ca());
525 }
526 }
527
528 #[test]
529 fn intermediate_have_apple_ca_extension() {
530 for cert in KnownCertificate::all()
532 .iter()
533 .filter(|cert| !cert.is_apple_root_ca())
534 .filter(|cert| {
537 cert.issuer_name()
538 .iter_common_name()
539 .all(|atv| !atv.to_string().unwrap().contains("GeoTrust"))
540 })
541 {
542 assert!(!cert.apple_ca_extensions().is_empty());
543 }
544
545 assert_eq!(
547 KnownCertificate::DeveloperIdG1
548 .apple_ca_extensions()
549 .first(),
550 Some(&CertificateAuthorityExtension::DeveloperId)
551 );
552 assert_eq!(
553 KnownCertificate::DeveloperIdG2
554 .apple_ca_extensions()
555 .first(),
556 Some(&CertificateAuthorityExtension::DeveloperId)
557 );
558 assert_eq!(
559 KnownCertificate::WwdrG1.apple_ca_extensions().first(),
560 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
561 );
562 assert_eq!(
563 KnownCertificate::WwdrG2.apple_ca_extensions().first(),
564 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelationsG2)
565 );
566 assert_eq!(
567 KnownCertificate::WwdrG3.apple_ca_extensions().first(),
568 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
569 );
570 assert_eq!(
571 KnownCertificate::WwdrG4.apple_ca_extensions().first(),
572 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
573 );
574 assert_eq!(
575 KnownCertificate::WwdrG5.apple_ca_extensions().first(),
576 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
577 );
578 assert_eq!(
579 KnownCertificate::WwdrG6.apple_ca_extensions().first(),
580 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
581 );
582 assert_eq!(
583 KnownCertificate::WwdrG7.apple_ca_extensions().first(),
584 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
585 );
586 assert_eq!(
587 KnownCertificate::WwdrG8.apple_ca_extensions().first(),
588 Some(&CertificateAuthorityExtension::AppleWorldwideDeveloperRelations)
589 );
590 }
591
592 #[test]
593 fn chaining() {
594 let relevant = KnownCertificate::all()
595 .iter()
596 .filter(|cert| {
597 cert.issuer_name()
598 .iter_common_name()
599 .all(|atv| !atv.to_string().unwrap().contains("GeoTrust"))
600 })
601 .filter(|cert| {
602 cert.constructed_data() != APPLICATION_INTEGRATION_G3_CERTIFICATE.constructed_data()
603 && cert.constructed_data()
604 != APPLE_APPLICATION_INTEGRATION_CA_5_G1_CERTIFICATE.constructed_data()
605 });
606
607 for cert in relevant {
608 let chain = cert.resolve_signing_chain(KnownCertificate::all().iter().copied());
609 let apple_chain = cert.apple_issuing_chain();
610 assert_eq!(chain.len(), apple_chain.len());
611 }
612 }
613}