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