1#![doc(
5 html_logo_url = "https://avatars3.githubusercontent.com/u/40766087?u=0267cf8b7fe892bbf35b6114d9eb48adc057d6ff",
6 html_root_url = "https://docs.rs/cryptouri/0.4.0"
7)]
8#![forbid(unsafe_code)]
9#![warn(missing_docs, rust_2018_idioms, unused_qualifications)]
10
11#[macro_use]
12mod encoding;
13#[macro_use]
14pub mod error;
15
16pub mod algorithm;
17pub mod hash;
18mod parts;
19pub mod public_key;
20pub mod secret_key;
21pub mod signature;
22
23pub use crate::{
24 encoding::Encodable,
25 error::{Error, ErrorKind},
26 hash::Hash,
27 public_key::PublicKey,
28 secret_key::SecretKey,
29 signature::Signature,
30};
31
32use crate::{
33 encoding::{Encoding, DASHERIZED_ENCODING, URI_ENCODING},
34 parts::Parts,
35};
36use anomaly::fail;
37use secrecy::{ExposeSecret, SecretString};
38
39pub struct CryptoUri {
41 kind: CryptoUriKind,
43
44 fragment: Option<SecretString>,
46}
47
48pub enum CryptoUriKind {
50 Hash(Hash),
52
53 PublicKey(PublicKey),
55
56 SecretKey(SecretKey),
58
59 Signature(Signature),
61}
62
63impl CryptoUri {
64 fn parse(uri: &str, encoding: &Encoding) -> Result<Self, Error> {
67 let parts = Parts::decode(uri, encoding)?;
68
69 let kind = if parts.prefix.starts_with(encoding.hash_scheme) {
70 CryptoUriKind::Hash(Hash::new(
71 &parts.prefix[encoding.hash_scheme.len()..],
72 parts.data.expose_secret(),
73 )?)
74 } else if parts.prefix.starts_with(encoding.public_key_scheme) {
75 CryptoUriKind::PublicKey(PublicKey::new(
76 &parts.prefix[encoding.public_key_scheme.len()..],
77 parts.data.expose_secret(),
78 )?)
79 } else if parts.prefix.starts_with(encoding.secret_key_scheme) {
80 let alg_id = &parts.prefix[encoding.secret_key_scheme.len()..];
81
82 if alg_id.contains(encoding.combine) {
83 let alg_ids = alg_id.split(encoding.combine).collect::<Vec<_>>();
85 CryptoUriKind::SecretKey(SecretKey::new_combination(
86 &alg_ids,
87 parts.data.expose_secret(),
88 )?)
89 } else {
90 CryptoUriKind::SecretKey(SecretKey::new(alg_id, parts.data.expose_secret())?)
91 }
92 } else if parts.prefix.starts_with(encoding.signature_scheme) {
93 CryptoUriKind::Signature(Signature::new(
94 &parts.prefix[encoding.signature_scheme.len()..],
95 parts.data.expose_secret(),
96 )?)
97 } else {
98 fail!(
99 ErrorKind::SchemeInvalid,
100 "unknown CryptoURI prefix: {}",
101 parts.prefix
102 )
103 };
104
105 Ok(Self {
106 kind,
107 fragment: parts.fragment,
108 })
109 }
110
111 pub fn parse_uri(uri: &str) -> Result<Self, Error> {
113 Self::parse(uri, URI_ENCODING)
114 }
115
116 pub fn parse_dasherized(token: &str) -> Result<Self, Error> {
118 Self::parse(token, DASHERIZED_ENCODING)
119 }
120
121 pub fn kind(&self) -> &CryptoUriKind {
123 &self.kind
124 }
125
126 pub fn secret_key(&self) -> Option<&SecretKey> {
128 match self.kind {
129 CryptoUriKind::SecretKey(ref key) => Some(key),
130 _ => None,
131 }
132 }
133
134 pub fn is_secret_key(&self) -> bool {
136 self.secret_key().is_some()
137 }
138
139 pub fn public_key(&self) -> Option<&PublicKey> {
141 match self.kind {
142 CryptoUriKind::PublicKey(ref key) => Some(key),
143 _ => None,
144 }
145 }
146
147 pub fn is_public_key(&self) -> bool {
149 self.public_key().is_some()
150 }
151
152 pub fn hash(&self) -> Option<&Hash> {
154 match self.kind {
155 CryptoUriKind::Hash(ref hash) => Some(hash),
156 _ => None,
157 }
158 }
159
160 pub fn is_hash(&self) -> bool {
162 self.hash().is_some()
163 }
164
165 pub fn signature(&self) -> Option<&Signature> {
167 match self.kind {
168 CryptoUriKind::Signature(ref sig) => Some(sig),
169 _ => None,
170 }
171 }
172
173 pub fn is_signature(&self) -> bool {
175 self.signature().is_some()
176 }
177
178 pub fn fragment(&self) -> Option<&str> {
180 self.fragment
181 .as_ref()
182 .map(|fragment| fragment.expose_secret().as_ref())
183 }
184}
185
186impl Encodable for CryptoUri {
187 fn to_uri_string(&self) -> String {
189 match self.kind {
190 CryptoUriKind::Hash(ref hash) => hash.to_uri_string(),
191 CryptoUriKind::PublicKey(ref pk) => pk.to_uri_string(),
192 CryptoUriKind::SecretKey(ref sk) => sk.to_uri_string(),
193 CryptoUriKind::Signature(ref sig) => sig.to_uri_string(),
194 }
195 }
196
197 fn to_dasherized_string(&self) -> String {
199 match self.kind {
200 CryptoUriKind::Hash(ref hash) => hash.to_dasherized_string(),
201 CryptoUriKind::PublicKey(ref pk) => pk.to_dasherized_string(),
202 CryptoUriKind::SecretKey(ref sk) => sk.to_dasherized_string(),
203 CryptoUriKind::Signature(ref sig) => sig.to_dasherized_string(),
204 }
205 }
206}