Skip to main content

chuchi_crypto/signature/
keypair.rs

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