Skip to main content

triblespace_core/value/schemas/
ed25519.rs

1use ed25519::ComponentBytes;
2use ed25519::Signature;
3use ed25519_dalek::SignatureError;
4pub use ed25519_dalek::VerifyingKey;
5
6use crate::blob::schemas::longstring::LongString;
7use crate::id::ExclusiveId;
8use crate::id::Id;
9use crate::id_hex;
10use crate::macros::entity;
11use crate::metadata;
12use crate::metadata::ConstMetadata;
13use crate::repo::BlobStore;
14use crate::trible::TribleSet;
15use crate::value::schemas::hash::Blake3;
16use crate::value::FromValue;
17use crate::value::ToValue;
18use crate::value::TryFromValue;
19use crate::value::Value;
20use crate::value::ValueSchema;
21use std::convert::Infallible;
22
23#[cfg(feature = "wasm")]
24use crate::blob::schemas::wasmcode::WasmCode;
25/// A value schema for the R component of an Ed25519 signature.
26pub struct ED25519RComponent;
27
28/// A value schema for the S component of an Ed25519 signature.
29pub struct ED25519SComponent;
30
31/// A value schema for an Ed25519 public key.
32pub struct ED25519PublicKey;
33
34impl ConstMetadata for ED25519RComponent {
35    fn id() -> Id {
36        id_hex!("995A86FFC83DB95ECEAA17E226208897")
37    }
38
39    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
40    where
41        B: BlobStore<Blake3>,
42    {
43        let id = Self::id();
44        let description = blobs.put::<LongString, _>(
45            "Ed25519 signature R component stored as a 32-byte field. This is one half of the standard 64-byte Ed25519 signature.\n\nUse when you store signatures as structured values or need to index the components separately. Pair with the S component to reconstruct or verify the full signature.\n\nIf you prefer storing the signature as a single binary blob, use a blob schema (for example LongString with base64 or a custom blob schema).",
46        )?;
47        let tribles = entity! {
48            ExclusiveId::force_ref(&id) @
49                metadata::shortname: "ed25519:r",
50                metadata::description: description,
51                metadata::tag: metadata::KIND_VALUE_SCHEMA,
52        };
53
54        #[cfg(feature = "wasm")]
55        let tribles = {
56            let mut tribles = tribles;
57            tribles += entity! { ExclusiveId::force_ref(&id) @
58                metadata::value_formatter: blobs.put::<WasmCode, _>(wasm_formatter::ED25519_R_WASM)?,
59            };
60            tribles
61        };
62        Ok(tribles)
63    }
64}
65impl ValueSchema for ED25519RComponent {
66    type ValidationError = Infallible;
67}
68impl ConstMetadata for ED25519SComponent {
69    fn id() -> Id {
70        id_hex!("10D35B0B628E9E409C549D8EC1FB3598")
71    }
72
73    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
74    where
75        B: BlobStore<Blake3>,
76    {
77        let id = Self::id();
78        let description = blobs.put::<LongString, _>(
79            "Ed25519 signature S component stored as a 32-byte field. This is the second half of the standard Ed25519 signature.\n\nUse when storing or querying signatures in a structured form. Pair with the R component to reconstruct or verify the full signature.\n\nAs with the R component, treat this as public data; private signing keys should be stored separately and securely.",
80        )?;
81        let tribles = entity! {
82            ExclusiveId::force_ref(&id) @
83                metadata::shortname: "ed25519:s",
84                metadata::description: description,
85                metadata::tag: metadata::KIND_VALUE_SCHEMA,
86        };
87
88        #[cfg(feature = "wasm")]
89        let tribles = {
90            let mut tribles = tribles;
91            tribles += entity! { ExclusiveId::force_ref(&id) @
92                metadata::value_formatter: blobs.put::<WasmCode, _>(wasm_formatter::ED25519_S_WASM)?,
93            };
94            tribles
95        };
96        Ok(tribles)
97    }
98}
99impl ValueSchema for ED25519SComponent {
100    type ValidationError = Infallible;
101}
102impl ConstMetadata for ED25519PublicKey {
103    fn id() -> Id {
104        id_hex!("69A872254E01B4C1ED36E08E40445E93")
105    }
106
107    fn describe<B>(blobs: &mut B) -> Result<TribleSet, B::PutError>
108    where
109        B: BlobStore<Blake3>,
110    {
111        let id = Self::id();
112        let description = blobs.put::<LongString, _>(
113            "Ed25519 public key stored as a 32-byte field. Public keys verify signatures and identify signing identities.\n\nUse for signer registries, verification records, or key references associated with signatures. Private keys are not represented by a built-in schema and should be handled separately.\n\nEd25519 is widely supported and deterministic; if you need another scheme, define a custom schema with its own metadata.",
114        )?;
115        let tribles = entity! {
116            ExclusiveId::force_ref(&id) @
117                metadata::shortname: "ed25519:pubkey",
118                metadata::description: description,
119                metadata::tag: metadata::KIND_VALUE_SCHEMA,
120        };
121
122        #[cfg(feature = "wasm")]
123        let tribles = {
124            let mut tribles = tribles;
125            tribles += entity! { ExclusiveId::force_ref(&id) @
126                metadata::value_formatter: blobs.put::<WasmCode, _>(wasm_formatter::ED25519_PUBKEY_WASM)?,
127            };
128            tribles
129        };
130        Ok(tribles)
131    }
132}
133impl ValueSchema for ED25519PublicKey {
134    type ValidationError = Infallible;
135}
136
137#[cfg(feature = "wasm")]
138mod wasm_formatter {
139    use core::fmt::Write;
140
141    use triblespace_core_macros::value_formatter;
142
143    #[value_formatter(const_wasm = ED25519_R_WASM)]
144    pub(crate) fn ed25519_r(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
145        out.write_str("ed25519:r:").map_err(|_| 1u32)?;
146        const TABLE: &[u8; 16] = b"0123456789ABCDEF";
147        for &byte in raw {
148            let hi = (byte >> 4) as usize;
149            let lo = (byte & 0x0F) as usize;
150            out.write_char(TABLE[hi] as char).map_err(|_| 1u32)?;
151            out.write_char(TABLE[lo] as char).map_err(|_| 1u32)?;
152        }
153        Ok(())
154    }
155
156    #[value_formatter(const_wasm = ED25519_S_WASM)]
157    pub(crate) fn ed25519_s(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
158        out.write_str("ed25519:s:").map_err(|_| 1u32)?;
159        const TABLE: &[u8; 16] = b"0123456789ABCDEF";
160        for &byte in raw {
161            let hi = (byte >> 4) as usize;
162            let lo = (byte & 0x0F) as usize;
163            out.write_char(TABLE[hi] as char).map_err(|_| 1u32)?;
164            out.write_char(TABLE[lo] as char).map_err(|_| 1u32)?;
165        }
166        Ok(())
167    }
168
169    #[value_formatter(const_wasm = ED25519_PUBKEY_WASM)]
170    pub(crate) fn ed25519_pubkey(raw: &[u8; 32], out: &mut impl Write) -> Result<(), u32> {
171        out.write_str("ed25519:pubkey:").map_err(|_| 1u32)?;
172        const TABLE: &[u8; 16] = b"0123456789ABCDEF";
173        for &byte in raw {
174            let hi = (byte >> 4) as usize;
175            let lo = (byte & 0x0F) as usize;
176            out.write_char(TABLE[hi] as char).map_err(|_| 1u32)?;
177            out.write_char(TABLE[lo] as char).map_err(|_| 1u32)?;
178        }
179        Ok(())
180    }
181}
182
183impl ED25519RComponent {
184    pub fn from_signature(s: Signature) -> Value<ED25519RComponent> {
185        Value::new(*s.r_bytes())
186    }
187}
188
189impl ED25519SComponent {
190    pub fn from_signature(s: Signature) -> Value<ED25519SComponent> {
191        Value::new(*s.s_bytes())
192    }
193}
194
195impl ToValue<ED25519RComponent> for Signature {
196    fn to_value(self) -> Value<ED25519RComponent> {
197        ED25519RComponent::from_signature(self)
198    }
199}
200
201impl ToValue<ED25519SComponent> for Signature {
202    fn to_value(self) -> Value<ED25519SComponent> {
203        ED25519SComponent::from_signature(self)
204    }
205}
206
207impl ToValue<ED25519RComponent> for ComponentBytes {
208    fn to_value(self) -> Value<ED25519RComponent> {
209        Value::new(self)
210    }
211}
212
213impl FromValue<'_, ED25519RComponent> for ComponentBytes {
214    fn from_value(v: &Value<ED25519RComponent>) -> Self {
215        v.raw
216    }
217}
218
219impl ToValue<ED25519SComponent> for ComponentBytes {
220    fn to_value(self) -> Value<ED25519SComponent> {
221        Value::new(self)
222    }
223}
224
225impl FromValue<'_, ED25519SComponent> for ComponentBytes {
226    fn from_value(v: &Value<ED25519SComponent>) -> Self {
227        v.raw
228    }
229}
230
231impl ToValue<ED25519PublicKey> for VerifyingKey {
232    fn to_value(self) -> Value<ED25519PublicKey> {
233        Value::new(self.to_bytes())
234    }
235}
236
237impl TryFromValue<'_, ED25519PublicKey> for VerifyingKey {
238    type Error = SignatureError;
239
240    fn try_from_value(v: &Value<ED25519PublicKey>) -> Result<Self, Self::Error> {
241        VerifyingKey::from_bytes(&v.raw)
242    }
243}