chuchi_crypto/cipher/
keypair.rs1use 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
15pub 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 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#[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 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#[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}