fire_crypto/signature/
public_key.rs

1use super::Signature;
2#[cfg(feature = "b64")]
3use crate::error::DecodeError;
4use crate::error::TryFromError;
5
6use ed25519_dalek as ed;
7
8use std::convert::{TryFrom, TryInto};
9use std::fmt;
10use std::hash::{Hash, Hasher};
11
12#[cfg(feature = "b64")]
13use base64::engine::{general_purpose::URL_SAFE_NO_PAD, Engine};
14
15#[derive(Clone, PartialEq, Eq)]
16#[repr(transparent)]
17pub struct PublicKey {
18	inner: ed::VerifyingKey,
19}
20
21impl PublicKey {
22	pub const LEN: usize = 32;
23
24	pub(crate) fn from_ref(inner: &ed::VerifyingKey) -> &Self {
25		// This is safe because PublicKey is transparent
26		unsafe { &*(inner as *const _ as *const _) }
27	}
28
29	pub(crate) fn from_raw(inner: ed::VerifyingKey) -> Self {
30		Self { inner }
31	}
32
33	/// ## Panics
34	/// if the slice is not 32 bytes long.
35	pub fn from_slice(slice: &[u8]) -> Self {
36		slice.try_into().unwrap()
37	}
38
39	pub fn to_bytes(&self) -> [u8; 32] {
40		self.inner.to_bytes()
41	}
42
43	pub fn verify(&self, msg: impl AsRef<[u8]>, signature: &Signature) -> bool {
44		self.inner
45			.verify_strict(msg.as_ref(), signature.inner())
46			.is_ok()
47	}
48}
49
50#[cfg(not(feature = "b64"))]
51impl fmt::Debug for PublicKey {
52	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53		f.debug_tuple("PublicKey").field(&self.as_ref()).finish()
54	}
55}
56
57#[cfg(feature = "b64")]
58impl fmt::Debug for PublicKey {
59	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60		f.debug_tuple("PublicKey").field(&self.to_string()).finish()
61	}
62}
63
64#[cfg(feature = "b64")]
65impl fmt::Display for PublicKey {
66	fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67		base64::display::Base64Display::new(self.as_ref(), &URL_SAFE_NO_PAD)
68			.fmt(f)
69	}
70}
71
72impl Hash for PublicKey {
73	fn hash<H: Hasher>(&self, state: &mut H) {
74		self.as_ref().hash(state)
75	}
76}
77
78impl TryFrom<&[u8]> for PublicKey {
79	type Error = TryFromError;
80
81	fn try_from(v: &[u8]) -> Result<Self, Self::Error> {
82		ed::VerifyingKey::try_from(v)
83			.map_err(TryFromError::from_any)
84			.map(Self::from_raw)
85	}
86}
87
88#[cfg(feature = "b64")]
89impl crate::FromStr for PublicKey {
90	type Err = DecodeError;
91
92	fn from_str(s: &str) -> Result<Self, Self::Err> {
93		if s.len() != crate::calculate_b64_len(Self::LEN) {
94			return Err(DecodeError::InvalidLength);
95		}
96
97		let mut bytes = [0u8; Self::LEN];
98		URL_SAFE_NO_PAD
99			.decode_slice_unchecked(s, &mut bytes)
100			.map_err(DecodeError::inv_bytes)
101			.and_then(|_| {
102				Self::try_from(bytes.as_ref()).map_err(DecodeError::inv_bytes)
103			})
104	}
105}
106
107impl AsRef<[u8]> for PublicKey {
108	fn as_ref(&self) -> &[u8] {
109		self.inner.as_bytes()
110	}
111}
112
113#[cfg(all(feature = "b64", feature = "serde"))]
114mod impl_serde {
115
116	use super::*;
117
118	use std::borrow::Cow;
119	use std::str::FromStr;
120
121	use _serde::de::Error;
122	use _serde::{Deserialize, Deserializer, Serialize, Serializer};
123
124	impl Serialize for PublicKey {
125		fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
126		where
127			S: Serializer,
128		{
129			serializer.collect_str(&self)
130		}
131	}
132
133	impl<'de> Deserialize<'de> for PublicKey {
134		fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135		where
136			D: Deserializer<'de>,
137		{
138			let s: Cow<'_, str> = Deserialize::deserialize(deserializer)?;
139			Self::from_str(s.as_ref()).map_err(D::Error::custom)
140		}
141	}
142}
143
144#[cfg(feature = "protobuf")]
145mod impl_protobuf {
146	use super::*;
147
148	use fire_protobuf::{
149		bytes::BytesWrite,
150		decode::{DecodeError, DecodeMessage, FieldKind},
151		encode::{
152			EncodeError, EncodeMessage, FieldOpt, MessageEncoder, SizeBuilder,
153		},
154		WireType,
155	};
156
157	impl EncodeMessage for PublicKey {
158		const WIRE_TYPE: WireType = WireType::Len;
159
160		fn is_default(&self) -> bool {
161			false
162		}
163
164		fn encoded_size(
165			&mut self,
166			field: Option<FieldOpt>,
167			builder: &mut SizeBuilder,
168		) -> Result<(), EncodeError> {
169			self.as_ref().encoded_size(field, builder)
170		}
171
172		fn encode<B>(
173			&mut self,
174			field: Option<FieldOpt>,
175			encoder: &mut MessageEncoder<B>,
176		) -> Result<(), EncodeError>
177		where
178			B: BytesWrite,
179		{
180			self.as_ref().encode(field, encoder)
181		}
182	}
183
184	impl<'m> DecodeMessage<'m> for PublicKey {
185		const WIRE_TYPE: WireType = WireType::Len;
186
187		fn decode_default() -> Self {
188			Self {
189				inner: Default::default(),
190			}
191		}
192
193		fn merge(
194			&mut self,
195			kind: FieldKind<'m>,
196			is_field: bool,
197		) -> Result<(), DecodeError> {
198			let mut t = self.to_bytes();
199			t.merge(kind, is_field)?;
200
201			self.inner = ed::VerifyingKey::from_bytes(&t)
202				.map_err(|e| DecodeError::Other(e.to_string()))?;
203
204			Ok(())
205		}
206	}
207}
208
209#[cfg(all(feature = "b64", feature = "postgres"))]
210mod impl_postgres {
211	use super::*;
212
213	use bytes::BytesMut;
214	use postgres_types::{to_sql_checked, FromSql, IsNull, ToSql, Type};
215
216	impl ToSql for PublicKey {
217		fn to_sql(
218			&self,
219			ty: &Type,
220			out: &mut BytesMut,
221		) -> Result<IsNull, Box<dyn std::error::Error + Sync + Send>>
222		where
223			Self: Sized,
224		{
225			self.to_string().to_sql(ty, out)
226		}
227
228		fn accepts(ty: &Type) -> bool
229		where
230			Self: Sized,
231		{
232			<&str as ToSql>::accepts(ty)
233		}
234
235		to_sql_checked!();
236	}
237
238	impl<'r> FromSql<'r> for PublicKey {
239		fn from_sql(
240			ty: &Type,
241			raw: &'r [u8],
242		) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
243			let s = <&str as FromSql>::from_sql(ty, raw)?;
244			s.parse().map_err(Into::into)
245		}
246
247		fn accepts(ty: &Type) -> bool {
248			<&str as FromSql>::accepts(ty)
249		}
250	}
251}