#![doc(
html_logo_url = "https://avatars3.githubusercontent.com/u/40766087?u=0267cf8b7fe892bbf35b6114d9eb48adc057d6ff",
html_root_url = "https://docs.rs/cryptouri/0.4.0"
)]
#![forbid(unsafe_code)]
#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
#[macro_use]
mod encoding;
#[macro_use]
pub mod error;
pub mod algorithm;
pub mod hash;
mod parts;
pub mod public_key;
pub mod secret_key;
pub mod signature;
pub use crate::{
encoding::Encodable,
error::{Error, ErrorKind},
hash::Hash,
public_key::PublicKey,
secret_key::SecretKey,
signature::Signature,
};
use crate::{
encoding::{Encoding, DASHERIZED_ENCODING, URI_ENCODING},
parts::Parts,
};
use anomaly::fail;
use secrecy::{ExposeSecret, SecretString};
pub struct CryptoUri {
kind: CryptoUriKind,
fragment: Option<SecretString>,
}
pub enum CryptoUriKind {
Hash(Hash),
PublicKey(PublicKey),
SecretKey(SecretKey),
Signature(Signature),
}
impl CryptoUri {
fn parse(uri: &str, encoding: &Encoding) -> Result<Self, Error> {
let parts = Parts::decode(uri, encoding)?;
let kind = if parts.prefix.starts_with(encoding.hash_scheme) {
CryptoUriKind::Hash(Hash::new(
&parts.prefix[encoding.hash_scheme.len()..],
parts.data.expose_secret(),
)?)
} else if parts.prefix.starts_with(encoding.public_key_scheme) {
CryptoUriKind::PublicKey(PublicKey::new(
&parts.prefix[encoding.public_key_scheme.len()..],
parts.data.expose_secret(),
)?)
} else if parts.prefix.starts_with(encoding.secret_key_scheme) {
let alg_id = &parts.prefix[encoding.secret_key_scheme.len()..];
if alg_id.contains(encoding.combine) {
let alg_ids = alg_id.split(encoding.combine).collect::<Vec<_>>();
CryptoUriKind::SecretKey(SecretKey::new_combination(
&alg_ids,
parts.data.expose_secret(),
)?)
} else {
CryptoUriKind::SecretKey(SecretKey::new(alg_id, parts.data.expose_secret())?)
}
} else if parts.prefix.starts_with(encoding.signature_scheme) {
CryptoUriKind::Signature(Signature::new(
&parts.prefix[encoding.signature_scheme.len()..],
parts.data.expose_secret(),
)?)
} else {
fail!(
ErrorKind::SchemeInvalid,
"unknown CryptoURI prefix: {}",
parts.prefix
)
};
Ok(Self {
kind,
fragment: parts.fragment,
})
}
pub fn parse_uri(uri: &str) -> Result<Self, Error> {
Self::parse(uri, URI_ENCODING)
}
pub fn parse_dasherized(token: &str) -> Result<Self, Error> {
Self::parse(token, DASHERIZED_ENCODING)
}
pub fn kind(&self) -> &CryptoUriKind {
&self.kind
}
pub fn secret_key(&self) -> Option<&SecretKey> {
match self.kind {
CryptoUriKind::SecretKey(ref key) => Some(key),
_ => None,
}
}
pub fn is_secret_key(&self) -> bool {
self.secret_key().is_some()
}
pub fn public_key(&self) -> Option<&PublicKey> {
match self.kind {
CryptoUriKind::PublicKey(ref key) => Some(key),
_ => None,
}
}
pub fn is_public_key(&self) -> bool {
self.public_key().is_some()
}
pub fn hash(&self) -> Option<&Hash> {
match self.kind {
CryptoUriKind::Hash(ref hash) => Some(hash),
_ => None,
}
}
pub fn is_hash(&self) -> bool {
self.hash().is_some()
}
pub fn signature(&self) -> Option<&Signature> {
match self.kind {
CryptoUriKind::Signature(ref sig) => Some(sig),
_ => None,
}
}
pub fn is_signature(&self) -> bool {
self.signature().is_some()
}
pub fn fragment(&self) -> Option<&str> {
self.fragment
.as_ref()
.map(|fragment| fragment.expose_secret().as_ref())
}
}
impl Encodable for CryptoUri {
fn to_uri_string(&self) -> String {
match self.kind {
CryptoUriKind::Hash(ref hash) => hash.to_uri_string(),
CryptoUriKind::PublicKey(ref pk) => pk.to_uri_string(),
CryptoUriKind::SecretKey(ref sk) => sk.to_uri_string(),
CryptoUriKind::Signature(ref sig) => sig.to_uri_string(),
}
}
fn to_dasherized_string(&self) -> String {
match self.kind {
CryptoUriKind::Hash(ref hash) => hash.to_dasherized_string(),
CryptoUriKind::PublicKey(ref pk) => pk.to_dasherized_string(),
CryptoUriKind::SecretKey(ref sk) => sk.to_dasherized_string(),
CryptoUriKind::Signature(ref sig) => sig.to_dasherized_string(),
}
}
}