1#![recursion_limit = "1024"]
97#![deny(
98 missing_docs,
99 missing_debug_implementations,
100 missing_copy_implementations,
101 trivial_casts,
102 trivial_numeric_casts,
103 unsafe_code,
104 unstable_features,
105 unused_import_braces,
106 unused_qualifications
107)]
108
109mod errors;
110mod parser;
111use parser::{parse_captures, parse_captures_iter, Captures};
112
113pub use crate::errors::{PemError, Result};
114use std::str;
115
116const LINE_WRAP: usize = 64;
118
119#[derive(Debug, Clone, Copy)]
121pub enum LineEnding {
122 CRLF,
124 LF,
126}
127
128#[derive(Debug, Clone, Copy)]
130pub struct EncodeConfig {
131 pub line_ending: LineEnding,
133}
134
135#[derive(PartialEq, Debug, Clone)]
137pub struct Pem {
138 pub tag: String,
140 pub contents: Vec<u8>,
142}
143
144fn decode_data(raw_data: &str) -> Result<Vec<u8>> {
145 let data: String = raw_data.lines().map(str::trim_end).collect();
148
149 let contents = base64::decode_config(&data, base64::STANDARD).map_err(PemError::InvalidData)?;
151
152 Ok(contents)
153}
154
155impl Pem {
156 fn new_from_captures(caps: Captures) -> Result<Pem> {
157 fn as_utf8<'a>(bytes: &'a [u8]) -> Result<&'a str> {
158 Ok(str::from_utf8(bytes).map_err(PemError::NotUtf8)?)
159 }
160
161 let tag = as_utf8(caps.begin)?;
163 if tag.is_empty() {
164 return Err(PemError::MissingBeginTag);
165 }
166
167 let tag_end = as_utf8(caps.end)?;
169 if tag_end.is_empty() {
170 return Err(PemError::MissingEndTag);
171 }
172
173 if tag != tag_end {
175 return Err(PemError::MismatchedTags(tag.into(), tag_end.into()));
176 }
177
178 let raw_data = as_utf8(caps.data)?;
180 let contents = decode_data(raw_data)?;
181
182 Ok(Pem {
183 tag: tag.to_owned(),
184 contents,
185 })
186 }
187}
188
189pub fn parse<B: AsRef<[u8]>>(input: B) -> Result<Pem> {
233 parse_captures(&input.as_ref())
234 .ok_or_else(|| PemError::MalformedFraming)
235 .and_then(Pem::new_from_captures)
236}
237
238pub fn parse_many<B: AsRef<[u8]>>(input: B) -> Result<Vec<Pem>> {
308 parse_captures_iter(&input.as_ref())
310 .map(|caps| Pem::new_from_captures(caps))
311 .collect()
312}
313
314pub fn encode(pem: &Pem) -> String {
327 encode_config(
328 pem,
329 EncodeConfig {
330 line_ending: LineEnding::CRLF,
331 },
332 )
333}
334
335pub fn encode_config(pem: &Pem, config: EncodeConfig) -> String {
349 let line_ending = match config.line_ending {
350 LineEnding::CRLF => "\r\n",
351 LineEnding::LF => "\n",
352 };
353
354 let mut output = String::new();
355
356 let contents = if pem.contents.is_empty() {
357 String::from("")
358 } else {
359 base64::encode_config(
360 &pem.contents,
361 base64::Config::new(base64::CharacterSet::Standard, true),
362 )
363 };
364
365 output.push_str(&format!("-----BEGIN {}-----{}", pem.tag, line_ending));
366 for c in contents.as_bytes().chunks(LINE_WRAP) {
367 output.push_str(&format!("{}{}", str::from_utf8(c).unwrap(), line_ending));
368 }
369 output.push_str(&format!("-----END {}-----{}", pem.tag, line_ending));
370
371 output
372}
373
374pub fn encode_many(pems: &[Pem]) -> String {
393 pems.iter()
394 .map(encode)
395 .collect::<Vec<String>>()
396 .join("\r\n")
397}
398
399pub fn encode_many_config(pems: &[Pem], config: EncodeConfig) -> String {
421 let line_ending = match config.line_ending {
422 LineEnding::CRLF => "\r\n",
423 LineEnding::LF => "\n",
424 };
425 pems.iter()
426 .map(|value| encode_config(value, config))
427 .collect::<Vec<String>>()
428 .join(line_ending)
429}
430
431#[cfg(feature = "serde")]
432mod serde_impl {
433 use super::{encode, parse, Pem};
434 use serde::{
435 de::{Error, Visitor},
436 Deserialize, Serialize,
437 };
438 use std::fmt;
439
440 impl Serialize for Pem {
441 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
442 where
443 S: serde::Serializer,
444 {
445 serializer.serialize_str(&encode(self))
446 }
447 }
448
449 struct PemVisitor;
450
451 impl<'de> Visitor<'de> for PemVisitor {
452 type Value = Pem;
453
454 fn expecting(&self, _formatter: &mut fmt::Formatter) -> fmt::Result {
455 Ok(())
456 }
457
458 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
459 where
460 E: Error,
461 {
462 parse(v).map_err(Error::custom)
463 }
464 }
465
466 impl<'de> Deserialize<'de> for Pem {
467 fn deserialize<D>(deserializer: D) -> Result<Pem, D::Error>
468 where
469 D: serde::Deserializer<'de>,
470 {
471 deserializer.deserialize_str(PemVisitor)
472 }
473 }
474}
475
476#[cfg(test)]
477mod test {
478 use super::*;
479 use std::error::Error;
480
481 const SAMPLE_CRLF: &'static str = "-----BEGIN RSA PRIVATE KEY-----\r
482MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc\r
483dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO\r
4842gkT00AWTSzM9Zns0HedY31yEabkuFvrMCHjscEF7u3Y6PB7An3IzooBHchsFDei\r
485AAECIQD/JahddzR5K3A6rzTidmAf1PBtqi7296EnWv8WvpfAAQIhAOvowIXZI4Un\r
486DXjgZ9ekuUjZN+GUQRAVlkEEohGLVy59AiEA90VtqDdQuWWpvJX0cM08V10tLXrT\r
487TTGsEtITid1ogAECIQDAaFl90ZgS5cMrL3wCeatVKzVUmuJmB/VAmlLFFGzK0QIh\r
488ANJGc7AFk4fyFD/OezhwGHbWmo/S+bfeAiIh2Ss2FxKJ\r
489-----END RSA PRIVATE KEY-----\r
490\r
491-----BEGIN RSA PUBLIC KEY-----\r
492MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo\r
493QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0\r
494RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI\r
495sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk\r
496ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6\r
497/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g\r
498RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg\r
499-----END RSA PUBLIC KEY-----\r
500";
501
502 const SAMPLE_LF: &'static str = "-----BEGIN RSA PRIVATE KEY-----
503MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc
504dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO
5052gkT00AWTSzM9Zns0HedY31yEabkuFvrMCHjscEF7u3Y6PB7An3IzooBHchsFDei
506AAECIQD/JahddzR5K3A6rzTidmAf1PBtqi7296EnWv8WvpfAAQIhAOvowIXZI4Un
507DXjgZ9ekuUjZN+GUQRAVlkEEohGLVy59AiEA90VtqDdQuWWpvJX0cM08V10tLXrT
508TTGsEtITid1ogAECIQDAaFl90ZgS5cMrL3wCeatVKzVUmuJmB/VAmlLFFGzK0QIh
509ANJGc7AFk4fyFD/OezhwGHbWmo/S+bfeAiIh2Ss2FxKJ
510-----END RSA PRIVATE KEY-----
511
512-----BEGIN RSA PUBLIC KEY-----
513MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo
514QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0
515RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI
516sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk
517ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6
518/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g
519RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
520-----END RSA PUBLIC KEY-----
521";
522
523 #[test]
524 fn test_parse_works() {
525 let pem = parse(SAMPLE_CRLF).unwrap();
526 assert_eq!(pem.tag, "RSA PRIVATE KEY");
527 }
528
529 #[test]
530 fn test_parse_invalid_framing() {
531 let input = "--BEGIN data-----
532 -----END data-----";
533 assert_eq!(parse(&input), Err(PemError::MalformedFraming));
534 }
535
536 #[test]
537 fn test_parse_invalid_begin() {
538 let input = "-----BEGIN -----
539MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo
540QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0
541RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI
542sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk
543ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6
544/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g
545RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
546-----END RSA PUBLIC KEY-----";
547 assert_eq!(parse(&input), Err(PemError::MissingBeginTag));
548 }
549
550 #[test]
551 fn test_parse_invalid_end() {
552 let input = "-----BEGIN DATA-----
553MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo
554QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0
555RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI
556sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk
557ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6
558/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g
559RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
560-----END -----";
561 assert_eq!(parse(&input), Err(PemError::MissingEndTag));
562 }
563
564 #[test]
565 fn test_parse_invalid_data() {
566 let input = "-----BEGIN DATA-----
567MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oY?
568QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0
569RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI
570sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk
571ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6
572/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g
573RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
574-----END DATA-----";
575 match parse(&input) {
576 Err(e @ PemError::InvalidData(_)) => {
577 assert_eq!(
578 &format!("{}", e.source().unwrap()),
579 "Invalid byte 63, offset 63."
580 );
581 }
582 _ => assert!(false),
583 }
584 }
585
586 #[test]
587 fn test_parse_empty_data() {
588 let input = "-----BEGIN DATA-----
589-----END DATA-----";
590 let pem = parse(&input).unwrap();
591 assert_eq!(pem.contents.len(), 0);
592 }
593
594 #[test]
595 fn test_parse_many_works() {
596 let pems = parse_many(SAMPLE_CRLF).unwrap();
597 assert_eq!(pems.len(), 2);
598 assert_eq!(pems[0].tag, "RSA PRIVATE KEY");
599 assert_eq!(pems[1].tag, "RSA PUBLIC KEY");
600 }
601
602 #[test]
603 fn test_parse_many_errors_on_invalid_section() {
604 let input = SAMPLE_LF.to_owned() + "-----BEGIN -----\n-----END -----";
605 assert_eq!(parse_many(&input), Err(PemError::MissingBeginTag));
606 }
607
608 #[test]
609 fn test_encode_empty_contents() {
610 let pem = Pem {
611 tag: String::from("FOO"),
612 contents: vec![],
613 };
614 let encoded = encode(&pem);
615 assert!(encoded != "");
616
617 let pem_out = parse(&encoded).unwrap();
618 assert_eq!(&pem, &pem_out);
619 }
620
621 #[test]
622 fn test_encode_contents() {
623 let pem = Pem {
624 tag: String::from("FOO"),
625 contents: vec![1, 2, 3, 4],
626 };
627 let encoded = encode(&pem);
628 assert!(encoded != "");
629
630 let pem_out = parse(&encoded).unwrap();
631 assert_eq!(&pem, &pem_out);
632 }
633
634 #[test]
635 fn test_encode_many() {
636 let pems = parse_many(SAMPLE_CRLF).unwrap();
637 let encoded = encode_many(&pems);
638
639 assert_eq!(SAMPLE_CRLF, encoded);
640 }
641
642 #[test]
643 fn test_encode_config_contents() {
644 let pem = Pem {
645 tag: String::from("FOO"),
646 contents: vec![1, 2, 3, 4],
647 };
648 let config = EncodeConfig {
649 line_ending: LineEnding::LF,
650 };
651 let encoded = encode_config(&pem, config);
652 assert!(encoded != "");
653
654 let pem_out = parse(&encoded).unwrap();
655 assert_eq!(&pem, &pem_out);
656 }
657
658 #[test]
659 fn test_encode_many_config() {
660 let pems = parse_many(SAMPLE_LF).unwrap();
661 let config = EncodeConfig {
662 line_ending: LineEnding::LF,
663 };
664 let encoded = encode_many_config(&pems, config);
665
666 assert_eq!(SAMPLE_LF, encoded);
667 }
668
669 #[cfg(feature = "serde")]
670 #[test]
671 fn test_serde() {
672 let pem = Pem {
673 tag: String::from("Mock tag"),
674 contents: "Mock contents".as_bytes().to_vec(),
675 };
676 let value = serde_json::to_string_pretty(&pem).unwrap();
677 let result = serde_json::from_str(&value).unwrap();
678 assert_eq!(pem, result);
679 }
680
681 const HEADER_CRLF: &'static str = "-----BEGIN CERTIFICATE-----\r
682MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc\r
683dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO\r
6842gkT00AWTSzM9Zns0HedY31yEabkuFvrMCHjscEF7u3Y6PB7An3IzooBHchsFDei\r
685AAECIQD/JahddzR5K3A6rzTidmAf1PBtqi7296EnWv8WvpfAAQIhAOvowIXZI4Un\r
686DXjgZ9ekuUjZN+GUQRAVlkEEohGLVy59AiEA90VtqDdQuWWpvJX0cM08V10tLXrT\r
687TTGsEtITid1ogAECIQDAaFl90ZgS5cMrL3wCeatVKzVUmuJmB/VAmlLFFGzK0QIh\r
688ANJGc7AFk4fyFD/OezhwGHbWmo/S+bfeAiIh2Ss2FxKJ\r
689-----END CERTIFICATE-----\r
690-----BEGIN RSA PRIVATE KEY-----\r
691Proc-Type: 4,ENCRYPTED\r
692DEK-Info: AES-256-CBC,975C518B7D2CCD1164A3354D1F89C5A6\r
693\r
694MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo\r
695QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0\r
696RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI\r
697sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk\r
698ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6\r
699/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g\r
700RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg\r
701-----END RSA PRIVATE KEY-----\r
702";
703 const HEADER_CRLF_DATA: [&'static str; 2] = [
704 "MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc\r
705dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO\r
7062gkT00AWTSzM9Zns0HedY31yEabkuFvrMCHjscEF7u3Y6PB7An3IzooBHchsFDei\r
707AAECIQD/JahddzR5K3A6rzTidmAf1PBtqi7296EnWv8WvpfAAQIhAOvowIXZI4Un\r
708DXjgZ9ekuUjZN+GUQRAVlkEEohGLVy59AiEA90VtqDdQuWWpvJX0cM08V10tLXrT\r
709TTGsEtITid1ogAECIQDAaFl90ZgS5cMrL3wCeatVKzVUmuJmB/VAmlLFFGzK0QIh\r
710ANJGc7AFk4fyFD/OezhwGHbWmo/S+bfeAiIh2Ss2FxKJ\r",
711 "MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo\r
712QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0\r
713RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI\r
714sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk\r
715ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6\r
716/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g\r
717RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg\r",
718 ];
719
720 const HEADER_LF: &'static str = "-----BEGIN CERTIFICATE-----
721MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc
722dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO
7232gkT00AWTSzM9Zns0HedY31yEabkuFvrMCHjscEF7u3Y6PB7An3IzooBHchsFDei
724AAECIQD/JahddzR5K3A6rzTidmAf1PBtqi7296EnWv8WvpfAAQIhAOvowIXZI4Un
725DXjgZ9ekuUjZN+GUQRAVlkEEohGLVy59AiEA90VtqDdQuWWpvJX0cM08V10tLXrT
726TTGsEtITid1ogAECIQDAaFl90ZgS5cMrL3wCeatVKzVUmuJmB/VAmlLFFGzK0QIh
727ANJGc7AFk4fyFD/OezhwGHbWmo/S+bfeAiIh2Ss2FxKJ
728-----END CERTIFICATE-----
729-----BEGIN RSA PRIVATE KEY-----
730Proc-Type: 4,ENCRYPTED
731DEK-Info: AES-256-CBC,975C518B7D2CCD1164A3354D1F89C5A6
732
733MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo
734QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0
735RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI
736sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk
737ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6
738/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g
739RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg
740-----END RSA PRIVATE KEY-----
741";
742 const HEADER_LF_DATA: [&'static str; 2] = [
743 "MIIBPQIBAAJBAOsfi5AGYhdRs/x6q5H7kScxA0Kzzqe6WI6gf6+tc6IvKQJo5rQc
744dWWSQ0nRGt2hOPDO+35NKhQEjBQxPh/v7n0CAwEAAQJBAOGaBAyuw0ICyENy5NsO
7452gkT00AWTSzM9Zns0HedY31yEabkuFvrMCHjscEF7u3Y6PB7An3IzooBHchsFDei
746AAECIQD/JahddzR5K3A6rzTidmAf1PBtqi7296EnWv8WvpfAAQIhAOvowIXZI4Un
747DXjgZ9ekuUjZN+GUQRAVlkEEohGLVy59AiEA90VtqDdQuWWpvJX0cM08V10tLXrT
748TTGsEtITid1ogAECIQDAaFl90ZgS5cMrL3wCeatVKzVUmuJmB/VAmlLFFGzK0QIh
749ANJGc7AFk4fyFD/OezhwGHbWmo/S+bfeAiIh2Ss2FxKJ",
750 "MIIBOgIBAAJBAMIeCnn9G/7g2Z6J+qHOE2XCLLuPoh5NHTO2Fm+PbzBvafBo0oYo
751QVVy7frzxmOqx6iIZBxTyfAQqBPO3Br59BMCAwEAAQJAX+PjHPuxdqiwF6blTkS0
752RFI1MrnzRbCmOkM6tgVO0cd6r5Z4bDGLusH9yjI9iI84gPRjK0AzymXFmBGuREHI
753sQIhAPKf4pp+Prvutgq2ayygleZChBr1DC4XnnufBNtaswyvAiEAzNGVKgNvzuhk
754ijoUXIDruJQEGFGvZTsi1D2RehXiT90CIQC4HOQUYKCydB7oWi1SHDokFW2yFyo6
755/+lf3fgNjPI6OQIgUPmTFXciXxT1msh3gFLf3qt2Kv8wbr9Ad9SXjULVpGkCIB+g
756RzHX0lkJl9Stshd/7Gbt65/QYq+v+xvAeT0CoyIg",
757 ];
758
759 fn cmp_data(left: &[u8], right: &[u8]) -> bool {
760 if left.len() != right.len() {
761 false
762 } else {
763 left.iter()
764 .zip(right.iter())
765 .all(|(left, right)| left == right)
766 }
767 }
768
769 #[test]
770 fn test_parse_many_with_headers_crlf() {
771 let pems = parse_many(HEADER_CRLF).unwrap();
772 assert_eq!(pems.len(), 2);
773 assert_eq!(pems[0].tag, "CERTIFICATE");
774 assert!(cmp_data(
775 &pems[0].contents,
776 &decode_data(HEADER_CRLF_DATA[0]).unwrap()
777 ));
778 assert_eq!(pems[1].tag, "RSA PRIVATE KEY");
779 assert!(cmp_data(
780 &pems[1].contents,
781 &decode_data(HEADER_CRLF_DATA[1]).unwrap()
782 ));
783 }
784
785 #[test]
786 fn test_parse_many_with_headers_lf() {
787 let pems = parse_many(HEADER_LF).unwrap();
788 assert_eq!(pems.len(), 2);
789 assert_eq!(pems[0].tag, "CERTIFICATE");
790 assert!(cmp_data(
791 &pems[0].contents,
792 &decode_data(HEADER_LF_DATA[0]).unwrap()
793 ));
794 assert_eq!(pems[1].tag, "RSA PRIVATE KEY");
795 assert!(cmp_data(
796 &pems[1].contents,
797 &decode_data(HEADER_LF_DATA[1]).unwrap()
798 ));
799 }
800}