1#![cfg_attr(docsrs, feature(doc_auto_cfg))]
5#![allow(clippy::needless_doctest_main)]
6#![doc = include_str!("../README.md")]
7#![warn(missing_docs)]
8
9use std::ops::Deref;
10
11use ed25519_dalek::{Context, DigestSigner, DigestVerifier};
12use fn_dsa_sign::SigningKey;
13use fn_dsa_vrfy::VerifyingKey;
14use rand_core::CryptoRngCore;
15use sha3::digest::core_api::CoreWrapper;
16use sha3::{Digest, Sha3_512, Sha3_512Core, digest::consts::U64};
17
18use reach_aliases::*;
19
20mod aliases;
21pub use aliases::*;
22
23pub trait Digestible {
28 fn hashable_bytes(&self) -> Vec<Box<dyn AsRef<[u8]> + '_>>;
32
33 fn digest(&self) -> CoreWrapper<Sha3_512Core> {
35 let mut hasher = <Sha3_512 as Digest>::new();
36 for bytes in self.hashable_bytes() {
37 hasher.update(bytes.deref());
38 }
39
40 hasher
41 }
42
43 fn finalized_digest(&self) -> [u8; 64] {
45 self.digest().finalize().into()
46 }
47}
48
49impl Digestible for &str {
50 fn hashable_bytes(&self) -> Vec<Box<dyn AsRef<[u8]> + '_>> {
51 vec![Box::new(self)]
52 }
53}
54
55impl<T> Digestible for [T]
56where
57 T: Digestible,
58{
59 fn hashable_bytes(&self) -> Vec<Box<dyn AsRef<[u8]> + '_>> {
60 self.iter().flat_map(Digestible::hashable_bytes).collect()
61 }
62}
63
64pub trait Signable: Digestible {
69 type SignedType: Signatures;
71
72 fn context() -> &'static [u8] {
77 std::any::type_name::<Self::SignedType>().as_bytes()
78 }
79
80 fn with_signature(
82 self,
83 ec_signature: Ed25519Signature,
84 pq_signature: FnDsaSignature,
85 ) -> Self::SignedType;
86}
87
88pub trait Sign {
93 fn ec_signing_key(&self) -> &Ed25519Signing;
95 fn pq_signing_key(&self) -> &FnDsaSigning;
97
98 fn ec_signing_context<'a, 'b>(&'a self, context: &'b [u8]) -> Context<'a, 'b, Ed25519Signing> {
100 self.ec_signing_key()
101 .with_context(context)
102 .expect("Failed to set Ed25519 signing context")
103 }
104
105 fn ec_sign_digest<D>(&self, context: &[u8], digest: D) -> Ed25519Signature
107 where
108 D: Digest<OutputSize = U64>,
109 {
110 self.ec_signing_context(context).sign_digest(digest)
111 }
112
113 fn pq_sign_digest(
115 &self,
116 context: &FnDsaDomainContext<'_>,
117 digest: &[u8],
118 csprng: &mut impl CryptoRngCore,
119 ) -> FnDsaSignature {
120 let mut pq_signing_key =
121 FnDsaSigningDecoded::decode(self.pq_signing_key().inner.as_slice())
122 .expect("Failed to decode FN-DSA Signing Key");
123 let mut pq_signature = FN_DSA_SIGNATURE_EMPTY;
124
125 pq_signing_key.sign(csprng, context, &FN_DSA_HASH_ID, digest, &mut pq_signature);
126
127 pq_signature
128 }
129
130 fn sign_digest<D>(
132 &self,
133 context: &[u8],
134 digest: D,
135 csprng: &mut impl CryptoRngCore,
136 ) -> (Ed25519Signature, FnDsaSignature)
137 where
138 D: Digest<OutputSize = U64> + Clone,
139 {
140 let digest_bytes = digest.clone().finalize();
141 let ec_signature = self.ec_sign_digest(context, digest);
142 let pq_signature =
143 self.pq_sign_digest(&FnDsaDomainContext { 0: context }, &digest_bytes, csprng);
144
145 (ec_signature, pq_signature)
146 }
147
148 fn sign<S, V>(&self, signable: S, csprng: &mut impl CryptoRngCore) -> S::SignedType
153 where
154 S: Signable + Digestible,
155 V: Verifies<S::SignedType> + Verifier,
156 Self: VerifyingKeys<V>,
157 {
158 let (ec_signature, pq_signature) =
159 self.sign_digest(S::context(), signable.digest(), csprng);
160
161 signable.with_signature(ec_signature, pq_signature)
162 }
163}
164
165pub trait Signatures {
167 fn ec_signature(&self) -> &Ed25519Signature;
169 fn pq_signature(&self) -> &FnDsaSignature;
171
172 fn context() -> &'static [u8] {
174 std::any::type_name::<Self>().as_bytes()
175 }
176}
177
178pub trait VerifyingKeys<V: Verifier> {}
180
181pub trait Verifies<V> {}
183
184pub trait Verifier {
186 fn ec_verifying_key(&self) -> &Ed25519Verifying;
188 fn pq_verifying_key(&self) -> &FnDsaVerifying;
190}
191
192fn ec_verifying_context<'a, 'b, V>(
193 verifier: &'a V,
194 context: &'b [u8],
195) -> Context<'a, 'b, Ed25519Verifying>
196where
197 V: Verifier + ?Sized,
198{
199 verifier
200 .ec_verifying_key()
201 .with_context(context)
202 .expect("Failed to set Ed25519 verifying context")
203}
204
205fn ec_verify_digest<V, D>(
206 verifier: &V,
207 context: &[u8],
208 digest: D,
209 signature: &Ed25519Signature,
210) -> bool
211where
212 V: Verifier + ?Sized,
213 D: Digest<OutputSize = U64>,
214{
215 ec_verifying_context(verifier, context)
216 .verify_digest(digest, signature)
217 .is_ok()
218}
219
220fn pq_verify_digest<V>(
221 verifier: &V,
222 context: &FnDsaDomainContext<'_>,
223 digest: &[u8],
224 signature: &FnDsaSignature,
225) -> bool
226where
227 V: Verifier + ?Sized,
228{
229 let pq_verifying_key = FnDsaVerifyingDecoded::decode(verifier.pq_verifying_key().as_ref())
230 .expect("Decoding of FN-DSA Verifying Key failed");
231
232 pq_verifying_key.verify(signature, context, &FN_DSA_HASH_ID, digest)
233}
234
235pub fn verify_digest<V, D>(
239 verifier: &V,
240 context: &[u8],
241 digest: D,
242 ec_signature: &Ed25519Signature,
243 pq_signature: &FnDsaSignature,
244) -> bool
245where
246 V: Verifier + ?Sized,
247 D: Digest<OutputSize = U64> + Clone,
248{
249 let digest_bytes = digest.clone().finalize();
250 ec_verify_digest(verifier, context, digest, ec_signature)
251 && pq_verify_digest(
252 verifier,
253 &FnDsaDomainContext { 0: context },
254 &digest_bytes,
255 pq_signature,
256 )
257}
258
259pub trait Verifiable: Signatures + Digestible {
261 fn verify<V>(&self, verifier: &V) -> bool
266 where
267 V: Verifier + Verifies<Self>,
268 Self: Sized,
269 {
270 verify_digest(
271 verifier,
272 Self::context(),
273 self.digest(),
274 self.ec_signature(),
275 self.pq_signature(),
276 )
277 }
278}