Skip to main content

defuse_core/
signature.rs

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