chio_settle/channel/
signed.rs1use chio_core::crypto::{Keypair, PublicKey, Signature};
2use serde::{Deserialize, Deserializer, Serialize};
3
4use super::validation::{validate_positive, validate_text};
5use super::ChannelError;
6
7const CHANNEL_SIGNATURE_DOMAIN: &str = "chio.channel.signature.v1";
8
9#[derive(Serialize)]
10#[serde(rename_all = "camelCase")]
11struct ChannelSignaturePayloadV1<'a, T> {
12 domain: &'static str,
13 signer_id: &'a str,
14 key_epoch: u64,
15 signer_key: &'a PublicKey,
16 body: &'a T,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
20#[serde(rename_all = "camelCase", deny_unknown_fields)]
21pub struct ChannelSignatureV1 {
22 pub signer_id: String,
23 pub key_epoch: u64,
24 #[serde(deserialize_with = "deserialize_canonical_public_key")]
25 pub signer_key: PublicKey,
26 #[serde(deserialize_with = "deserialize_canonical_signature")]
27 pub signature: Signature,
28}
29
30pub(super) fn deserialize_canonical_public_key<'de, D>(
31 deserializer: D,
32) -> Result<PublicKey, D::Error>
33where
34 D: Deserializer<'de>,
35{
36 let encoded = String::deserialize(deserializer)?;
37 let key = PublicKey::from_hex(&encoded).map_err(serde::de::Error::custom)?;
38 if key.to_hex() != encoded {
39 return Err(serde::de::Error::custom("noncanonical public key"));
40 }
41 Ok(key)
42}
43
44pub(super) fn deserialize_canonical_signature<'de, D>(
45 deserializer: D,
46) -> Result<Signature, D::Error>
47where
48 D: Deserializer<'de>,
49{
50 let encoded = String::deserialize(deserializer)?;
51 let signature = Signature::from_hex(&encoded).map_err(serde::de::Error::custom)?;
52 if signature.to_hex() != encoded {
53 return Err(serde::de::Error::custom("noncanonical signature"));
54 }
55 Ok(signature)
56}
57
58impl ChannelSignatureV1 {
59 pub fn sign<T: Serialize>(
60 body: &T,
61 signer_id: String,
62 key_epoch: u64,
63 keypair: &Keypair,
64 ) -> Result<Self, ChannelError> {
65 validate_text("signer_id", &signer_id)?;
66 validate_positive("signer_key_epoch", key_epoch)?;
67 let signer_key = keypair.public_key();
68 let payload = ChannelSignaturePayloadV1 {
69 domain: CHANNEL_SIGNATURE_DOMAIN,
70 signer_id: &signer_id,
71 key_epoch,
72 signer_key: &signer_key,
73 body,
74 };
75 let (signature, _) = keypair
76 .sign_canonical(&payload)
77 .map_err(|error| ChannelError::Canonicalization(error.to_string()))?;
78 Ok(Self {
79 signer_id,
80 key_epoch,
81 signer_key,
82 signature,
83 })
84 }
85
86 pub fn verify<T: Serialize>(
87 &self,
88 body: &T,
89 expected_signer_id: &str,
90 expected_key_epoch: u64,
91 expected_key: &PublicKey,
92 ) -> Result<(), ChannelError> {
93 validate_text("signer_id", &self.signer_id)?;
94 validate_positive("signer_key_epoch", self.key_epoch)?;
95 if self.signer_id != expected_signer_id
96 || self.key_epoch != expected_key_epoch
97 || &self.signer_key != expected_key
98 {
99 return Err(ChannelError::AuthorityVerification);
100 }
101 let payload = ChannelSignaturePayloadV1 {
102 domain: CHANNEL_SIGNATURE_DOMAIN,
103 signer_id: &self.signer_id,
104 key_epoch: self.key_epoch,
105 signer_key: &self.signer_key,
106 body,
107 };
108 if !self
109 .signer_key
110 .verify_canonical(&payload, &self.signature)
111 .map_err(|_| ChannelError::AuthorityVerification)?
112 {
113 return Err(ChannelError::AuthorityVerification);
114 }
115 Ok(())
116 }
117}