Skip to main content

defuse_core/
signature.rs

1use core::{
2    fmt::{self, Debug, Display},
3    str::FromStr,
4};
5
6use borsh::{BorshDeserialize, BorshSerialize};
7use defuse_crypto::{
8    ed25519::{Ed25519, Ed25519Signature},
9    fmt::{ParseCurveError, TypedCurve, checked_base58_decode_array},
10    p256::{P256, P256Signature},
11    secp256k1::{Secp256k1, Secp256k1RecoverableSignature},
12};
13use serde_with::{DeserializeFromStr, SerializeDisplay};
14
15#[cfg_attr(feature = "abi", derive(::borsh::BorshSchema))]
16#[derive(
17    Clone,
18    Copy,
19    Hash,
20    PartialEq,
21    Eq,
22    PartialOrd,
23    Ord,
24    SerializeDisplay,
25    DeserializeFromStr,
26    BorshSerialize,
27    BorshDeserialize,
28    derive_more::From,
29)]
30#[borsh(use_discriminant = true)]
31#[repr(u8)]
32pub enum Signature {
33    Ed25519(Ed25519Signature) = 0,
34    Secp256k1(Secp256k1RecoverableSignature) = 1,
35    P256(P256Signature) = 2,
36}
37
38impl Debug for Signature {
39    #[inline]
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        write!(
42            f,
43            "{}",
44            match self {
45                Self::Ed25519(sig) => sig.to_string(),
46                Self::Secp256k1(sig) => sig.to_string(),
47                Self::P256(sig) => sig.to_string(),
48            }
49        )
50    }
51}
52
53impl Display for Signature {
54    #[inline]
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        fmt::Debug::fmt(self, f)
57    }
58}
59
60impl FromStr for Signature {
61    type Err = ParseCurveError;
62
63    fn from_str(s: &str) -> Result<Self, Self::Err> {
64        let (curve, data) = s
65            .split_once(':')
66            // ed25519 by default
67            .unwrap_or((Ed25519::CURVE_TYPE, s));
68
69        match curve {
70            Ed25519::CURVE_TYPE => checked_base58_decode_array(data)
71                .map(Ed25519Signature)
72                .map(Into::into),
73            Secp256k1::CURVE_TYPE => checked_base58_decode_array(data)
74                .map(Secp256k1RecoverableSignature)
75                .map(Into::into),
76            P256::CURVE_TYPE => checked_base58_decode_array(data)
77                .map(P256Signature)
78                .map(Into::into),
79            _ => Err(ParseCurveError::WrongCurveType),
80        }
81    }
82}
83
84#[cfg(feature = "abi")]
85const _: () = {
86    use schemars::{
87        JsonSchema,
88        r#gen::SchemaGenerator,
89        schema::{InstanceType, Metadata, Schema, SchemaObject},
90    };
91
92    impl JsonSchema for Signature {
93        fn schema_name() -> String {
94            String::schema_name()
95        }
96
97        fn is_referenceable() -> bool {
98            false
99        }
100
101        fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
102            SchemaObject {
103                instance_type: Some(InstanceType::String.into()),
104                extensions: std::iter::once(("contentEncoding", "base58".into()))
105                    .map(|(k, v)| (k.to_string(), v))
106                    .collect(),
107                metadata: Some(
108                    Metadata {
109                        examples: [
110                            Self::example_ed25519(),
111                            Self::example_secp256k1(),
112                            Self::example_p256(),
113                        ]
114                        .map(|s: Self| serde_json::Value::String(s.to_string()))
115                        .into(),
116                        ..Default::default()
117                    }
118                    .into(),
119                ),
120                ..Default::default()
121            }
122            .into()
123        }
124    }
125
126    impl Signature {
127        pub(super) fn example_ed25519() -> Self {
128            "ed25519:DNxoVu7L7sHr9pcHGWQoJtPsrwheB8akht1JxaGpc9hGrpehdycXBMLJg4ph1bQ9bXdfoxJCbbwxj3Bdrda52eF"
129                .parse()
130                .unwrap()
131        }
132
133        pub(super) fn example_secp256k1() -> Self {
134            "secp256k1:7huDZxNnibusy6wFkbUBQ9Rqq2VmCKgTWYdJwcPj8VnciHjZKPa41rn5n6WZnMqSUCGRHWMAsMjKGtMVVmpETCeCs"
135                .parse()
136                .unwrap()
137        }
138
139        pub(super) fn example_p256() -> Self {
140            "p256:DNxoVu7L7sHr9pcHGWQoJtPsrwheB8akht1JxaGpc9hGrpehdycXBMLJg4ph1bQ9bXdfoxJCbbwxj3Bdrda52eF"
141                .parse()
142                .unwrap()
143        }
144    }
145};
146
147#[cfg(test)]
148mod tests {
149    use rstest::rstest;
150
151    use super::*;
152
153    #[rstest]
154    #[case(
155        "ed25519:4nrYPT9gQbagzC1c7gSRnSkjZukXqjFxnPVp6wjmH1QgsBB1xzsbHB3piY7eHBnofUVS4WRRHpSfTVaqYq9KM265"
156    )]
157    #[case(
158        "secp256k1:7o3557Aipc2MDtvh3E5ZQet85ZcRsynThmhcVZye9mUD1fcG6PBCerX6BKDGkKf3L31DUSkAtSd9o4kGvc3h4wZJ7"
159    )]
160    #[case(
161        "p256:4skfJSJRVHKjXs2FztBcSnTsbSRMjF3ykFz9hB4kZo486KvRrTpwz54uzQawsKtCdM1BdQR6JdAAZXmHreNXmNBj"
162    )]
163    fn parse_ok(#[case] sig: &str) {
164        sig.parse::<Signature>().unwrap();
165    }
166
167    #[rstest]
168    #[case("ed25519:5TagutioHgKLh7KZ1VEFBYfgRkPtqnKm9LoMnJMJ")]
169    #[case("ed25519:")]
170    #[case("secp256k1:p3UPfBR3kWxE2C8wF1855eguaoRvoW6jV5ZXbu3sTTCs")]
171    #[case("secp256k1:")]
172    #[case("p256:p3UPfBR3kWxE2C8wF1855eguaoRvoW6jV5ZXbu3sTTCs")]
173    #[case("p256:")]
174    fn parse_invalid_length(#[case] sig: &str) {
175        assert_eq!(
176            sig.parse::<Signature>(),
177            Err(ParseCurveError::InvalidLength)
178        );
179    }
180}