nym_api_requests/
signable.rs1use nym_crypto::asymmetric::ed25519;
5use nym_crypto::asymmetric::ed25519::serde_helpers::bs58_ed25519_signature;
6use serde::{Deserialize, Serialize};
7use utoipa::ToSchema;
8
9pub trait SignableMessageBody: Serialize + sealed::Sealed {
11 fn sign(self, key: &ed25519::PrivateKey) -> SignedMessage<Self>
12 where
13 Self: Sized,
14 {
15 let signature = key.sign(self.plaintext());
16 SignedMessage {
17 body: self,
18 signature,
19 }
20 }
21
22 fn plaintext(&self) -> Vec<u8> {
23 #[allow(clippy::unwrap_used)]
24 serde_json::to_vec(&self).unwrap()
26 }
27}
28
29impl<T> SignableMessageBody for T where T: Serialize + sealed::Sealed {}
30
31#[derive(Clone, Serialize, Deserialize, Debug, ToSchema)]
32#[serde(rename_all = "camelCase")]
33pub struct SignedMessage<T> {
34 pub body: T,
35 #[schema(value_type = String)]
36 #[serde(with = "bs58_ed25519_signature")]
37 pub signature: ed25519::Signature,
38}
39
40impl<T> SignedMessage<T> {
41 pub fn verify_signature(&self, pub_key: &ed25519::PublicKey) -> bool
42 where
43 T: SignableMessageBody,
44 {
45 let plaintext = self.body.plaintext();
46 if plaintext.is_empty() {
47 return false;
48 }
49
50 pub_key.verify(&plaintext, &self.signature).is_ok()
51 }
52}
53
54pub(crate) mod sealed {
56 use crate::ecash::models::*;
57 use crate::models::{
58 ChainBlocksStatusResponseBody, DetailedSignersStatusResponseBody, SignersStatusResponseBody,
59 };
60
61 pub trait Sealed {}
62
63 impl Sealed for IssuedTicketbooksChallengeCommitmentRequestBody {}
65 impl Sealed for IssuedTicketbooksDataRequestBody {}
66
67 impl Sealed for IssuedTicketbooksChallengeCommitmentResponseBody {}
69 impl Sealed for IssuedTicketbooksForResponseBody {}
70 impl Sealed for IssuedTicketbooksDataResponseBody {}
71 impl Sealed for EcashSignerStatusResponseBody {}
72 impl Sealed for ChainBlocksStatusResponseBody {}
73 impl Sealed for SignersStatusResponseBody {}
74 impl Sealed for DetailedSignersStatusResponseBody {}
75}