use crate::{error, Error, Result, SubjectPublicKeyInfo};
use alloc::{borrow::ToOwned, vec::Vec};
use core::{
convert::{TryFrom, TryInto},
fmt,
};
use der::Encodable;
#[cfg(feature = "std")]
use std::{fs, path::Path, str};
#[cfg(feature = "pem")]
use {
crate::{pem, LineEnding},
alloc::string::String,
core::str::FromStr,
};
#[cfg(feature = "pem")]
pub(crate) const PEM_TYPE_LABEL: &str = "PUBLIC KEY";
#[derive(Clone)]
#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
pub struct PublicKeyDocument(Vec<u8>);
impl PublicKeyDocument {
pub fn spki(&self) -> SubjectPublicKeyInfo<'_> {
SubjectPublicKeyInfo::try_from(self.0.as_slice()).expect("malformed PublicKeyDocument")
}
pub fn from_der(bytes: &[u8]) -> Result<Self> {
bytes.try_into()
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
pub fn from_pem(s: &str) -> Result<Self> {
let (label, der_bytes) = pem::decode_vec(s.as_bytes())?;
if label != PEM_TYPE_LABEL {
return Err(pem::Error::Label.into());
}
Self::from_der(&*der_bytes)
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
pub fn to_pem(&self) -> String {
self.to_pem_with_le(LineEnding::default())
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
pub fn to_pem_with_le(&self, line_ending: LineEnding) -> String {
pem::encode_string(PEM_TYPE_LABEL, line_ending, &self.0).expect(error::PEM_ENCODING_MSG)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn read_der_file(path: impl AsRef<Path>) -> Result<Self> {
fs::read(path)?.try_into()
}
#[cfg(all(feature = "pem", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn read_pem_file(path: impl AsRef<Path>) -> Result<Self> {
Self::from_pem(&fs::read_to_string(path)?)
}
#[cfg(feature = "std")]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn write_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
fs::write(path, self.as_ref())?;
Ok(())
}
#[cfg(all(feature = "pem", feature = "std"))]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
pub fn write_pem_file(&self, path: impl AsRef<Path>) -> Result<()> {
fs::write(path, self.to_pem().as_bytes())?;
Ok(())
}
}
impl AsRef<[u8]> for PublicKeyDocument {
fn as_ref(&self) -> &[u8] {
self.0.as_ref()
}
}
impl From<SubjectPublicKeyInfo<'_>> for PublicKeyDocument {
fn from(spki: SubjectPublicKeyInfo<'_>) -> PublicKeyDocument {
PublicKeyDocument::from(&spki)
}
}
impl From<&SubjectPublicKeyInfo<'_>> for PublicKeyDocument {
fn from(spki: &SubjectPublicKeyInfo<'_>) -> PublicKeyDocument {
spki.to_vec()
.ok()
.and_then(|buf| buf.try_into().ok())
.expect(error::DER_ENCODING_MSG)
}
}
impl TryFrom<&[u8]> for PublicKeyDocument {
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
SubjectPublicKeyInfo::try_from(bytes)?;
Ok(Self(bytes.to_owned()))
}
}
impl TryFrom<Vec<u8>> for PublicKeyDocument {
type Error = Error;
fn try_from(bytes: Vec<u8>) -> Result<Self> {
SubjectPublicKeyInfo::try_from(bytes.as_slice())?;
Ok(Self(bytes))
}
}
impl fmt::Debug for PublicKeyDocument {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_tuple("PublicKeyDocument")
.field(&self.spki())
.finish()
}
}
#[cfg(feature = "pem")]
#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
impl FromStr for PublicKeyDocument {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_pem(s)
}
}