1use {
8 crate::error::AppleCodesignError,
9 num_traits::cast::ToPrimitive,
10 plist::Value,
11 rasn::{
12 ber::de::DecodeError,
13 ber::enc::EncodeError,
14 de::Error as DeError,
15 enc::Error as EncError,
16 types::{fields::{Field, Fields}, Class, Constraints, Constructed, Integer, Tag},
17 AsnType, Codec, Decode, Decoder, Encode, Encoder,
18 },
19 std::collections::BTreeMap,
20};
21
22#[derive(AsnType, Debug, Decode)]
23struct DictionaryEntry {
24 #[rasn(tag(universal, 12))]
25 key: String,
26 value: WrappedValue,
27}
28
29#[derive(Debug)]
31struct Dictionary(plist::Dictionary);
32
33impl AsnType for Dictionary {
34 const TAG: Tag = Tag {
35 class: Class::Context,
36 value: 16,
37 };
38}
39
40impl Constructed for Dictionary {
41 const FIELDS: Fields = Fields::from_static(&[Field::new_optional_type::<()>("key")]);
44}
45
46impl Encode for Dictionary {
47 fn encode_with_tag_and_constraints<E: Encoder>(
48 &self,
49 encoder: &mut E,
50 tag: Tag,
51 _constraints: Constraints,
52 ) -> Result<(), E::Error> {
53 let map = self.0.iter().collect::<BTreeMap<_, _>>();
55
56 encoder.encode_sequence::<Self, _>(tag, |encoder| {
57 for (k, v) in map {
58 let wrapped = WrappedValue::try_from(v.clone())?;
59
60 encoder.encode_sequence::<Self, _>(Tag::SEQUENCE, |encoder| {
61 encoder.encode_utf8_string(Tag::UTF8_STRING, Constraints::NONE, k)?;
62 wrapped.encode(encoder)?;
63 Ok(())
64 })?;
65 }
66
67 Ok(())
68 })?;
69
70 Ok(())
71 }
72}
73
74impl Decode for Dictionary {
75 fn decode_with_tag_and_constraints<D: Decoder>(
76 decoder: &mut D,
77 tag: Tag,
78 _constraints: Constraints,
79 ) -> Result<Self, D::Error> {
80 decoder.decode_sequence::<Self, _, _>(tag, Some(|| Self(plist::Dictionary::new())), |decoder| {
81 let mut dict = plist::Dictionary::new();
82
83 loop {
84 let entry = decoder.decode_optional::<DictionaryEntry>()?;
85
86 if let Some(entry) = entry {
87 let value = plist::Value::try_from(entry.value)?;
88
89 dict.insert(entry.key, value);
90 } else {
91 break;
92 }
93 }
94
95 Ok(Self(dict))
96 })
97 }
98}
99
100#[derive(AsnType, Debug, Decode, Encode)]
102#[rasn(choice)]
103enum WrappedValue {
104 Array(Vec<WrappedValue>),
105 Dictionary(Dictionary),
106 #[rasn(tag(universal, 1))]
107 Boolean(bool),
108 #[rasn(tag(universal, 2))]
109 Integer(Integer),
110 #[rasn(tag(universal, 12))]
111 String(String),
112}
113
114impl TryFrom<Value> for WrappedValue {
115 type Error = EncodeError;
116
117 fn try_from(value: Value) -> Result<Self, Self::Error> {
118 match value {
119 Value::Array(v) => Ok(Self::Array(
120 v.into_iter()
121 .map(Self::try_from)
122 .collect::<Result<Vec<_>, _>>()?,
123 )),
124 Value::Dictionary(v) => Ok(Self::Dictionary(Dictionary(v))),
125 Value::Boolean(v) => Ok(Self::Boolean(v)),
126 Value::Integer(v) => {
127 let integer = Integer::from(v.as_signed().ok_or(EncodeError::custom(
128 "could not obtain integer representation from plist integer",
129 Codec::Der,
130 ))?);
131
132 Ok(Self::Integer(integer))
133 }
134 Value::String(v) => Ok(Self::String(v)),
135 Value::Data(_) => Err(EncodeError::custom(
136 "encoding of data values not supported",
137 Codec::Der,
138 )),
139 Value::Date(_) => Err(EncodeError::custom(
140 "encoding of date values not supported",
141 Codec::Der,
142 )),
143 Value::Real(_) => Err(EncodeError::custom(
144 "encoding of real values not supported",
145 Codec::Der,
146 )),
147 Value::Uid(_) => Err(EncodeError::custom(
148 "encoding of uid values not supported",
149 Codec::Der,
150 )),
151 _ => Err(EncodeError::custom(
152 "encoding of unknown value type not supported",
153 Codec::Der,
154 )),
155 }
156 }
157}
158
159impl TryFrom<WrappedValue> for Value {
160 type Error = DecodeError;
161
162 fn try_from(value: WrappedValue) -> Result<Self, Self::Error> {
163 match value {
164 WrappedValue::Array(v) => Ok(Self::Array(
165 v.into_iter()
166 .map(Self::try_from)
167 .collect::<Result<Vec<_>, _>>()?,
168 )),
169 WrappedValue::Dictionary(v) => Ok(Self::Dictionary(v.0)),
170 WrappedValue::Boolean(v) => Ok(Self::Boolean(v)),
171 WrappedValue::Integer(v) => {
172 let v = v.to_i64().ok_or(DecodeError::custom(
173 "could not convert BigInt to i64",
174 Codec::Der,
175 ))?;
176
177 Ok(Self::Integer(plist::Integer::from(v)))
178 }
179 WrappedValue::String(v) => Ok(Self::String(v)),
180 }
181 }
182}
183
184struct WrappedPlist(WrappedValue);
186
187impl AsnType for WrappedPlist {
188 const TAG: Tag = Tag {
189 class: Class::Application,
190 value: 16,
191 };
192}
193
194impl Constructed for WrappedPlist {
195 const FIELDS: Fields = Fields::from_static(&[
196 Field::new_required_type::<Integer>("key"),
197 Field::new_required_type::<WrappedValue>("value"),
198 ]);
199}
200
201impl TryFrom<Value> for WrappedPlist {
202 type Error = EncodeError;
203
204 fn try_from(value: Value) -> Result<Self, Self::Error> {
205 Ok(Self(value.try_into()?))
206 }
207}
208
209impl TryFrom<WrappedPlist> for Value {
210 type Error = DecodeError;
211
212 fn try_from(value: WrappedPlist) -> Result<Self, Self::Error> {
213 if let WrappedValue::Dictionary(d) = value.0 {
214 Ok(Self::Dictionary(d.0))
215 } else {
216 Err(DecodeError::custom(
217 "wrapped value not a dictionary",
218 Codec::Der,
219 ))
220 }
221 }
222}
223
224impl Encode for WrappedPlist {
225 fn encode_with_tag_and_constraints<E: Encoder>(
226 &self,
227 encoder: &mut E,
228 tag: Tag,
229 _constraints: Constraints,
230 ) -> Result<(), E::Error> {
231 encoder.encode_sequence::<Self, _>(tag, |encoder| {
232 encoder.encode_integer(Tag::INTEGER, Constraints::NONE, &Integer::from(1))?;
233 self.0.encode(encoder)
234 })?;
235
236 Ok(())
237 }
238}
239
240impl Decode for WrappedPlist {
241 fn decode_with_tag_and_constraints<D: Decoder>(
242 decoder: &mut D,
243 tag: Tag,
244 _constraints: Constraints,
245 ) -> Result<Self, D::Error> {
246 decoder.decode_sequence::<Self, _, _>(tag, None::<fn() -> Self>, |decoder| {
247 let _: Integer = decoder.decode_integer(Tag::INTEGER, Constraints::NONE)?;
248 let value = WrappedValue::decode(decoder)?;
249
250 Ok(Self(value))
251 })
252 }
253}
254
255pub fn der_encode_plist(value: &Value) -> Result<Vec<u8>, AppleCodesignError> {
257 rasn::der::encode_scope(|encoder| {
258 let wrapped = WrappedPlist::try_from(value.clone())?;
259 wrapped.encode(encoder)
260 })
261 .map_err(|e| AppleCodesignError::PlistDer(format!("{e}")))
262}
263
264pub fn der_decode_plist(data: impl AsRef<[u8]>) -> Result<Value, AppleCodesignError> {
266 rasn::der::decode::<WrappedPlist>(data.as_ref())
267 .and_then(Value::try_from)
268 .map_err(|e| AppleCodesignError::PlistDer(format!("{e}")))
269}
270
271#[cfg(test)]
272mod test {
273 use {
274 super::*,
275 crate::{
276 embedded_signature::{Blob, CodeSigningSlot},
277 macho::MachFile,
278 },
279 anyhow::{anyhow, Result},
280 plist::{Date, Uid},
281 std::{
282 process::Command,
283 time::{Duration, SystemTime},
284 },
285 };
286
287 const DER_EMPTY_DICT: &[u8] = &[112, 5, 2, 1, 1, 176, 0];
288 const DER_BOOL_FALSE: &[u8] = &[
289 112, 15, 2, 1, 1, 176, 10, 48, 8, 12, 3, 107, 101, 121, 1, 1, 0,
290 ];
291 const DER_BOOL_TRUE: &[u8] = &[
292 112, 15, 2, 1, 1, 176, 10, 48, 8, 12, 3, 107, 101, 121, 1, 1, 255,
293 ];
294 const DER_INTEGER_0: &[u8] = &[
295 112, 15, 2, 1, 1, 176, 10, 48, 8, 12, 3, 107, 101, 121, 2, 1, 0,
296 ];
297 const DER_INTEGER_NEG1: &[u8] = &[
298 112, 15, 2, 1, 1, 176, 10, 48, 8, 12, 3, 107, 101, 121, 2, 1, 255,
299 ];
300 const DER_INTEGER_1: &[u8] = &[
301 112, 15, 2, 1, 1, 176, 10, 48, 8, 12, 3, 107, 101, 121, 2, 1, 1,
302 ];
303 const DER_INTEGER_42: &[u8] = &[
304 112, 15, 2, 1, 1, 176, 10, 48, 8, 12, 3, 107, 101, 121, 2, 1, 42,
305 ];
306 const DER_STRING_EMPTY: &[u8] = &[112, 14, 2, 1, 1, 176, 9, 48, 7, 12, 3, 107, 101, 121, 12, 0];
307 const DER_STRING_VALUE: &[u8] = &[
308 112, 19, 2, 1, 1, 176, 14, 48, 12, 12, 3, 107, 101, 121, 12, 5, 118, 97, 108, 117, 101,
309 ];
310 const DER_ARRAY_EMPTY: &[u8] = &[112, 14, 2, 1, 1, 176, 9, 48, 7, 12, 3, 107, 101, 121, 48, 0];
311 const DER_ARRAY_FALSE: &[u8] = &[
312 112, 17, 2, 1, 1, 176, 12, 48, 10, 12, 3, 107, 101, 121, 48, 3, 1, 1, 0,
313 ];
314 const DER_ARRAY_TRUE_FOO: &[u8] = &[
315 112, 22, 2, 1, 1, 176, 17, 48, 15, 12, 3, 107, 101, 121, 48, 8, 1, 1, 255, 12, 3, 102, 111,
316 111,
317 ];
318 const DER_DICT_EMPTY: &[u8] = &[
319 112, 14, 2, 1, 1, 176, 9, 48, 7, 12, 3, 107, 101, 121, 176, 0,
320 ];
321 const DER_DICT_BOOL: &[u8] = &[
322 112, 26, 2, 1, 1, 176, 21, 48, 19, 12, 3, 107, 101, 121, 176, 12, 48, 10, 12, 5, 105, 110,
323 110, 101, 114, 1, 1, 0,
324 ];
325 const DER_MULTIPLE_KEYS: &[u8] = &[
326 112, 37, 2, 1, 1, 176, 32, 48, 8, 12, 3, 107, 101, 121, 1, 1, 0, 48, 9, 12, 4, 107, 101,
327 121, 50, 1, 1, 255, 48, 9, 12, 4, 107, 101, 121, 51, 2, 1, 42,
328 ];
329
330 #[allow(unused)]
335 fn sign_and_get_entitlements_der(value: &Value) -> Result<Vec<u8>> {
336 let this_exe = std::env::current_exe()?;
337
338 let temp_dir = tempfile::tempdir()?;
339
340 let in_path = temp_dir.path().join("original");
341 let entitlements_path = temp_dir.path().join("entitlements.xml");
342 std::fs::copy(this_exe, &in_path)?;
343 {
344 let mut fh = std::fs::File::create(&entitlements_path)?;
345 value.to_writer_xml(&mut fh)?;
346 }
347
348 let args = vec![
349 "--verbose".to_string(),
350 "--force".to_string(),
351 "-s".to_string(),
353 "-".to_string(),
354 "--generate-entitlement-der".to_string(),
355 "--entitlements".to_string(),
356 format!("{}", entitlements_path.display()),
357 format!("{}", in_path.display()),
358 ];
359
360 let status = Command::new("codesign").args(args).output()?;
361 if !status.status.success() {
362 return Err(anyhow!("codesign invocation failure"));
363 }
364
365 let signed_exe = std::fs::read(&in_path)?;
368 let mach = MachFile::parse(&signed_exe)?;
369 let macho = mach.nth_macho(0)?;
370
371 let signature = macho
372 .code_signature()?
373 .expect("unable to find code signature");
374
375 let slot = signature
376 .find_slot(CodeSigningSlot::EntitlementsDer)
377 .expect("unable to find der entitlements blob");
378
379 match slot.clone().into_parsed_blob()?.blob {
380 crate::embedded_signature::BlobData::EntitlementsDer(der) => {
381 Ok(der.serialize_payload()?)
382 }
383 _ => Err(anyhow!(
384 "failed to obtain entitlements DER (this should never happen)"
385 )),
386 }
387 }
388
389 #[cfg(target_os = "macos")]
393 #[allow(unused)]
394 fn apple_der_entitlements_encoding() -> Result<()> {
395 let mut d = plist::Dictionary::new();
398
399 assert_eq!(
400 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
401 DER_EMPTY_DICT
402 );
403
404 d.insert("key".into(), Value::Boolean(false));
405 assert_eq!(
406 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
407 DER_BOOL_FALSE
408 );
409
410 d.insert("key".into(), Value::Boolean(true));
411 assert_eq!(
412 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
413 DER_BOOL_TRUE
414 );
415
416 d.insert("key".into(), Value::Integer(0u32.into()));
417 assert_eq!(
418 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
419 DER_INTEGER_0
420 );
421
422 d.insert("key".into(), Value::Integer((-1i32).into()));
423 assert_eq!(
424 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
425 DER_INTEGER_NEG1
426 );
427
428 d.insert("key".into(), Value::Integer(1u32.into()));
429 assert_eq!(
430 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
431 DER_INTEGER_1
432 );
433
434 d.insert("key".into(), Value::Integer(42u32.into()));
435 assert_eq!(
436 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
437 DER_INTEGER_42
438 );
439
440 d.insert("key".into(), Value::Real(0.0f32.into()));
442 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
443
444 d.insert("key".into(), Value::Real((-1.0f32).into()));
445 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
446
447 d.insert("key".into(), Value::Real(1.0f32.into()));
448 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
449
450 d.insert("key".into(), Value::String("".into()));
451 assert_eq!(
452 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
453 DER_STRING_EMPTY
454 );
455
456 d.insert("key".into(), Value::String("value".into()));
457 assert_eq!(
458 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
459 DER_STRING_VALUE
460 );
461
462 d.insert("key".into(), Value::Uid(Uid::new(0)));
464 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
465
466 d.insert("key".into(), Value::Uid(Uid::new(1)));
467 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
468
469 d.insert("key".into(), Value::Uid(Uid::new(42)));
470 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
471
472 d.insert(
476 "key".into(),
477 Value::Date(Date::from(SystemTime::UNIX_EPOCH)),
478 );
479 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
480 d.insert(
481 "key".into(),
482 Value::Date(Date::from(
483 SystemTime::UNIX_EPOCH + Duration::from_secs(86400 * 365 * 30),
484 )),
485 );
486 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
487
488 d.insert("key".into(), Value::Data(vec![]));
490 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
491 d.insert("key".into(), Value::Data(b"foo".to_vec()));
492 assert!(sign_and_get_entitlements_der(&Value::Dictionary(d.clone())).is_err());
493
494 d.insert("key".into(), Value::Array(vec![]));
495 assert_eq!(
496 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
497 DER_ARRAY_EMPTY
498 );
499
500 d.insert("key".into(), Value::Array(vec![Value::Boolean(false)]));
501 assert_eq!(
502 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
503 DER_ARRAY_FALSE
504 );
505
506 d.insert(
507 "key".into(),
508 Value::Array(vec![Value::Boolean(true), Value::String("foo".into())]),
509 );
510 assert_eq!(
511 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
512 DER_ARRAY_TRUE_FOO
513 );
514
515 let mut inner = plist::Dictionary::new();
516 d.insert("key".into(), Value::Dictionary(inner.clone()));
517 assert_eq!(
518 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
519 DER_DICT_EMPTY
520 );
521
522 inner.insert("inner".into(), Value::Boolean(false));
523 d.insert("key".into(), Value::Dictionary(inner.clone()));
524 assert_eq!(
525 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
526 DER_DICT_BOOL
527 );
528
529 d.insert("key".into(), Value::Boolean(false));
530 d.insert("key2".into(), Value::Boolean(true));
531 d.insert("key3".into(), Value::Integer(42i32.into()));
532 assert_eq!(
533 sign_and_get_entitlements_der(&Value::Dictionary(d.clone()))?,
534 DER_MULTIPLE_KEYS
535 );
536
537 Ok(())
538 }
539
540 #[test]
541 fn der_encoding() -> Result<()> {
542 let mut d = plist::Dictionary::new();
543
544 assert_eq!(
545 der_encode_plist(&Value::Dictionary(d.clone()))?,
546 DER_EMPTY_DICT
547 );
548 assert_eq!(
549 der_decode_plist(DER_EMPTY_DICT)?,
550 Value::Dictionary(d.clone())
551 );
552
553 d.insert("key".into(), Value::Boolean(false));
554 assert_eq!(
555 der_encode_plist(&Value::Dictionary(d.clone()))?,
556 DER_BOOL_FALSE
557 );
558 assert_eq!(
559 der_decode_plist(DER_BOOL_FALSE)?,
560 Value::Dictionary(d.clone())
561 );
562
563 d.insert("key".into(), Value::Boolean(true));
564 assert_eq!(
565 der_encode_plist(&Value::Dictionary(d.clone()))?,
566 DER_BOOL_TRUE
567 );
568 assert_eq!(
569 der_decode_plist(DER_BOOL_TRUE)?,
570 Value::Dictionary(d.clone())
571 );
572
573 d.insert("key".into(), Value::Integer(0u32.into()));
574 assert_eq!(
575 der_encode_plist(&Value::Dictionary(d.clone()))?,
576 DER_INTEGER_0
577 );
578 assert_eq!(
579 der_decode_plist(DER_INTEGER_0)?,
580 Value::Dictionary(d.clone())
581 );
582
583 d.insert("key".into(), Value::Integer((-1i32).into()));
584 assert_eq!(
585 der_encode_plist(&Value::Dictionary(d.clone()))?,
586 DER_INTEGER_NEG1
587 );
588 assert_eq!(
589 der_decode_plist(DER_INTEGER_NEG1)?,
590 Value::Dictionary(d.clone())
591 );
592
593 d.insert("key".into(), Value::Integer(1u32.into()));
594 assert_eq!(
595 der_encode_plist(&Value::Dictionary(d.clone()))?,
596 DER_INTEGER_1
597 );
598 assert_eq!(
599 der_decode_plist(DER_INTEGER_1)?,
600 Value::Dictionary(d.clone())
601 );
602
603 d.insert("key".into(), Value::Integer(42u32.into()));
604 assert_eq!(
605 der_encode_plist(&Value::Dictionary(d.clone()))?,
606 DER_INTEGER_42
607 );
608 assert_eq!(
609 der_decode_plist(DER_INTEGER_42)?,
610 Value::Dictionary(d.clone())
611 );
612
613 d.insert("key".into(), Value::Real(0.0f32.into()));
614 assert!(matches!(
615 der_encode_plist(&Value::Dictionary(d.clone())),
616 Err(AppleCodesignError::PlistDer(_))
617 ));
618
619 d.insert("key".into(), Value::Real((-1.0f32).into()));
620 assert!(matches!(
621 der_encode_plist(&Value::Dictionary(d.clone())),
622 Err(AppleCodesignError::PlistDer(_))
623 ));
624
625 d.insert("key".into(), Value::Real(1.0f32.into()));
626 assert!(matches!(
627 der_encode_plist(&Value::Dictionary(d.clone())),
628 Err(AppleCodesignError::PlistDer(_))
629 ));
630
631 d.insert("key".into(), Value::String("".into()));
632 assert_eq!(
633 der_encode_plist(&Value::Dictionary(d.clone()))?,
634 DER_STRING_EMPTY
635 );
636 assert_eq!(
637 der_decode_plist(DER_STRING_EMPTY)?,
638 Value::Dictionary(d.clone())
639 );
640
641 d.insert("key".into(), Value::String("value".into()));
642 assert_eq!(
643 der_encode_plist(&Value::Dictionary(d.clone()))?,
644 DER_STRING_VALUE
645 );
646 assert_eq!(
647 der_decode_plist(DER_STRING_VALUE)?,
648 Value::Dictionary(d.clone())
649 );
650
651 d.insert("key".into(), Value::Uid(Uid::new(0)));
652 assert!(matches!(
653 der_encode_plist(&Value::Dictionary(d.clone())),
654 Err(AppleCodesignError::PlistDer(_))
655 ));
656
657 d.insert("key".into(), Value::Uid(Uid::new(1)));
658 assert!(matches!(
659 der_encode_plist(&Value::Dictionary(d.clone())),
660 Err(AppleCodesignError::PlistDer(_))
661 ));
662
663 d.insert("key".into(), Value::Uid(Uid::new(42)));
664 assert!(matches!(
665 der_encode_plist(&Value::Dictionary(d.clone())),
666 Err(AppleCodesignError::PlistDer(_))
667 ));
668
669 d.insert(
670 "key".into(),
671 Value::Date(Date::from(SystemTime::UNIX_EPOCH)),
672 );
673 assert!(matches!(
674 der_encode_plist(&Value::Dictionary(d.clone())),
675 Err(AppleCodesignError::PlistDer(_))
676 ));
677 d.insert(
678 "key".into(),
679 Value::Date(Date::from(
680 SystemTime::UNIX_EPOCH + Duration::from_secs(86400 * 365 * 30),
681 )),
682 );
683 assert!(matches!(
684 der_encode_plist(&Value::Dictionary(d.clone())),
685 Err(AppleCodesignError::PlistDer(_))
686 ));
687
688 d.insert("key".into(), Value::Data(vec![]));
690 assert!(matches!(
691 der_encode_plist(&Value::Dictionary(d.clone())),
692 Err(AppleCodesignError::PlistDer(_))
693 ));
694 d.insert("key".into(), Value::Data(b"foo".to_vec()));
695 assert!(matches!(
696 der_encode_plist(&Value::Dictionary(d.clone())),
697 Err(AppleCodesignError::PlistDer(_))
698 ));
699
700 d.insert("key".into(), Value::Array(vec![]));
701 assert_eq!(
702 der_encode_plist(&Value::Dictionary(d.clone()))?,
703 DER_ARRAY_EMPTY
704 );
705 assert_eq!(
706 der_decode_plist(DER_ARRAY_EMPTY)?,
707 Value::Dictionary(d.clone())
708 );
709
710 d.insert("key".into(), Value::Array(vec![Value::Boolean(false)]));
711 assert_eq!(
712 der_encode_plist(&Value::Dictionary(d.clone()))?,
713 DER_ARRAY_FALSE
714 );
715 assert_eq!(
716 der_decode_plist(DER_ARRAY_FALSE)?,
717 Value::Dictionary(d.clone())
718 );
719
720 d.insert(
721 "key".into(),
722 Value::Array(vec![Value::Boolean(true), Value::String("foo".into())]),
723 );
724 assert_eq!(
725 der_encode_plist(&Value::Dictionary(d.clone()))?,
726 DER_ARRAY_TRUE_FOO
727 );
728 assert_eq!(
729 der_decode_plist(DER_ARRAY_TRUE_FOO)?,
730 Value::Dictionary(d.clone())
731 );
732
733 let mut inner = plist::Dictionary::new();
734 d.insert("key".into(), Value::Dictionary(inner.clone()));
735 assert_eq!(
736 der_encode_plist(&Value::Dictionary(d.clone()))?,
737 DER_DICT_EMPTY
738 );
739 assert_eq!(
740 der_decode_plist(DER_DICT_EMPTY)?,
741 Value::Dictionary(d.clone())
742 );
743
744 inner.insert("inner".into(), Value::Boolean(false));
745 d.insert("key".into(), Value::Dictionary(inner.clone()));
746 assert_eq!(
747 der_encode_plist(&Value::Dictionary(d.clone()))?,
748 DER_DICT_BOOL
749 );
750 assert_eq!(
751 der_decode_plist(DER_DICT_BOOL)?,
752 Value::Dictionary(d.clone())
753 );
754
755 d.insert("key".into(), Value::Boolean(false));
756 d.insert("key2".into(), Value::Boolean(true));
757 d.insert("key3".into(), Value::Integer(42i32.into()));
758 assert_eq!(
759 der_encode_plist(&Value::Dictionary(d.clone()))?,
760 DER_MULTIPLE_KEYS
761 );
762 assert_eq!(
763 der_decode_plist(DER_MULTIPLE_KEYS)?,
764 Value::Dictionary(d.clone())
765 );
766
767 Ok(())
768 }
769}