defuse_crypto/
signature.rs1use core::{
2 fmt::{self, Debug, Display},
3 str::FromStr,
4};
5
6use near_sdk::{
7 bs58, near,
8 serde_with::{DeserializeFromStr, SerializeDisplay},
9};
10
11#[cfg(feature = "ed25519")]
12use crate::Ed25519;
13#[cfg(feature = "p256")]
14use crate::P256;
15#[cfg(feature = "secp256k1")]
16use crate::Secp256k1;
17
18use crate::{Curve, CurveType, ParseCurveError, parse::checked_base58_decode_array};
19
20#[near(serializers = [borsh(use_discriminant = true)])]
21#[derive(
22 Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord, SerializeDisplay, DeserializeFromStr,
23)]
24#[serde_with(crate = "::near_sdk::serde_with")]
25#[repr(u8)]
26pub enum Signature {
27 #[cfg(feature = "ed25519")]
28 Ed25519(<Ed25519 as Curve>::Signature) = 0,
29 #[cfg(feature = "secp256k1")]
30 Secp256k1(<Secp256k1 as Curve>::Signature) = 1,
31 #[cfg(feature = "p256")]
32 P256(<P256 as Curve>::Signature) = 2,
33}
34
35impl Signature {
36 #[inline]
37 pub const fn curve_type(&self) -> CurveType {
38 match self {
39 #[cfg(feature = "ed25519")]
40 Self::Ed25519(_) => CurveType::Ed25519,
41 #[cfg(feature = "secp256k1")]
42 Self::Secp256k1(_) => CurveType::Secp256k1,
43 #[cfg(feature = "p256")]
44 Self::P256(_) => CurveType::P256,
45 }
46 }
47
48 #[inline]
49 const fn data(&self) -> &[u8] {
50 #[allow(clippy::match_same_arms)]
51 match self {
52 #[cfg(feature = "ed25519")]
53 Self::Ed25519(data) => data,
54 #[cfg(feature = "secp256k1")]
55 Self::Secp256k1(data) => data,
56 #[cfg(feature = "p256")]
57 Self::P256(data) => data,
58 }
59 }
60}
61
62impl Debug for Signature {
63 #[inline]
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 write!(
66 f,
67 "{}:{}",
68 self.curve_type(),
69 bs58::encode(self.data()).into_string()
70 )
71 }
72}
73
74impl Display for Signature {
75 #[inline]
76 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77 fmt::Debug::fmt(self, f)
78 }
79}
80
81impl FromStr for Signature {
82 type Err = ParseCurveError;
83
84 fn from_str(s: &str) -> Result<Self, Self::Err> {
85 let (curve, data) = if let Some((curve, data)) = s.split_once(':') {
86 (
87 curve.parse().map_err(|_| ParseCurveError::WrongCurveType)?,
88 data,
89 )
90 } else {
91 #[cfg(not(feature = "ed25519"))]
92 return Err(ParseCurveError::WrongCurveType);
93
94 #[cfg(feature = "ed25519")]
95 (CurveType::Ed25519, s)
96 };
97
98 match curve {
99 #[cfg(feature = "ed25519")]
100 CurveType::Ed25519 => checked_base58_decode_array(data).map(Self::Ed25519),
101 #[cfg(feature = "secp256k1")]
102 CurveType::Secp256k1 => checked_base58_decode_array(data).map(Self::Secp256k1),
103 #[cfg(feature = "p256")]
104 CurveType::P256 => checked_base58_decode_array(data).map(Self::P256),
105 }
106 }
107}
108
109#[cfg(feature = "abi")]
110const _: () = {
111 use near_sdk::{
112 schemars::{
113 JsonSchema,
114 r#gen::SchemaGenerator,
115 schema::{InstanceType, Metadata, Schema, SchemaObject},
116 },
117 serde_json,
118 };
119
120 impl JsonSchema for Signature {
121 fn schema_name() -> String {
122 String::schema_name()
123 }
124
125 fn is_referenceable() -> bool {
126 false
127 }
128
129 fn json_schema(_gen: &mut SchemaGenerator) -> Schema {
130 SchemaObject {
131 instance_type: Some(InstanceType::String.into()),
132 extensions: [("contentEncoding", "base58".into())]
133 .into_iter()
134 .map(|(k, v)| (k.to_string(), v))
135 .collect(),
136 metadata: Some(
137 Metadata {
138 examples: [
139 #[cfg(feature = "ed25519")]
140 Self::example_ed25519(),
141 #[cfg(feature = "secp256k1")]
142 Self::example_secp256k1(),
143 #[cfg(feature = "p256")]
144 Self::example_p256(),
145 ]
146 .map(serde_json::to_value)
147 .map(Result::unwrap)
148 .into(),
149 ..Default::default()
150 }
151 .into(),
152 ),
153 ..Default::default()
154 }
155 .into()
156 }
157 }
158
159 impl Signature {
160 #[cfg(feature = "ed25519")]
161 pub(super) fn example_ed25519() -> Self {
162 "ed25519:DNxoVu7L7sHr9pcHGWQoJtPsrwheB8akht1JxaGpc9hGrpehdycXBMLJg4ph1bQ9bXdfoxJCbbwxj3Bdrda52eF"
163 .parse()
164 .unwrap()
165 }
166
167 #[cfg(feature = "secp256k1")]
168 pub(super) fn example_secp256k1() -> Self {
169 "secp256k1:7huDZxNnibusy6wFkbUBQ9Rqq2VmCKgTWYdJwcPj8VnciHjZKPa41rn5n6WZnMqSUCGRHWMAsMjKGtMVVmpETCeCs"
170 .parse()
171 .unwrap()
172 }
173
174 #[cfg(feature = "p256")]
175 pub(super) fn example_p256() -> Self {
176 "p256:DNxoVu7L7sHr9pcHGWQoJtPsrwheB8akht1JxaGpc9hGrpehdycXBMLJg4ph1bQ9bXdfoxJCbbwxj3Bdrda52eF"
177 .parse()
178 .unwrap()
179 }
180 }
181};
182
183#[cfg(test)]
184mod tests {
185 use rstest::rstest;
186
187 use super::*;
188
189 #[rstest]
190 #[cfg_attr(
191 feature = "ed25519",
192 case(
193 "ed25519:4nrYPT9gQbagzC1c7gSRnSkjZukXqjFxnPVp6wjmH1QgsBB1xzsbHB3piY7eHBnofUVS4WRRHpSfTVaqYq9KM265",
194 )
195 )]
196 #[cfg_attr(
197 feature = "secp256k1",
198 case(
199 "secp256k1:7o3557Aipc2MDtvh3E5ZQet85ZcRsynThmhcVZye9mUD1fcG6PBCerX6BKDGkKf3L31DUSkAtSd9o4kGvc3h4wZJ7",
200 )
201 )]
202 #[cfg_attr(
203 feature = "p256",
204 case(
205 "p256:4skfJSJRVHKjXs2FztBcSnTsbSRMjF3ykFz9hB4kZo486KvRrTpwz54uzQawsKtCdM1BdQR6JdAAZXmHreNXmNBj",
206 )
207 )]
208 fn parse_ok(#[case] sig: &str) {
209 sig.parse::<Signature>().unwrap();
210 }
211
212 #[rstest]
213 #[cfg_attr(
214 feature = "ed25519",
215 case("ed25519:5TagutioHgKLh7KZ1VEFBYfgRkPtqnKm9LoMnJMJ"),
216 case("ed25519:")
217 )]
218 #[cfg_attr(
219 feature = "secp256k1",
220 case("secp256k1:p3UPfBR3kWxE2C8wF1855eguaoRvoW6jV5ZXbu3sTTCs"),
221 case("secp256k1:")
222 )]
223 #[cfg_attr(
224 feature = "p256",
225 case("p256:p3UPfBR3kWxE2C8wF1855eguaoRvoW6jV5ZXbu3sTTCs"),
226 case("p256:")
227 )]
228 fn parse_invalid_length(#[case] sig: &str) {
229 assert_eq!(
230 sig.parse::<Signature>(),
231 Err(ParseCurveError::InvalidLength)
232 );
233 }
234}