fire_crypto/cipher/
public_key.rs

1#[cfg(feature = "b64")]
2use crate::error::DecodeError;
3use crate::error::TryFromError;
4
5use std::convert::{TryFrom, TryInto};
6use std::hash::{Hash, Hasher};
7use std::{cmp, fmt};
8
9use x25519_dalek as x;
10
11#[cfg(feature = "b64")]
12use base64::engine::{general_purpose::URL_SAFE_NO_PAD, Engine};
13
14#[derive(Clone)]
15pub struct PublicKey {
16	pub inner: x::PublicKey,
17}
18
19impl PublicKey {
20	pub const LEN: usize = 32;
21
22	pub(crate) fn from_ephemeral_secret(secret: &x::EphemeralSecret) -> Self {
23		Self {
24			inner: secret.into(),
25		}
26	}
27
28	pub(crate) fn from_static_secret(secret: &x::StaticSecret) -> Self {
29		Self {
30			inner: secret.into(),
31		}
32	}
33
34	/// ## Panics
35	/// if the slice is not 32 bytes long.
36	pub fn from_slice(slice: &[u8]) -> Self {
37		slice.try_into().unwrap()
38	}
39
40	pub fn to_bytes(&self) -> [u8; 32] {
41		self.as_ref().try_into().unwrap()
42	}
43
44	pub fn inner(&self) -> &x::PublicKey {
45		&self.inner
46	}
47}
48
49#[cfg(not(feature = "b64"))]
50impl fmt::Debug for PublicKey {
51	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52		f.debug_tuple("PublicKey").field(&self.as_ref()).finish()
53	}
54}
55
56#[cfg(feature = "b64")]
57impl fmt::Debug for PublicKey {
58	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59		f.debug_tuple("PublicKey").field(&self.to_string()).finish()
60	}
61}
62
63#[cfg(feature = "b64")]
64impl fmt::Display for PublicKey {
65	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66		base64::display::Base64Display::new(self.as_ref(), &URL_SAFE_NO_PAD)
67			.fmt(f)
68	}
69}
70
71impl cmp::PartialEq for PublicKey {
72	fn eq(&self, other: &PublicKey) -> bool {
73		self.as_ref() == other.as_ref()
74	}
75}
76
77impl cmp::Eq for PublicKey {}
78
79impl Hash for PublicKey {
80	fn hash<H: Hasher>(&self, state: &mut H) {
81		self.as_ref().hash(state)
82	}
83}
84
85impl From<[u8; 32]> for PublicKey {
86	fn from(bytes: [u8; 32]) -> Self {
87		Self {
88			inner: bytes.into(),
89		}
90	}
91}
92
93impl TryFrom<&[u8]> for PublicKey {
94	type Error = TryFromError;
95
96	fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
97		<[u8; 32]>::try_from(v)
98			.map_err(TryFromError::from_any)
99			.map(Self::from)
100	}
101}
102
103#[cfg(feature = "b64")]
104impl crate::FromStr for PublicKey {
105	type Err = DecodeError;
106
107	fn from_str(s: &str) -> Result<Self, Self::Err> {
108		if s.len() != crate::calculate_b64_len(Self::LEN) {
109			return Err(DecodeError::InvalidLength);
110		}
111
112		let mut bytes = [0u8; Self::LEN];
113		URL_SAFE_NO_PAD
114			.decode_slice_unchecked(s, &mut bytes)
115			.map_err(DecodeError::inv_bytes)
116			.and_then(|_| {
117				Self::try_from(bytes.as_ref()).map_err(DecodeError::inv_bytes)
118			})
119	}
120}
121
122impl AsRef<[u8]> for PublicKey {
123	fn as_ref(&self) -> &[u8] {
124		self.inner.as_bytes()
125	}
126}
127
128#[cfg(all(feature = "b64", feature = "serde"))]
129mod impl_serde {
130
131	use super::*;
132
133	use std::borrow::Cow;
134	use std::str::FromStr;
135
136	use _serde::de::Error;
137	use _serde::{Deserialize, Deserializer, Serialize, Serializer};
138
139	impl Serialize for PublicKey {
140		fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
141		where
142			S: Serializer,
143		{
144			serializer.collect_str(&self)
145		}
146	}
147
148	impl<'de> Deserialize<'de> for PublicKey {
149		fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
150		where
151			D: Deserializer<'de>,
152		{
153			let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
154			Self::from_str(s.as_ref()).map_err(D::Error::custom)
155		}
156	}
157}
158
159#[cfg(all(feature = "b64", feature = "postgres"))]
160mod impl_postgres {
161	use super::*;
162
163	use bytes::BytesMut;
164	use postgres_types::{to_sql_checked, FromSql, IsNull, ToSql, Type};
165
166	impl ToSql for PublicKey {
167		fn to_sql(
168			&self,
169			ty: &Type,
170			out: &mut BytesMut,
171		) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
172		where
173			Self: Sized,
174		{
175			self.to_string().to_sql(ty, out)
176		}
177
178		fn accepts(ty: &Type) -> bool
179		where
180			Self: Sized,
181		{
182			<&str as ToSql>::accepts(ty)
183		}
184
185		to_sql_checked!();
186	}
187
188	impl<'r> FromSql<'r> for PublicKey {
189		fn from_sql(
190			ty: &Type,
191			raw: &'r [u8],
192		) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
193			let s = <&str as FromSql>::from_sql(ty, raw)?;
194			s.parse().map_err(Into::into)
195		}
196
197		fn accepts(ty: &Type) -> bool {
198			<&str as FromSql>::accepts(ty)
199		}
200	}
201}