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, Hash, Deftly)]
70 #[allow(clippy::derived_hash_with_manual_eq)]
72 #[derive_deftly(PartialEqFromCtEq)]
73 pub struct PublicKey(x25519_dalek::PublicKey);
74
75 impl ConstantTimeEq for PublicKey {
76 fn ct_eq(&self, other: &Self) -> subtle::Choice {
77 let Self { 0: self_secret } = self;
78 let Self { 0: other_secret } = other;
79
80 self_secret.as_bytes().ct_eq(other_secret.as_bytes())
81 }
82 }
83
84 pub struct SharedSecret(x25519_dalek::SharedSecret);
88
89 impl<'a> From<&'a EphemeralSecret> for PublicKey {
90 fn from(secret: &'a EphemeralSecret) -> Self {
91 Self((&secret.0).into())
92 }
93 }
94
95 impl<'a> From<&'a StaticSecret> for PublicKey {
96 fn from(secret: &'a StaticSecret) -> Self {
97 Self((&secret.0).into())
98 }
99 }
100
101 impl From<[u8; 32]> for StaticSecret {
102 fn from(value: [u8; 32]) -> Self {
103 Self(value.into())
104 }
105 }
106 impl From<[u8; 32]> for PublicKey {
107 fn from(value: [u8; 32]) -> Self {
108 Self(value.into())
109 }
110 }
111
112 impl EphemeralSecret {
113 pub fn random_from_rng<R: rand_core::Rng + rand_core::CryptoRng>(csprng: R) -> Self {
115 Self(x25519_dalek::EphemeralSecret::random_from_rng(
116 RngCompat::new(csprng),
117 ))
118 }
119 pub fn diffie_hellman(self, their_public: &PublicKey) -> SharedSecret {
121 SharedSecret(self.0.diffie_hellman(&their_public.0))
122 }
123 }
124 impl StaticSecret {
125 pub fn random_from_rng<R: rand_core::Rng + rand_core::CryptoRng>(csprng: R) -> Self {
127 Self(x25519_dalek::StaticSecret::random_from_rng(RngCompat::new(
128 csprng,
129 )))
130 }
131 pub fn diffie_hellman(&self, their_public: &PublicKey) -> SharedSecret {
133 SharedSecret(self.0.diffie_hellman(&their_public.0))
134 }
135 pub fn to_bytes(&self) -> [u8; 32] {
137 self.0.to_bytes()
138 }
139 pub fn as_bytes(&self) -> &[u8; 32] {
141 self.0.as_bytes()
142 }
143 }
144 impl SharedSecret {
145 pub fn as_bytes(&self) -> &[u8; 32] {
147 self.0.as_bytes()
148 }
149 pub fn was_contributory(&self) -> bool {
153 self.0.was_contributory()
154 }
155 }
156 impl PublicKey {
157 pub fn as_bytes(&self) -> &[u8; 32] {
159 self.0.as_bytes()
160 }
161 pub fn to_bytes(&self) -> [u8; 32] {
163 self.0.to_bytes()
164 }
165 }
166}
167
168pub trait ValidatableSignature {
179 fn is_valid(&self) -> bool;
181
182 fn as_ed25519(&self) -> Option<&ed25519::ValidatableEd25519Signature> {
184 None
185 }
186}
187
188pub fn validate_all_sigs(v: &[Box<dyn ValidatableSignature>]) -> bool {
199 let mut ed_sigs = Vec::new();
202 let mut non_ed_sigs = Vec::new();
203 for sig in v.iter() {
204 match sig.as_ed25519() {
205 Some(ed_sig) => ed_sigs.push(ed_sig),
206 None => non_ed_sigs.push(sig),
207 }
208 }
209
210 let ed_batch_is_valid = crate::pk::ed25519::validate_batch(&ed_sigs[..]);
212
213 ed_batch_is_valid && non_ed_sigs.iter().all(|b| b.is_valid())
215}
216
217#[cfg(test)]
218mod test {
219 #![allow(clippy::bool_assert_comparison)]
221 #![allow(clippy::clone_on_copy)]
222 #![allow(clippy::dbg_macro)]
223 #![allow(clippy::mixed_attributes_style)]
224 #![allow(clippy::print_stderr)]
225 #![allow(clippy::print_stdout)]
226 #![allow(clippy::single_char_pattern)]
227 #![allow(clippy::unwrap_used)]
228 #![allow(clippy::unchecked_time_subtraction)]
229 #![allow(clippy::useless_vec)]
230 #![allow(clippy::needless_pass_by_value)]
231 #![allow(clippy::string_slice)] #[test]
234 fn validatable_ed_sig() {
235 use super::ValidatableSignature;
236 use super::ed25519::{PublicKey, Signature, ValidatableEd25519Signature};
237 use hex_literal::hex;
238 let pk = PublicKey::from_bytes(&hex!(
239 "fc51cd8e6218a1a38da47ed00230f058
240 0816ed13ba3303ac5deb911548908025"
241 ))
242 .unwrap();
243 let sig: Signature = hex!(
244 "6291d657deec24024827e69c3abe01a3
245 0ce548a284743a445e3680d7db5ac3ac
246 18ff9b538d16f290ae67f760984dc659
247 4a7c15e9716ed28dc027beceea1ec40a"
248 )
249 .into();
250
251 let valid = ValidatableEd25519Signature::new(pk, sig, &hex!("af82"));
252 let invalid = ValidatableEd25519Signature::new(pk, sig, &hex!("af83"));
253
254 assert!(valid.is_valid());
255 assert!(!invalid.is_valid());
256 }
257}