1pub mod ed25519;
7pub mod keymanip;
8pub mod rsa;
9
10pub mod curve25519 {
16 use derive_deftly::Deftly;
17 use educe::Educe;
18 use subtle::ConstantTimeEq;
19
20 use crate::util::ct::derive_deftly_template_PartialEqFromCtEq;
21 use crate::util::rng::RngCompat;
22
23 #[allow(clippy::exhaustive_structs)]
25 #[derive(Clone, Educe)]
26 #[educe(Debug)]
27 pub struct StaticKeypair {
28 #[educe(Debug(ignore))]
30 pub secret: StaticSecret,
31 pub public: PublicKey,
33 }
34
35 pub struct EphemeralSecret(x25519_dalek::EphemeralSecret);
40
41 #[derive(Clone)]
55 pub struct StaticSecret(x25519_dalek::StaticSecret);
56
57 impl ConstantTimeEq for StaticSecret {
58 fn ct_eq(&self, other: &Self) -> subtle::Choice {
59 let Self { 0: self_secret } = self;
60 let Self { 0: other_secret } = other;
61
62 self_secret.as_bytes().ct_eq(other_secret.as_bytes())
63 }
64 }
65
66 #[derive(Clone, Copy, Debug, Eq, Deftly)]
70 #[derive_deftly(PartialEqFromCtEq)]
71 pub struct PublicKey(x25519_dalek::PublicKey);
72
73 impl ConstantTimeEq for PublicKey {
74 fn ct_eq(&self, other: &Self) -> subtle::Choice {
75 let Self { 0: self_secret } = self;
76 let Self { 0: other_secret } = other;
77
78 self_secret.as_bytes().ct_eq(other_secret.as_bytes())
79 }
80 }
81
82 pub struct SharedSecret(x25519_dalek::SharedSecret);
86
87 impl<'a> From<&'a EphemeralSecret> for PublicKey {
88 fn from(secret: &'a EphemeralSecret) -> Self {
89 Self((&secret.0).into())
90 }
91 }
92
93 impl<'a> From<&'a StaticSecret> for PublicKey {
94 fn from(secret: &'a StaticSecret) -> Self {
95 Self((&secret.0).into())
96 }
97 }
98
99 impl From<[u8; 32]> for StaticSecret {
100 fn from(value: [u8; 32]) -> Self {
101 Self(value.into())
102 }
103 }
104 impl From<[u8; 32]> for PublicKey {
105 fn from(value: [u8; 32]) -> Self {
106 Self(value.into())
107 }
108 }
109
110 impl EphemeralSecret {
111 pub fn random_from_rng<R: rand_core::RngCore + rand_core::CryptoRng>(csprng: R) -> Self {
113 Self(x25519_dalek::EphemeralSecret::random_from_rng(
114 RngCompat::new(csprng),
115 ))
116 }
117 pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
119 SharedSecret(self.0.diffie_hellman(&their_public.0))
120 }
121 }
122 impl StaticSecret {
123 pub fn random_from_rng<R: rand_core::RngCore + rand_core::CryptoRng>(csprng: R) -> Self {
125 Self(x25519_dalek::StaticSecret::random_from_rng(RngCompat::new(
126 csprng,
127 )))
128 }
129 pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
131 SharedSecret(self.0.diffie_hellman(&their_public.0))
132 }
133 pub fn to_bytes(&self) -> [u8; 32] {
135 self.0.to_bytes()
136 }
137 pub fn as_bytes(&self) -> &[u8; 32] {
139 self.0.as_bytes()
140 }
141 }
142 impl SharedSecret {
143 pub fn as_bytes(&self) -> &[u8; 32] {
145 self.0.as_bytes()
146 }
147 pub fn was_contributory(&self) -> bool {
151 self.0.was_contributory()
152 }
153 }
154 impl PublicKey {
155 pub fn as_bytes(&self) -> &[u8; 32] {
157 self.0.as_bytes()
158 }
159 pub fn to_bytes(&self) -> [u8; 32] {
161 self.0.to_bytes()
162 }
163 }
164}
165
166pub trait ValidatableSignature {
177 fn is_valid(&self) -> bool;
179
180 fn as_ed25519(&self) -> Option<&ed25519::ValidatableEd25519Signature> {
182 None
183 }
184}
185
186pub fn validate_all_sigs(v: &[Box<dyn ValidatableSignature>]) -> bool {
197 let mut ed_sigs = Vec::new();
200 let mut non_ed_sigs = Vec::new();
201 for sig in v.iter() {
202 match sig.as_ed25519() {
203 Some(ed_sig) => ed_sigs.push(ed_sig),
204 None => non_ed_sigs.push(sig),
205 }
206 }
207
208 let ed_batch_is_valid = crate::pk::ed25519::validate_batch(&ed_sigs[..]);
210
211 ed_batch_is_valid && non_ed_sigs.iter().all(|b| b.is_valid())
213}
214
215#[cfg(test)]
216mod test {
217 #![allow(clippy::bool_assert_comparison)]
219 #![allow(clippy::clone_on_copy)]
220 #![allow(clippy::dbg_macro)]
221 #![allow(clippy::mixed_attributes_style)]
222 #![allow(clippy::print_stderr)]
223 #![allow(clippy::print_stdout)]
224 #![allow(clippy::single_char_pattern)]
225 #![allow(clippy::unwrap_used)]
226 #![allow(clippy::unchecked_duration_subtraction)]
227 #![allow(clippy::useless_vec)]
228 #![allow(clippy::needless_pass_by_value)]
229 #[test]
231 fn validatable_ed_sig() {
232 use super::ValidatableSignature;
233 use super::ed25519::{PublicKey, Signature, ValidatableEd25519Signature};
234 use hex_literal::hex;
235 let pk = PublicKey::from_bytes(&hex!(
236 "fc51cd8e6218a1a38da47ed00230f058
237 0816ed13ba3303ac5deb911548908025"
238 ))
239 .unwrap();
240 let sig: Signature = hex!(
241 "6291d657deec24024827e69c3abe01a3
242 0ce548a284743a445e3680d7db5ac3ac
243 18ff9b538d16f290ae67f760984dc659
244 4a7c15e9716ed28dc027beceea1ec40a"
245 )
246 .into();
247
248 let valid = ValidatableEd25519Signature::new(pk, sig, &hex!("af82"));
249 let invalid = ValidatableEd25519Signature::new(pk, sig, &hex!("af83"));
250
251 assert!(valid.is_valid());
252 assert!(!invalid.is_valid());
253 }
254}