fire_crypto/signature/
keypair.rs

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