Skip to main content

chio_settle/channel/
signed.rs

1use 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}