Skip to main content

asimov_id/
public_key.rs

1// This is free and unencumbered software released into the public domain.
2
3//! ASIMOV public keys.
4
5use crate::{KeyError, PUBLIC_KEY_LEN, PUBLIC_KEY_PREFIX, PublicKeyEncoding};
6use alloc::{
7    string::{String, ToString},
8    vec::Vec,
9};
10use core::str::FromStr;
11use derive_more::Display;
12
13#[derive(Clone, Copy, Debug, Default, Display, Eq, Hash, Ord, PartialEq, PartialOrd)]
14#[display("ⒶY{}", bs58::encode(self.0).into_string())]
15#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
16#[cfg_attr(feature = "serde", serde(try_from = "String", into = "String"))]
17pub struct PublicKey(pub(crate) [u8; 32]);
18
19impl PublicKey {
20    pub const ZERO: Self = Self([0u8; 32]);
21
22    pub fn as_bytes(&self) -> &[u8] {
23        self.0.as_slice()
24    }
25
26    pub fn into_bytes(self) -> [u8; 32] {
27        self.0
28    }
29
30    pub fn encode(&self, encoding: PublicKeyEncoding) -> Option<String> {
31        use PublicKeyEncoding::*;
32        Some(match encoding {
33            Asimov => self.to_string(),
34            Base58 => bs58::encode(self.0).into_string(),
35            Near => {
36                alloc::format!("ed25519:{}", bs58::encode(self.0).into_string())
37            },
38            #[cfg(feature = "base64")]
39            Base64 => data_encoding::BASE64.encode(self.as_bytes()),
40            #[cfg(feature = "base64")]
41            Base64Url => data_encoding::BASE64URL_NOPAD.encode(self.as_bytes()),
42            #[cfg(feature = "hex")]
43            Hex => data_encoding::HEXLOWER.encode(self.as_bytes()),
44            #[cfg(feature = "z32")]
45            Z32 => data_encoding_macro::new_encoding! {
46                symbols: "ybndrfg8ejkmcpqxot1uwisza345h769",
47            }
48            .encode(self.as_bytes()),
49            _ => return None, // unsupported format
50        })
51    }
52}
53
54impl FromStr for PublicKey {
55    type Err = KeyError;
56
57    fn from_str(input: &str) -> Result<Self, Self::Err> {
58        if input.is_empty() {
59            return Err(KeyError::EmptyInput);
60        }
61        if !PUBLIC_KEY_LEN.contains(&input.len()) {
62            return Err(KeyError::InvalidLength);
63        }
64        let Some(input) = input.strip_prefix(PUBLIC_KEY_PREFIX) else {
65            return Err(KeyError::InvalidPrefix);
66        };
67        let mut output = [0u8; 32];
68        let count = bs58::decode(&input)
69            .onto(&mut output)
70            .map_err(|e| KeyError::InvalidEncoding(e))?;
71        if count != output.len() {
72            return Err(KeyError::InvalidLength);
73        }
74        Ok(Self(output))
75    }
76}
77
78impl AsRef<[u8]> for PublicKey {
79    fn as_ref(&self) -> &[u8] {
80        self.as_bytes()
81    }
82}
83
84impl<T> From<&T> for PublicKey
85where
86    T: Clone + Into<Self>,
87{
88    fn from(t: &T) -> Self {
89        t.clone().into()
90    }
91}
92
93impl From<[u8; 32]> for PublicKey {
94    fn from(input: [u8; 32]) -> Self {
95        Self(input)
96    }
97}
98
99impl From<&Vec<u8>> for PublicKey {
100    fn from(input: &Vec<u8>) -> Self {
101        let mut bytes = [0u8; 32];
102        let len = bytes.len().min(input.len());
103        bytes[..len].copy_from_slice(&input[..len]);
104        Self(bytes)
105    }
106}
107
108#[cfg(feature = "ed25519-dalek")]
109impl From<&ed25519_dalek::VerifyingKey> for PublicKey {
110    fn from(input: &ed25519_dalek::VerifyingKey) -> Self {
111        Self(input.as_bytes().clone())
112    }
113}
114
115#[cfg(feature = "iroh")]
116impl From<iroh::PublicKey> for PublicKey {
117    fn from(input: iroh::PublicKey) -> Self {
118        Self(input.as_bytes().clone())
119    }
120}
121
122#[cfg(feature = "iroh")]
123impl From<PublicKey> for iroh::PublicKey {
124    fn from(input: PublicKey) -> Self {
125        iroh::PublicKey::from_bytes(&input.into_bytes()).unwrap() // TODO
126    }
127}
128
129#[cfg(feature = "iroh")]
130impl From<iroh::EndpointAddr> for PublicKey {
131    fn from(input: iroh::EndpointAddr) -> Self {
132        Self(input.id.as_bytes().clone())
133    }
134}
135
136#[cfg(feature = "iroh")]
137impl From<PublicKey> for iroh::EndpointAddr {
138    fn from(input: PublicKey) -> Self {
139        let endpoint_id = iroh::EndpointId::from(input);
140        iroh::EndpointAddr::from(endpoint_id)
141    }
142}
143
144// #[cfg(feature = "p2panda")]
145// impl From<&p2panda_core::PublicKey> for PublicKey {
146//     fn from(bytes: &p2panda_core::PublicKey) -> Self {
147//         Self(bytes.as_bytes().clone())
148//     }
149// }
150
151// #[cfg(feature = "p2panda")]
152// impl TryInto<p2panda_core::PublicKey> for PublicKey {
153//     type Error = p2panda_core::IdentityError;
154
155//     fn try_into(self) -> Result<p2panda_core::PublicKey, Self::Error> {
156//         p2panda_core::PublicKey::from_bytes(&self.into_bytes())
157//     }
158// }
159
160impl TryFrom<String> for PublicKey {
161    type Error = KeyError;
162
163    fn try_from(input: String) -> Result<Self, Self::Error> {
164        Self::from_str(&input)
165    }
166}
167
168impl From<PublicKey> for String {
169    fn from(input: PublicKey) -> String {
170        input.to_string()
171    }
172}
173
174#[cfg(feature = "eloquent")]
175impl eloquent::ToSql for PublicKey {
176    fn to_sql(&self) -> Result<String, eloquent::error::EloquentError> {
177        use alloc::format;
178        let hex: String = self.0.iter().map(|b| format!("{b:02X}")).collect();
179        Ok(format!("X'{hex}'"))
180    }
181}
182
183#[cfg(feature = "libsql")]
184impl libsql::params::IntoValue for PublicKey {
185    fn into_value(self) -> libsql::Result<libsql::Value> {
186        Ok(libsql::Value::Blob(self.0.to_vec()))
187    }
188}
189
190#[cfg(feature = "rocket")]
191impl<'r> rocket::request::FromParam<'r> for PublicKey {
192    type Error = KeyError;
193
194    fn from_param(input: &'r str) -> Result<Self, Self::Error> {
195        Self::from_str(input)
196    }
197}
198
199#[cfg(feature = "turso")]
200impl turso::IntoValue for PublicKey {
201    fn into_value(self) -> turso::Result<turso::Value> {
202        Ok(turso::Value::Blob(self.0.to_vec()))
203    }
204}