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