Skip to main content

chuchi_crypto/cipher/
keypair.rs

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