|  | @@ -1,29 +1,43 @@
 | 
											
												
													
														|  |  //! Code for converting [Writecap]s to and from the X.509 certificate format.
 |  |  //! Code for converting [Writecap]s to and from the X.509 certificate format.
 | 
											
												
													
														|  | 
 |  | +//! TODO: Improve the efficiency and quality of this code by moving to a different library for
 | 
											
												
													
														|  | 
 |  | +//! handling X.509 certificates.
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  use crate::{
 |  |  use crate::{
 | 
											
												
													
														|  |      bterr,
 |  |      bterr,
 | 
											
												
													
														|  | -    crypto::{AsymKeyPub, HashKind, RsaSsaPss, Sha2_256, Sha2_512, Sign},
 |  | 
 | 
											
												
													
														|  | -    Principaled, Result, Writecap,
 |  | 
 | 
											
												
													
														|  | 
 |  | +    crypto::{AsymKeyPub, HashKind, KeyLen, RsaSsaPss, Sha2_256, Sha2_512, Sign, Signature},
 | 
											
												
													
														|  | 
 |  | +    BlockPath, Epoch, Principal, Principaled, Result, Writecap, WritecapBody,
 | 
											
												
													
														|  |  };
 |  |  };
 | 
											
												
													
														|  |  use bcder::{
 |  |  use bcder::{
 | 
											
												
													
														|  | -    decode::{BytesSource, Constructed},
 |  | 
 | 
											
												
													
														|  | 
 |  | +    decode::{BytesSource, Constructed, DecodeError, SliceSource},
 | 
											
												
													
														|  |      encode::{PrimitiveContent, Values},
 |  |      encode::{PrimitiveContent, Values},
 | 
											
												
													
														|  | -    BitString, Captured, Ia5String, Integer, Mode, OctetString, Oid, Tag,
 |  | 
 | 
											
												
													
														|  | 
 |  | +    BitString, Captured, Integer, Mode, OctetString, Oid, Tag, Utf8String,
 | 
											
												
													
														|  |  };
 |  |  };
 | 
											
												
													
														|  |  use bytes::{BufMut, Bytes, BytesMut};
 |  |  use bytes::{BufMut, Bytes, BytesMut};
 | 
											
												
													
														|  |  use chrono::{offset::Utc, TimeZone};
 |  |  use chrono::{offset::Utc, TimeZone};
 | 
											
												
													
														|  | 
 |  | +use std::ops::Deref;
 | 
											
												
													
														|  |  use x509_certificate::{
 |  |  use x509_certificate::{
 | 
											
												
													
														|  |      asn1time::{Time, UtcTime},
 |  |      asn1time::{Time, UtcTime},
 | 
											
												
													
														|  |      certificate::X509Certificate,
 |  |      certificate::X509Certificate,
 | 
											
												
													
														|  | -    rfc3280::Name,
 |  | 
 | 
											
												
													
														|  | 
 |  | +    rfc3280::{AttributeValue, Name},
 | 
											
												
													
														|  |      rfc5280::{
 |  |      rfc5280::{
 | 
											
												
													
														|  |          AlgorithmIdentifier, AlgorithmParameter, Certificate, CertificateSerialNumber, Extension,
 |  |          AlgorithmIdentifier, AlgorithmParameter, Certificate, CertificateSerialNumber, Extension,
 | 
											
												
													
														|  |          Extensions, SubjectPublicKeyInfo, TbsCertificate, Validity, Version,
 |  |          Extensions, SubjectPublicKeyInfo, TbsCertificate, Validity, Version,
 | 
											
												
													
														|  |      },
 |  |      },
 | 
											
												
													
														|  |  };
 |  |  };
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |  mod private {
 |  |  mod private {
 | 
											
												
													
														|  |      use super::*;
 |  |      use super::*;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +    fn oid(slice: &'static [u8]) -> Oid {
 | 
											
												
													
														|  | 
 |  | +        Oid(Bytes::from(slice))
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    macro_rules! bit_string {
 | 
											
												
													
														|  | 
 |  | +        ($bytes:expr) => {
 | 
											
												
													
														|  | 
 |  | +            BitString::new(0, Bytes::from($bytes))
 | 
											
												
													
														|  | 
 |  | +        };
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |      impl Sha2_256 {
 |  |      impl Sha2_256 {
 | 
											
												
													
														|  |          // The DER encoding of the OID 2.16.840.1.101.3.4.2.1
 |  |          // The DER encoding of the OID 2.16.840.1.101.3.4.2.1
 | 
											
												
													
														|  |          const OID: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
 |  |          const OID: &[u8] = &[0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01];
 | 
											
										
											
												
													
														|  | @@ -41,44 +55,30 @@ mod private {
 | 
											
												
													
														|  |                  HashKind::Sha2_512 => Sha2_512::OID,
 |  |                  HashKind::Sha2_512 => Sha2_512::OID,
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn from_oid(slice: &[u8]) -> Result<Self> {
 | 
											
												
													
														|  | 
 |  | +            if slice == Sha2_256::OID {
 | 
											
												
													
														|  | 
 |  | +                Ok(Self::Sha2_256)
 | 
											
												
													
														|  | 
 |  | +            } else if slice == Sha2_512::OID {
 | 
											
												
													
														|  | 
 |  | +                Ok(Self::Sha2_512)
 | 
											
												
													
														|  | 
 |  | +            } else {
 | 
											
												
													
														|  | 
 |  | +                Err(bterr!("unrecognized OID"))
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      /// The DER encoding of 1.2.840.113549.1.1.8 (id-mgf1 in RFC 4055)
 |  |      /// The DER encoding of 1.2.840.113549.1.1.8 (id-mgf1 in RFC 4055)
 | 
											
												
													
														|  |      const MGF1_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08];
 |  |      const MGF1_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x08];
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    struct NullTermOid {
 |  | 
 | 
											
												
													
														|  | -        oid: Oid,
 |  | 
 | 
											
												
													
														|  | -    }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -    impl NullTermOid {
 |  | 
 | 
											
												
													
														|  | -        fn new(oid: Oid) -> Self {
 |  | 
 | 
											
												
													
														|  | -            Self { oid }
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        fn encode_ref(&self) -> impl Values + '_ {
 |  | 
 | 
											
												
													
														|  | -            self.encode_ref_as(Tag::SEQUENCE)
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        fn encode_ref_as(&self, tag: Tag) -> impl Values + '_ {
 |  | 
 | 
											
												
													
														|  | -            bcder::encode::sequence_as(
 |  | 
 | 
											
												
													
														|  | -                tag,
 |  | 
 | 
											
												
													
														|  | -                (
 |  | 
 | 
											
												
													
														|  | -                    self.oid.encode_ref(),
 |  | 
 | 
											
												
													
														|  | -                    bcder::encode::Constructed::new(Tag::NULL, bcder::encode::Nothing),
 |  | 
 | 
											
												
													
														|  | -                ),
 |  | 
 | 
											
												
													
														|  | -            )
 |  | 
 | 
											
												
													
														|  | -        }
 |  | 
 | 
											
												
													
														|  | -    }
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -    struct AlgoId {
 |  | 
 | 
											
												
													
														|  | 
 |  | +    struct SingleParamAlgoId {
 | 
											
												
													
														|  |          algorithm: Oid,
 |  |          algorithm: Oid,
 | 
											
												
													
														|  | -        parameters: NullTermOid,
 |  | 
 | 
											
												
													
														|  | 
 |  | +        parameters: Oid,
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    impl AlgoId {
 |  | 
 | 
											
												
													
														|  | 
 |  | +    impl SingleParamAlgoId {
 | 
											
												
													
														|  |          fn new(hash_kind: HashKind) -> Self {
 |  |          fn new(hash_kind: HashKind) -> Self {
 | 
											
												
													
														|  |              let algorithm = Oid(Bytes::from(MGF1_OID));
 |  |              let algorithm = Oid(Bytes::from(MGF1_OID));
 | 
											
												
													
														|  | -            let parameters = NullTermOid::new(Oid(Bytes::from(hash_kind.oid())));
 |  | 
 | 
											
												
													
														|  | 
 |  | +            let parameters = Oid(Bytes::from(hash_kind.oid()));
 | 
											
												
													
														|  |              Self {
 |  |              Self {
 | 
											
												
													
														|  |                  algorithm,
 |  |                  algorithm,
 | 
											
												
													
														|  |                  parameters,
 |  |                  parameters,
 | 
											
										
											
												
													
														|  | @@ -95,19 +95,30 @@ mod private {
 | 
											
												
													
														|  |                  (self.algorithm.encode_ref(), self.parameters.encode_ref()),
 |  |                  (self.algorithm.encode_ref(), self.parameters.encode_ref()),
 | 
											
												
													
														|  |              )
 |  |              )
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn take_from<S: bcder::decode::Source>(
 | 
											
												
													
														|  | 
 |  | +            cons: &mut Constructed<'_, S>,
 | 
											
												
													
														|  | 
 |  | +        ) -> std::result::Result<Self, DecodeError<S::Error>> {
 | 
											
												
													
														|  | 
 |  | +            cons.take_sequence(|cons| {
 | 
											
												
													
														|  | 
 |  | +                Ok(Self {
 | 
											
												
													
														|  | 
 |  | +                    algorithm: Oid::take_from(cons)?,
 | 
											
												
													
														|  | 
 |  | +                    parameters: Oid::take_from(cons)?,
 | 
											
												
													
														|  | 
 |  | +                })
 | 
											
												
													
														|  | 
 |  | +            })
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      struct RsaSsaPssParams {
 |  |      struct RsaSsaPssParams {
 | 
											
												
													
														|  | -        hash_algorithm: NullTermOid,
 |  | 
 | 
											
												
													
														|  | -        mask_gen_algorithm: AlgoId,
 |  | 
 | 
											
												
													
														|  | 
 |  | +        hash_algorithm: Oid,
 | 
											
												
													
														|  | 
 |  | +        mask_gen_algorithm: SingleParamAlgoId,
 | 
											
												
													
														|  |          salt_length: Integer,
 |  |          salt_length: Integer,
 | 
											
												
													
														|  |          trailer_field: Option<Integer>,
 |  |          trailer_field: Option<Integer>,
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      impl RsaSsaPssParams {
 |  |      impl RsaSsaPssParams {
 | 
											
												
													
														|  |          fn new(hash_kind: HashKind) -> Self {
 |  |          fn new(hash_kind: HashKind) -> Self {
 | 
											
												
													
														|  | -            let hash_algorithm = NullTermOid::new(Oid(Bytes::from(hash_kind.oid())));
 |  | 
 | 
											
												
													
														|  | -            let mask_gen_algorithm = AlgoId::new(hash_kind);
 |  | 
 | 
											
												
													
														|  | 
 |  | +            let hash_algorithm = Oid(Bytes::from(hash_kind.oid()));
 | 
											
												
													
														|  | 
 |  | +            let mask_gen_algorithm = SingleParamAlgoId::new(hash_kind);
 | 
											
												
													
														|  |              let salt_length = Integer::from(hash_kind.len() as u64);
 |  |              let salt_length = Integer::from(hash_kind.len() as u64);
 | 
											
												
													
														|  |              Self {
 |  |              Self {
 | 
											
												
													
														|  |                  hash_algorithm,
 |  |                  hash_algorithm,
 | 
											
										
											
												
													
														|  | @@ -135,6 +146,25 @@ mod private {
 | 
											
												
													
														|  |                  ),
 |  |                  ),
 | 
											
												
													
														|  |              )
 |  |              )
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn take_from<S: bcder::decode::Source>(
 | 
											
												
													
														|  | 
 |  | +            cons: &mut Constructed<'_, S>,
 | 
											
												
													
														|  | 
 |  | +        ) -> std::result::Result<Option<Self>, DecodeError<S::Error>> {
 | 
											
												
													
														|  | 
 |  | +            let option = cons.take_opt_value_if(Tag::SEQUENCE, |content| {
 | 
											
												
													
														|  | 
 |  | +                let cons = content.as_constructed()?;
 | 
											
												
													
														|  | 
 |  | +                Ok(RsaSsaPssParams {
 | 
											
												
													
														|  | 
 |  | +                    hash_algorithm: cons.take_constructed_if(Tag::CTX_0, Oid::take_from)?,
 | 
											
												
													
														|  | 
 |  | +                    mask_gen_algorithm: cons
 | 
											
												
													
														|  | 
 |  | +                        .take_constructed_if(Tag::CTX_1, SingleParamAlgoId::take_from)?,
 | 
											
												
													
														|  | 
 |  | +                    salt_length: cons.take_constructed_if(Tag::CTX_2, Integer::take_from)?,
 | 
											
												
													
														|  | 
 |  | +                    trailer_field: cons.take_opt_constructed_if(Tag::CTX_3, Integer::take_from)?,
 | 
											
												
													
														|  | 
 |  | +                })
 | 
											
												
													
														|  | 
 |  | +            })?;
 | 
											
												
													
														|  | 
 |  | +            if option.is_none() {
 | 
											
												
													
														|  | 
 |  | +                cons.take_null()?;
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +            Ok(option)
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      impl RsaSsaPss {
 |  |      impl RsaSsaPss {
 | 
											
										
											
												
													
														|  | @@ -146,7 +176,7 @@ mod private {
 | 
											
												
													
														|  |          /// The OID 1.2.840.113549.1.1.1 (RSA-ES)
 |  |          /// The OID 1.2.840.113549.1.1.1 (RSA-ES)
 | 
											
												
													
														|  |          const RSA_ES_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
 |  |          const RSA_ES_OID: &[u8] = &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01];
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -        const fn oid(&self) -> &'static [u8] {
 |  | 
 | 
											
												
													
														|  | 
 |  | +        const fn oid() -> &'static [u8] {
 | 
											
												
													
														|  |              if Self::USE_PSS_OID {
 |  |              if Self::USE_PSS_OID {
 | 
											
												
													
														|  |                  Self::RSA_PSS_OID
 |  |                  Self::RSA_PSS_OID
 | 
											
												
													
														|  |              } else {
 |  |              } else {
 | 
											
										
											
												
													
														|  | @@ -164,12 +194,33 @@ mod private {
 | 
											
												
													
														|  |                  Ok(None)
 |  |                  Ok(None)
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn from_params(sig_octet_len: u32, params: Option<&[u8]>) -> Result<Self> {
 | 
											
												
													
														|  | 
 |  | +            let key_bits = KeyLen::try_from(sig_octet_len)?;
 | 
											
												
													
														|  | 
 |  | +            let hash_kind = match params {
 | 
											
												
													
														|  | 
 |  | +                Some(params) => {
 | 
											
												
													
														|  | 
 |  | +                    let source = SliceSource::new(params);
 | 
											
												
													
														|  | 
 |  | +                    let params =
 | 
											
												
													
														|  | 
 |  | +                        Constructed::decode(source, Mode::Der, RsaSsaPssParams::take_from)?;
 | 
											
												
													
														|  | 
 |  | +                    if let Some(params) = params {
 | 
											
												
													
														|  | 
 |  | +                        HashKind::from_oid(params.hash_algorithm.as_ref())?
 | 
											
												
													
														|  | 
 |  | +                    } else {
 | 
											
												
													
														|  | 
 |  | +                        HashKind::default()
 | 
											
												
													
														|  | 
 |  | +                    }
 | 
											
												
													
														|  | 
 |  | +                }
 | 
											
												
													
														|  | 
 |  | +                None => HashKind::default(),
 | 
											
												
													
														|  | 
 |  | +            };
 | 
											
												
													
														|  | 
 |  | +            Ok(Self {
 | 
											
												
													
														|  | 
 |  | +                key_bits,
 | 
											
												
													
														|  | 
 |  | +                hash_kind,
 | 
											
												
													
														|  | 
 |  | +            })
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      impl Sign {
 |  |      impl Sign {
 | 
											
												
													
														|  |          const fn oid(&self) -> &'static [u8] {
 |  |          const fn oid(&self) -> &'static [u8] {
 | 
											
												
													
														|  |              match self {
 |  |              match self {
 | 
											
												
													
														|  | -                Sign::RsaSsaPss(inner) => inner.oid(),
 |  | 
 | 
											
												
													
														|  | 
 |  | +                Sign::RsaSsaPss(..) => RsaSsaPss::oid(),
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
										
											
												
													
														|  | @@ -178,36 +229,43 @@ mod private {
 | 
											
												
													
														|  |                  Sign::RsaSsaPss(inner) => inner.params(),
 |  |                  Sign::RsaSsaPss(inner) => inner.params(),
 | 
											
												
													
														|  |              }
 |  |              }
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | -    }
 |  | 
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    fn scheme_to_algo_id(scheme: &Sign) -> Result<AlgorithmIdentifier> {
 |  | 
 | 
											
												
													
														|  | -        Ok(AlgorithmIdentifier {
 |  | 
 | 
											
												
													
														|  | -            algorithm: oid(scheme.oid()),
 |  | 
 | 
											
												
													
														|  | -            parameters: scheme.params()?,
 |  | 
 | 
											
												
													
														|  | -        })
 |  | 
 | 
											
												
													
														|  | -    }
 |  | 
 | 
											
												
													
														|  | 
 |  | +        fn from_der(sig_octet_len: u32, oid: &[u8], params: Option<&[u8]>) -> Result<Sign> {
 | 
											
												
													
														|  | 
 |  | +            if oid == RsaSsaPss::oid() {
 | 
											
												
													
														|  | 
 |  | +                Ok(Sign::RsaSsaPss(RsaSsaPss::from_params(
 | 
											
												
													
														|  | 
 |  | +                    sig_octet_len,
 | 
											
												
													
														|  | 
 |  | +                    params,
 | 
											
												
													
														|  | 
 |  | +                )?))
 | 
											
												
													
														|  | 
 |  | +            } else {
 | 
											
												
													
														|  | 
 |  | +                Err(bterr!("OID does not match a Sign variant"))
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    fn oid(slice: &'static [u8]) -> Oid {
 |  | 
 | 
											
												
													
														|  | -        Oid(Bytes::from(slice))
 |  | 
 | 
											
												
													
														|  | -    }
 |  | 
 | 
											
												
													
														|  | 
 |  | +        fn to_algo_id(self) -> Result<AlgorithmIdentifier> {
 | 
											
												
													
														|  | 
 |  | +            Ok(AlgorithmIdentifier {
 | 
											
												
													
														|  | 
 |  | +                algorithm: oid(self.oid()),
 | 
											
												
													
														|  | 
 |  | +                parameters: self.params()?,
 | 
											
												
													
														|  | 
 |  | +            })
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    macro_rules! bit_string {
 |  | 
 | 
											
												
													
														|  | -        ($bytes:expr) => {
 |  | 
 | 
											
												
													
														|  | -            BitString::new(0, Bytes::from($bytes))
 |  | 
 | 
											
												
													
														|  | -        };
 |  | 
 | 
											
												
													
														|  | 
 |  | +        fn from_algo_id(sig_octet_len: u32, algo_id: &AlgorithmIdentifier) -> Result<Sign> {
 | 
											
												
													
														|  | 
 |  | +            let params = algo_id.parameters.as_ref().map(|e| e.as_ref());
 | 
											
												
													
														|  | 
 |  | +            Sign::from_der(sig_octet_len, algo_id.algorithm.as_ref(), params)
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    /// OID 2.5.29.17 (Subject Alternative Name)
 |  | 
 | 
											
												
													
														|  | -    const SAN_OID: &[u8] = &[0x55, 0x1D, 0x11];
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  |      struct SubjectAltName {
 |  |      struct SubjectAltName {
 | 
											
												
													
														|  | -        issued_to: Ia5String,
 |  | 
 | 
											
												
													
														|  | 
 |  | +        block_path: Utf8String,
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      impl SubjectAltName {
 |  |      impl SubjectAltName {
 | 
											
												
													
														|  | -        fn new(issued_to: String) -> Result<Self> {
 |  | 
 | 
											
												
													
														|  | -            let issued_to = Ia5String::from_string(issued_to).map_err(|err| bterr!("{:?}", err))?;
 |  | 
 | 
											
												
													
														|  | -            Ok(Self { issued_to })
 |  | 
 | 
											
												
													
														|  | 
 |  | +        /// OID 2.5.29.17 (Subject Alternative Name)
 | 
											
												
													
														|  | 
 |  | +        const OID: &[u8] = &[0x55, 0x1D, 0x11];
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn new(block_path: &BlockPath) -> Result<Self> {
 | 
											
												
													
														|  | 
 |  | +            let block_path = Utf8String::from_string(block_path.to_string())
 | 
											
												
													
														|  | 
 |  | +                .map_err(|err| bterr!("{:?}", err))?;
 | 
											
												
													
														|  | 
 |  | +            Ok(Self { block_path })
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          fn encode(&self) -> impl Values + '_ {
 |  |          fn encode(&self) -> impl Values + '_ {
 | 
											
										
											
												
													
														|  | @@ -215,20 +273,35 @@ mod private {
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          fn encode_as(&self, tag: Tag) -> impl Values + '_ {
 |  |          fn encode_as(&self, tag: Tag) -> impl Values + '_ {
 | 
											
												
													
														|  | -            bcder::encode::sequence_as(tag, self.issued_to.encode_ref())
 |  | 
 | 
											
												
													
														|  | 
 |  | +            bcder::encode::sequence_as(tag, self.block_path.encode_ref())
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn take_from<S: bcder::decode::Source>(
 | 
											
												
													
														|  | 
 |  | +            cons: &mut Constructed<'_, S>,
 | 
											
												
													
														|  | 
 |  | +        ) -> std::result::Result<Self, DecodeError<S::Error>> {
 | 
											
												
													
														|  | 
 |  | +            cons.take_sequence(|cons| {
 | 
											
												
													
														|  | 
 |  | +                Ok(Self {
 | 
											
												
													
														|  | 
 |  | +                    block_path: Utf8String::take_from(cons)?,
 | 
											
												
													
														|  | 
 |  | +                })
 | 
											
												
													
														|  | 
 |  | +            })
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          fn encode_der(&self) -> Result<Bytes> {
 |  |          fn encode_der(&self) -> Result<Bytes> {
 | 
											
												
													
														|  |              let mut writer = BytesMut::new().writer();
 |  |              let mut writer = BytesMut::new().writer();
 | 
											
												
													
														|  | -            self.encode().write_encoded(bcder::Mode::Der, &mut writer)?;
 |  | 
 | 
											
												
													
														|  | 
 |  | +            self.encode().write_encoded(Mode::Der, &mut writer)?;
 | 
											
												
													
														|  |              Ok(writer.into_inner().into())
 |  |              Ok(writer.into_inner().into())
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn decode_der<B: AsRef<[u8]>>(bytes: B) -> Result<Self> {
 | 
											
												
													
														|  | 
 |  | +            let source = SliceSource::new(bytes.as_ref());
 | 
											
												
													
														|  | 
 |  | +            Constructed::decode(source, Mode::Der, Self::take_from).map_err(|err| err.into())
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      impl AsymKeyPub<Sign> {
 |  |      impl AsymKeyPub<Sign> {
 | 
											
												
													
														|  |          fn subject_public_key_info(&self) -> Result<SubjectPublicKeyInfo> {
 |  |          fn subject_public_key_info(&self) -> Result<SubjectPublicKeyInfo> {
 | 
											
												
													
														|  |              Ok(SubjectPublicKeyInfo {
 |  |              Ok(SubjectPublicKeyInfo {
 | 
											
												
													
														|  | -                algorithm: scheme_to_algo_id(&self.scheme)?,
 |  | 
 | 
											
												
													
														|  | 
 |  | +                algorithm: self.scheme.to_algo_id()?,
 | 
											
												
													
														|  |                  subject_public_key: self.to_bit_string()?,
 |  |                  subject_public_key: self.to_bit_string()?,
 | 
											
												
													
														|  |              })
 |  |              })
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
										
											
												
													
														|  | @@ -240,6 +313,16 @@ mod private {
 | 
											
												
													
														|  |              Ok(spki.subject_public_key)
 |  |              Ok(spki.subject_public_key)
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +        fn from_subject_public_key_info(
 | 
											
												
													
														|  | 
 |  | +            sig_octet_len: u32,
 | 
											
												
													
														|  | 
 |  | +            spki: &SubjectPublicKeyInfo,
 | 
											
												
													
														|  | 
 |  | +        ) -> Result<Self> {
 | 
											
												
													
														|  | 
 |  | +            let scheme = Sign::from_algo_id(sig_octet_len, &spki.algorithm)?;
 | 
											
												
													
														|  | 
 |  | +            let mut der = Vec::new();
 | 
											
												
													
														|  | 
 |  | +            spki.encode_ref().write_encoded(Mode::Der, &mut der)?;
 | 
											
												
													
														|  | 
 |  | +            AsymKeyPub::new(scheme, der.as_slice())
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |          pub fn to_der(&self) -> Result<Vec<u8>> {
 |  |          pub fn to_der(&self) -> Result<Vec<u8>> {
 | 
											
												
													
														|  |              let spki = self.subject_public_key_info()?;
 |  |              let spki = self.subject_public_key_info()?;
 | 
											
												
													
														|  |              let mut vec = Vec::new();
 |  |              let mut vec = Vec::new();
 | 
											
										
											
												
													
														|  | @@ -248,19 +331,64 @@ mod private {
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    impl Writecap {
 |  | 
 | 
											
												
													
														|  | -        pub fn to_cert_chain(
 |  | 
 | 
											
												
													
														|  | -            &self,
 |  | 
 | 
											
												
													
														|  | -            subject_key: &AsymKeyPub<Sign>,
 |  | 
 | 
											
												
													
														|  | -        ) -> Result<Vec<X509Certificate>> {
 |  | 
 | 
											
												
													
														|  | -            let mut chain = match self.next.as_ref() {
 |  | 
 | 
											
												
													
														|  | -                Some(next) => next.as_ref().to_cert_chain(&self.body.signing_key)?,
 |  | 
 | 
											
												
													
														|  | -                None => Vec::new(),
 |  | 
 | 
											
												
													
														|  | -            };
 |  | 
 | 
											
												
													
														|  | 
 |  | +    trait NameExt {
 | 
											
												
													
														|  | 
 |  | +        fn try_get_common_name(&self) -> Result<&AttributeValue>;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    impl NameExt for Name {
 | 
											
												
													
														|  | 
 |  | +        fn try_get_common_name(&self) -> Result<&AttributeValue> {
 | 
											
												
													
														|  | 
 |  | +            Ok(&self
 | 
											
												
													
														|  | 
 |  | +                .iter_common_name()
 | 
											
												
													
														|  | 
 |  | +                .next()
 | 
											
												
													
														|  | 
 |  | +                .ok_or_else(|| bterr!("no CommonName component in Name"))?
 | 
											
												
													
														|  | 
 |  | +                .value)
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    trait TryAsStr {
 | 
											
												
													
														|  | 
 |  | +        fn try_as_str(&self) -> Result<&str>;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    impl<T: ?Sized + AsRef<[u8]>> TryAsStr for T {
 | 
											
												
													
														|  | 
 |  | +        fn try_as_str(&self) -> Result<&str> {
 | 
											
												
													
														|  | 
 |  | +            std::str::from_utf8(self.as_ref()).map_err(|err| err.into())
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    trait TimeExt {
 | 
											
												
													
														|  | 
 |  | +        fn try_to_epoch(&self) -> Result<Epoch>;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    impl TimeExt for Time {
 | 
											
												
													
														|  | 
 |  | +        fn try_to_epoch(&self) -> Result<Epoch> {
 | 
											
												
													
														|  | 
 |  | +            match self {
 | 
											
												
													
														|  | 
 |  | +                Self::UtcTime(time) => Ok(Epoch::from_value(time.timestamp() as u64)),
 | 
											
												
													
														|  | 
 |  | +                Self::GeneralTime(..) => Err(bterr!("unsupported Time variant encountered")),
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    trait ExtensionsExt {
 | 
											
												
													
														|  | 
 |  | +        fn find_subject_alt_name(&self) -> Result<SubjectAltName>;
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    impl ExtensionsExt for Extensions {
 | 
											
												
													
														|  | 
 |  | +        fn find_subject_alt_name(&self) -> Result<SubjectAltName> {
 | 
											
												
													
														|  | 
 |  | +            let extensions: &[Extension] = self.deref();
 | 
											
												
													
														|  | 
 |  | +            for extension in extensions {
 | 
											
												
													
														|  | 
 |  | +                if extension.id.as_ref() == SubjectAltName::OID {
 | 
											
												
													
														|  | 
 |  | +                    return SubjectAltName::decode_der(extension.value.to_bytes());
 | 
											
												
													
														|  | 
 |  | +                }
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +            Err(bterr!("SubjectAltName not found"))
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | 
 |  | +    impl Writecap {
 | 
											
												
													
														|  | 
 |  | +        pub fn to_cert(&self, subject_key: &AsymKeyPub<Sign>) -> Result<Vec<u8>> {
 | 
											
												
													
														|  |              let version = Some(Version::V3);
 |  |              let version = Some(Version::V3);
 | 
											
												
													
														|  |              let serial_number = CertificateSerialNumber::from(1);
 |  |              let serial_number = CertificateSerialNumber::from(1);
 | 
											
												
													
														|  | -            let signature_algorithm = scheme_to_algo_id(&self.body.signing_key.scheme)?;
 |  | 
 | 
											
												
													
														|  | 
 |  | +            let signature_algorithm = self.body.signing_key.scheme.to_algo_id()?;
 | 
											
												
													
														|  |              let mut issuer = Name::default();
 |  |              let mut issuer = Name::default();
 | 
											
												
													
														|  |              issuer
 |  |              issuer
 | 
											
												
													
														|  |                  .append_common_name_utf8_string(&self.body.signing_key.principal().to_string())
 |  |                  .append_common_name_utf8_string(&self.body.signing_key.principal().to_string())
 | 
											
										
											
												
													
														|  | @@ -282,9 +410,9 @@ mod private {
 | 
											
												
													
														|  |                  .map_err(|_| bterr!("failed to create subject common name"))?;
 |  |                  .map_err(|_| bterr!("failed to create subject common name"))?;
 | 
											
												
													
														|  |              let subject_public_key_info = subject_key.subject_public_key_info()?;
 |  |              let subject_public_key_info = subject_key.subject_public_key_info()?;
 | 
											
												
													
														|  |              let mut extensions = Extensions::default();
 |  |              let mut extensions = Extensions::default();
 | 
											
												
													
														|  | -            let san = SubjectAltName::new(issued_to)?;
 |  | 
 | 
											
												
													
														|  | 
 |  | +            let san = SubjectAltName::new(&self.body.path)?;
 | 
											
												
													
														|  |              extensions.push(Extension {
 |  |              extensions.push(Extension {
 | 
											
												
													
														|  | -                id: oid(SAN_OID),
 |  | 
 | 
											
												
													
														|  | 
 |  | +                id: oid(SubjectAltName::OID),
 | 
											
												
													
														|  |                  critical: Some(false),
 |  |                  critical: Some(false),
 | 
											
												
													
														|  |                  value: OctetString::new(san.encode_der()?),
 |  |                  value: OctetString::new(san.encode_der()?),
 | 
											
												
													
														|  |              });
 |  |              });
 | 
											
										
											
												
													
														|  | @@ -306,17 +434,119 @@ mod private {
 | 
											
												
													
														|  |                  signature_algorithm,
 |  |                  signature_algorithm,
 | 
											
												
													
														|  |                  signature: bit_string!(self.signature.data.clone()),
 |  |                  signature: bit_string!(self.signature.data.clone()),
 | 
											
												
													
														|  |              };
 |  |              };
 | 
											
												
													
														|  | -            chain.push(cert.into());
 |  | 
 | 
											
												
													
														|  | 
 |  | +            let cert: X509Certificate = cert.into();
 | 
											
												
													
														|  | 
 |  | +            cert.encode_der().map_err(|err| err.into())
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        fn to_cert_chain_impl(&self, subject_key: &AsymKeyPub<Sign>) -> Result<Vec<Vec<u8>>> {
 | 
											
												
													
														|  | 
 |  | +            let mut chain = match self.next.as_ref() {
 | 
											
												
													
														|  | 
 |  | +                Some(next) => next.as_ref().to_cert_chain(&self.body.signing_key)?,
 | 
											
												
													
														|  | 
 |  | +                None => {
 | 
											
												
													
														|  | 
 |  | +                    // An extra cert is added to the end of the chain to contain the root
 | 
											
												
													
														|  | 
 |  | +                    // principal's signing key.
 | 
											
												
													
														|  | 
 |  | +                    let mut vec = Vec::with_capacity(2);
 | 
											
												
													
														|  | 
 |  | +                    let root_principal = self.body.signing_key.principal();
 | 
											
												
													
														|  | 
 |  | +                    let path = BlockPath::new(root_principal.clone(), vec![]);
 | 
											
												
													
														|  | 
 |  | +                    let writecap = Writecap {
 | 
											
												
													
														|  | 
 |  | +                        body: WritecapBody {
 | 
											
												
													
														|  | 
 |  | +                            issued_to: root_principal,
 | 
											
												
													
														|  | 
 |  | +                            expires: Epoch::now(),
 | 
											
												
													
														|  | 
 |  | +                            path,
 | 
											
												
													
														|  | 
 |  | +                            signing_key: self.body.signing_key.clone(),
 | 
											
												
													
														|  | 
 |  | +                        },
 | 
											
												
													
														|  | 
 |  | +                        signature: self.signature.clone(),
 | 
											
												
													
														|  | 
 |  | +                        next: None,
 | 
											
												
													
														|  | 
 |  | +                    };
 | 
											
												
													
														|  | 
 |  | +                    vec.push(writecap.to_cert(&self.body.signing_key)?);
 | 
											
												
													
														|  | 
 |  | +                    vec
 | 
											
												
													
														|  | 
 |  | +                }
 | 
											
												
													
														|  | 
 |  | +            };
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            chain.push(self.to_cert(subject_key)?);
 | 
											
												
													
														|  |              Ok(chain)
 |  |              Ok(chain)
 | 
											
												
													
														|  |          }
 |  |          }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        pub fn to_cert_chain(&self, subject_key: &AsymKeyPub<Sign>) -> Result<Vec<Vec<u8>>> {
 | 
											
												
													
														|  | 
 |  | +            let mut chain = self.to_cert_chain_impl(subject_key)?;
 | 
											
												
													
														|  | 
 |  | +            chain.reverse();
 | 
											
												
													
														|  | 
 |  | +            Ok(chain)
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        pub fn from_cert_chain<B: AsRef<[u8]>>(
 | 
											
												
													
														|  | 
 |  | +            chain: &[B],
 | 
											
												
													
														|  | 
 |  | +        ) -> Result<(Writecap, AsymKeyPub<Sign>)> {
 | 
											
												
													
														|  | 
 |  | +            let der = chain
 | 
											
												
													
														|  | 
 |  | +                .first()
 | 
											
												
													
														|  | 
 |  | +                .ok_or_else(|| bterr!("at least one certificate must be supplied"))?;
 | 
											
												
													
														|  | 
 |  | +            let (next, signing_key) = if chain.len() > 1 {
 | 
											
												
													
														|  | 
 |  | +                let (writecap, signing_key) = Self::from_cert_chain(&chain[1..])?;
 | 
											
												
													
														|  | 
 |  | +                // Remove the extra writecap at the end.
 | 
											
												
													
														|  | 
 |  | +                let writecap = if chain.len() == 2 {
 | 
											
												
													
														|  | 
 |  | +                    None
 | 
											
												
													
														|  | 
 |  | +                } else {
 | 
											
												
													
														|  | 
 |  | +                    Some(writecap)
 | 
											
												
													
														|  | 
 |  | +                };
 | 
											
												
													
														|  | 
 |  | +                (writecap, Some(signing_key))
 | 
											
												
													
														|  | 
 |  | +            } else {
 | 
											
												
													
														|  | 
 |  | +                (None, None)
 | 
											
												
													
														|  | 
 |  | +            };
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +            let x509_cert = X509Certificate::from_der(der)?;
 | 
											
												
													
														|  | 
 |  | +            let cert: &Certificate = x509_cert.as_ref();
 | 
											
												
													
														|  | 
 |  | +            if cert.signature.unused() > 0 {
 | 
											
												
													
														|  | 
 |  | +                return Err(bterr!("signature length is not divisible by 8"));
 | 
											
												
													
														|  | 
 |  | +            }
 | 
											
												
													
														|  | 
 |  | +            let extensions = cert
 | 
											
												
													
														|  | 
 |  | +                .tbs_certificate
 | 
											
												
													
														|  | 
 |  | +                .extensions
 | 
											
												
													
														|  | 
 |  | +                .as_ref()
 | 
											
												
													
														|  | 
 |  | +                .ok_or_else(|| bterr!("no extensions present"))?;
 | 
											
												
													
														|  | 
 |  | +            let san = extensions.find_subject_alt_name()?;
 | 
											
												
													
														|  | 
 |  | +            let path = BlockPath::try_from(san.block_path.into_bytes().try_as_str()?)
 | 
											
												
													
														|  | 
 |  | +                .map_err(|err| bterr!(err))?;
 | 
											
												
													
														|  | 
 |  | +            let sig_octet_len: u32 = cert.signature.octet_len().try_into()?;
 | 
											
												
													
														|  | 
 |  | +            let scheme = Sign::from_algo_id(
 | 
											
												
													
														|  | 
 |  | +                sig_octet_len,
 | 
											
												
													
														|  | 
 |  | +                &cert.tbs_certificate.subject_public_key_info.algorithm,
 | 
											
												
													
														|  | 
 |  | +            )?;
 | 
											
												
													
														|  | 
 |  | +            let signature = Signature::new(scheme, cert.signature.octet_bytes().into());
 | 
											
												
													
														|  | 
 |  | +            let cert = &cert.tbs_certificate;
 | 
											
												
													
														|  | 
 |  | +            let subject_key = AsymKeyPub::from_subject_public_key_info(
 | 
											
												
													
														|  | 
 |  | +                sig_octet_len,
 | 
											
												
													
														|  | 
 |  | +                &cert.subject_public_key_info,
 | 
											
												
													
														|  | 
 |  | +            )?;
 | 
											
												
													
														|  | 
 |  | +            let issued_to: Principal = cert
 | 
											
												
													
														|  | 
 |  | +                .subject
 | 
											
												
													
														|  | 
 |  | +                .try_get_common_name()?
 | 
											
												
													
														|  | 
 |  | +                .to_string()?
 | 
											
												
													
														|  | 
 |  | +                .as_str()
 | 
											
												
													
														|  | 
 |  | +                .try_into()?;
 | 
											
												
													
														|  | 
 |  | +            let expires = cert.validity.not_after.try_to_epoch()?;
 | 
											
												
													
														|  | 
 |  | +            // If signing_key is None, then we're at the last certificate in the chain, which is
 | 
											
												
													
														|  | 
 |  | +            // self-signed. So the subject_key is the same as the issuer's signing key.
 | 
											
												
													
														|  | 
 |  | +            let signing_key = signing_key.unwrap_or_else(|| subject_key.clone());
 | 
											
												
													
														|  | 
 |  | +            let writecap = Writecap {
 | 
											
												
													
														|  | 
 |  | +                body: WritecapBody {
 | 
											
												
													
														|  | 
 |  | +                    issued_to,
 | 
											
												
													
														|  | 
 |  | +                    signing_key,
 | 
											
												
													
														|  | 
 |  | +                    path,
 | 
											
												
													
														|  | 
 |  | +                    expires,
 | 
											
												
													
														|  | 
 |  | +                },
 | 
											
												
													
														|  | 
 |  | +                signature,
 | 
											
												
													
														|  | 
 |  | +                next: next.map(Box::new),
 | 
											
												
													
														|  | 
 |  | +            };
 | 
											
												
													
														|  | 
 |  | +            Ok((writecap, subject_key))
 | 
											
												
													
														|  | 
 |  | +        }
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  }
 |  |  }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |  #[cfg(test)]
 |  |  #[cfg(test)]
 | 
											
												
													
														|  |  mod tests {
 |  |  mod tests {
 | 
											
												
													
														|  | 
 |  | +    use super::*;
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  |      use webpki::EndEntityCert;
 |  |      use webpki::EndEntityCert;
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  | -    use crate::test_helpers::node_creds;
 |  | 
 | 
											
												
													
														|  | 
 |  | +    use crate::{crypto::CredsPub, test_helpers::node_creds};
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      #[allow(dead_code)]
 |  |      #[allow(dead_code)]
 | 
											
												
													
														|  |      fn save_first_writecap_der_to_file() {
 |  |      fn save_first_writecap_der_to_file() {
 | 
											
										
											
												
													
														|  | @@ -328,13 +558,7 @@ mod tests {
 | 
											
												
													
														|  |              .to_cert_chain(&node_creds.sign.public)
 |  |              .to_cert_chain(&node_creds.sign.public)
 | 
											
												
													
														|  |              .unwrap();
 |  |              .unwrap();
 | 
											
												
													
														|  |          let first = chain.first().unwrap();
 |  |          let first = chain.first().unwrap();
 | 
											
												
													
														|  | -        let mut buf = Vec::new();
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        first.encode_der_to(&mut buf).unwrap();
 |  | 
 | 
											
												
													
														|  | -        std::fs::write("/tmp/cert.der", buf).unwrap();
 |  | 
 | 
											
												
													
														|  | -
 |  | 
 | 
											
												
													
														|  | -        let pem = first.encode_pem().unwrap();
 |  | 
 | 
											
												
													
														|  | -        std::fs::write("/tmp/cert.pem", &pem).unwrap();
 |  | 
 | 
											
												
													
														|  | 
 |  | +        std::fs::write("/tmp/cert.der", first).unwrap();
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |      #[test]
 |  |      #[test]
 | 
											
										
											
												
													
														|  | @@ -359,10 +583,24 @@ mod tests {
 | 
											
												
													
														|  |              .unwrap()
 |  |              .unwrap()
 | 
											
												
													
														|  |              .to_cert_chain(&node_creds.sign.public)
 |  |              .to_cert_chain(&node_creds.sign.public)
 | 
											
												
													
														|  |              .unwrap();
 |  |              .unwrap();
 | 
											
												
													
														|  | -        let der = chain.first().unwrap().encode_der().unwrap();
 |  | 
 | 
											
												
													
														|  | 
 |  | +        let der = chain.first().unwrap();
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          let result = EndEntityCert::try_from(der.as_slice());
 |  |          let result = EndEntityCert::try_from(der.as_slice());
 | 
											
												
													
														|  |  
 |  |  
 | 
											
												
													
														|  |          result.unwrap();
 |  |          result.unwrap();
 | 
											
												
													
														|  |      }
 |  |      }
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +    #[test]
 | 
											
												
													
														|  | 
 |  | +    fn round_trip_writecap() {
 | 
											
												
													
														|  | 
 |  | +        let node_creds = node_creds();
 | 
											
												
													
														|  | 
 |  | +        let expected_key = node_creds.public_sign();
 | 
											
												
													
														|  | 
 |  | +        let expected_wc = node_creds.writecap.as_ref().unwrap();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        let certs = expected_wc.to_cert_chain(expected_key).unwrap();
 | 
											
												
													
														|  | 
 |  | +        let (actual_wc, actual_key) = Writecap::from_cert_chain(certs.as_slice()).unwrap();
 | 
											
												
													
														|  | 
 |  | +
 | 
											
												
													
														|  | 
 |  | +        assert_eq!(expected_key, &actual_key);
 | 
											
												
													
														|  | 
 |  | +        assert_eq!(expected_wc, &actual_wc);
 | 
											
												
													
														|  | 
 |  | +        actual_wc.assert_valid_for(&expected_wc.body.path).unwrap();
 | 
											
												
													
														|  | 
 |  | +    }
 | 
											
												
													
														|  |  }
 |  |  }
 |