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