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