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