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 .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}