1use crate::crypto::{Digest, Hashable};
2use ed25519_dalek::{self as ext, Signer, Verifier};
3use rand::{rngs::OsRng, CryptoRng, Rng};
4use serde::{Deserialize, Serialize};
5use sqlx::{
6 sqlite::{SqliteArgumentValue, SqliteTypeInfo, SqliteValueRef},
7 Sqlite,
8};
9use std::{cmp::Ordering, fmt, str::FromStr};
10use thiserror::Error;
11
12#[derive(Serialize, Deserialize)]
13#[repr(transparent)]
14#[serde(transparent)]
15pub struct Keypair(ext::SigningKey);
16
17impl Keypair {
18 pub const SECRET_KEY_SIZE: usize = ext::SECRET_KEY_LENGTH;
19
20 pub fn generate<R: Rng + CryptoRng>(rng: &mut R) -> Self {
21 Self(ext::SigningKey::generate(rng))
22 }
23
24 pub fn random() -> Self {
25 Self::generate(&mut OsRng)
26 }
27
28 pub fn to_bytes(&self) -> [u8; Self::SECRET_KEY_SIZE] {
29 self.0.to_bytes()
30 }
31
32 pub fn public_key(&self) -> PublicKey {
33 PublicKey(self.0.verifying_key())
34 }
35
36 pub fn sign(&self, msg: &[u8]) -> Signature {
37 Signature(self.0.sign(msg))
38 }
39}
40
41impl From<&'_ [u8; Self::SECRET_KEY_SIZE]> for Keypair {
42 fn from(bytes: &'_ [u8; Self::SECRET_KEY_SIZE]) -> Self {
43 Self(ext::SigningKey::from(bytes))
44 }
45}
46
47impl TryFrom<&'_ [u8]> for Keypair {
48 type Error = SignatureError;
49
50 fn try_from(bytes: &'_ [u8]) -> Result<Self, Self::Error> {
51 Ok(Self(ext::SigningKey::try_from(bytes)?))
52 }
53}
54
55impl fmt::Debug for Keypair {
56 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
57 f.debug_struct("Keypair")
58 .field("public_key", &self.public_key())
59 .finish_non_exhaustive()
60 }
61}
62
63#[derive(PartialEq, Eq, Hash, Clone, Copy, Deserialize, Serialize)]
64#[repr(transparent)]
65#[serde(transparent)]
66pub struct PublicKey(ext::VerifyingKey);
67
68impl PublicKey {
69 pub const SIZE: usize = ext::PUBLIC_KEY_LENGTH;
70
71 #[cfg(test)]
72 pub fn generate<R: Rng + CryptoRng>(rng: &mut R) -> Self {
73 Keypair::generate(rng).public_key()
74 }
75
76 #[cfg(test)]
77 pub fn random() -> Self {
78 Self::generate(&mut OsRng)
79 }
80
81 pub fn verify(&self, msg: &[u8], signature: &Signature) -> bool {
82 self.0.verify(msg, &signature.0).is_ok()
83 }
84
85 pub fn starts_with(&self, needle: &[u8]) -> bool {
86 self.0.as_ref().starts_with(needle)
87 }
88}
89
90impl PartialOrd for PublicKey {
91 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
92 Some(self.cmp(other))
93 }
94}
95
96impl Ord for PublicKey {
97 fn cmp(&self, other: &Self) -> Ordering {
98 self.0.as_bytes().cmp(other.0.as_bytes())
99 }
100}
101
102impl fmt::LowerHex for PublicKey {
103 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
104 hex_fmt::HexFmt(self.0.as_bytes()).fmt(f)
105 }
106}
107
108impl Hashable for PublicKey {
109 fn update_hash<S: Digest>(&self, state: &mut S) {
110 self.0.as_bytes().update_hash(state)
111 }
112}
113
114impl AsRef<[u8]> for PublicKey {
115 fn as_ref(&self) -> &[u8] {
116 self.0.as_bytes()
117 }
118}
119
120impl TryFrom<&'_ [u8]> for PublicKey {
121 type Error = ext::SignatureError;
122
123 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
124 Ok(Self(bytes.try_into()?))
125 }
126}
127
128impl From<PublicKey> for [u8; PublicKey::SIZE] {
129 fn from(key: PublicKey) -> Self {
130 key.0.to_bytes()
131 }
132}
133
134impl fmt::Display for PublicKey {
135 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
136 write!(f, "{self:x}")
137 }
138}
139
140impl fmt::Debug for PublicKey {
141 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
142 write!(f, "{self:<8x}")
143 }
144}
145
146impl FromStr for PublicKey {
147 type Err = ParseError;
148
149 fn from_str(s: &str) -> Result<Self, Self::Err> {
150 let mut bytes = [0; Self::SIZE];
151 hex::decode_to_slice(s, &mut bytes).map_err(|_| ParseError)?;
152 Self::try_from(&bytes[..]).map_err(|_| ParseError)
153 }
154}
155
156#[derive(Debug, Error)]
157#[error("failed to parse public key")]
158pub struct ParseError;
159
160derive_sqlx_traits_for_byte_array_wrapper!(PublicKey);
161
162#[cfg(test)]
163mod test_utils {
164 use super::{Keypair, PublicKey};
165 use proptest::{
166 arbitrary::{any, Arbitrary},
167 array::UniformArrayStrategy,
168 num,
169 strategy::{Map, NoShrink, Strategy},
170 };
171
172 impl Arbitrary for Keypair {
173 type Parameters = ();
174 type Strategy = Map<
175 NoShrink<UniformArrayStrategy<num::u8::Any, [u8; Keypair::SECRET_KEY_SIZE]>>,
176 fn([u8; Keypair::SECRET_KEY_SIZE]) -> Self,
177 >;
178
179 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
180 any::<[u8; Keypair::SECRET_KEY_SIZE]>()
181 .no_shrink()
182 .prop_map(|array| Keypair::from(&array))
183 }
184 }
185
186 impl Arbitrary for PublicKey {
187 type Parameters = ();
188 type Strategy = Map<<Keypair as Arbitrary>::Strategy, fn(Keypair) -> Self>;
189
190 fn arbitrary_with(_: Self::Parameters) -> Self::Strategy {
191 any::<Keypair>().prop_map(|keypair| keypair.public_key())
192 }
193 }
194}
195
196#[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize)]
197#[repr(transparent)]
198pub struct Signature(ext::Signature);
199
200impl Signature {
201 pub const SIZE: usize = ext::SIGNATURE_LENGTH;
202
203 pub fn to_bytes(&self) -> [u8; Self::SIZE] {
204 self.0.to_bytes()
205 }
206}
207
208impl From<&'_ [u8; Self::SIZE]> for Signature {
209 fn from(bytes: &'_ [u8; Self::SIZE]) -> Self {
210 Self(ext::Signature::from(bytes))
211 }
212}
213
214impl TryFrom<&'_ [u8]> for Signature {
215 type Error = ext::SignatureError;
216
217 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
218 Ok(Signature(ext::Signature::try_from(bytes)?))
219 }
220}
221
222impl fmt::Debug for Signature {
223 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224 write!(f, "Signature(_)")
225 }
226}
227
228impl sqlx::Type<Sqlite> for Signature {
229 fn type_info() -> SqliteTypeInfo {
230 <&[u8] as sqlx::Type<Sqlite>>::type_info()
231 }
232}
233
234impl<'q> sqlx::Encode<'q, Sqlite> for &'q Signature {
235 fn encode_by_ref(
236 &self,
237 args: &mut Vec<SqliteArgumentValue<'q>>,
238 ) -> Result<sqlx::encode::IsNull, sqlx::error::BoxDynError> {
239 sqlx::Encode::<Sqlite>::encode(self.to_bytes().to_vec(), args)
242 }
243}
244
245impl<'r> sqlx::Decode<'r, Sqlite> for Signature {
246 fn decode(value: SqliteValueRef<'r>) -> Result<Self, sqlx::error::BoxDynError> {
247 let slice = <&[u8] as sqlx::Decode<Sqlite>>::decode(value)?;
248 Ok(slice.try_into()?)
249 }
250}
251
252pub type SignatureError = ext::SignatureError;
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257 use rand::{rngs::StdRng, SeedableRng};
258
259 #[test]
262 fn compatibility() {
263 let mut rng = StdRng::seed_from_u64(0);
264 let keypair = Keypair::generate(&mut rng);
265
266 assert_eq!(
267 dump_signature(&keypair.sign(b"")),
268 "51ad17bc6bfbeeddd86c2a328d7a9b37197453244f4470a446ac9516acb4f243add7f93a5a6ba44bd21b9ed45c830dbbe28e2c40f7819d4c42c45b844258140a"
269 );
270
271 assert_eq!(
272 dump_signature(&keypair.sign(b"hello world")),
273 "bcbd9b3aee0031f9616ed873106f2a0a136572fb5182c71e8d56c1308098c7c687367608e99bb64ace8de09544e8d87dc46e0cdaa7d188ee78bfbfb7d754a703"
274 );
275
276 assert_eq!(
277 dump_signature(&keypair.sign(&rng.r#gen::<[u8; 32]>())),
278 "3230b7f98529273c71f8af92b1581d290bf424fd7bd5015399c6213cbc461ca79ff932a7fcbb5e19d2ef6efa8ed9b833b6d17431793facf1b810c3b579570d0d"
279 );
280 }
281
282 fn dump_signature(signature: &Signature) -> String {
283 hex::encode(signature.to_bytes())
284 }
285}