chuchi_crypto/signature/
keypair.rs1use 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 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}