sodot_mpc/ffi/
conversions.rs

1use crate::{
2    ExtendedPrivateKey, ExtendedPublicKey, KeygenId, KeygenPrivateKey, PublicKey, RoomUUID, SecretShare,
3    bip340::Bip340Tweak,
4    ecdsa::{EcdsaSignature, MessageHash},
5    ffi::{AsRawFFI, ConstSlice, ConstStr, FromFFI, Room, ToFFI, bindings},
6};
7use std::{borrow::Cow, marker::PhantomData};
8
9const FFI_EXPECT: &str = "internal FFI error";
10
11impl<const N: usize> AsRawFFI for const_hex::Buffer<N, false> {
12    type FFI = bindings::ConstStr;
13
14    unsafe fn raw_ffi(&self) -> Self::FFI {
15        unsafe { ConstStr::from_str(self.as_str()).raw_ffi() }
16    }
17}
18
19impl AsRawFFI for ConstStr<'_> {
20    type FFI = bindings::ConstStr;
21    unsafe fn raw_ffi(&self) -> Self::FFI {
22        self.ffi
23    }
24}
25
26impl AsRawFFI for ConstSlice<'_, ConstStr<'_>> {
27    type FFI = bindings::ConstSlice_ConstStr;
28    unsafe fn raw_ffi(&self) -> Self::FFI {
29        // The `ptr::cast` is valid due to `#[repr(transparent)]` on `ConstStr`
30        bindings::ConstSlice_ConstStr { ptr: self.ptr.cast::<bindings::ConstStr>(), len: self.len }
31    }
32}
33
34impl AsRawFFI for ConstSlice<'_, u32> {
35    type FFI = bindings::ConstSlice_u32;
36    unsafe fn raw_ffi(&self) -> Self::FFI {
37        bindings::ConstSlice_u32 { ptr: self.ptr, len: self.len }
38    }
39}
40
41impl AsRawFFI for str {
42    type FFI = bindings::ConstStr;
43    unsafe fn raw_ffi(&self) -> Self::FFI {
44        unsafe { ConstStr::from_str(self).raw_ffi() }
45    }
46}
47
48impl AsRawFFI for String {
49    type FFI = bindings::ConstStr;
50    unsafe fn raw_ffi(&self) -> Self::FFI {
51        unsafe { ConstStr::from_str(self).raw_ffi() }
52    }
53}
54
55impl<S: AsRawFFI<FFI = bindings::ConstStr>> AsRawFFI for Option<S> {
56    type FFI = bindings::ConstStr;
57    unsafe fn raw_ffi(&self) -> Self::FFI {
58        match self {
59            Some(s) => unsafe { s.raw_ffi() },
60            None => unsafe { ConstStr::NULL.raw_ffi() },
61        }
62    }
63}
64
65impl<S: AsRawFFI + ?Sized> AsRawFFI for &'_ S {
66    type FFI = S::FFI;
67    unsafe fn raw_ffi(&self) -> Self::FFI {
68        unsafe { (*self).raw_ffi() }
69    }
70}
71
72impl<T> AsRawFFI for SecretShare<T> {
73    type FFI = bindings::ConstStr;
74    unsafe fn raw_ffi(&self) -> Self::FFI {
75        unsafe { self.as_str().raw_ffi() }
76    }
77}
78
79impl AsRawFFI for Room<'_> {
80    type FFI = bindings::Room;
81    unsafe fn raw_ffi(&self) -> Self::FFI {
82        unsafe { bindings::Room { host_url: self.host_url.raw_ffi(), uuid: self.uuid.raw_ffi() } }
83    }
84}
85
86impl<T: FromFFI> FromFFI for Option<T> {
87    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
88        let s = s.into();
89        if s.is_empty() { None } else { Some(T::parse_ffi(s)) }
90    }
91}
92
93impl<T: ToFFI> ToFFI for Option<&T>
94where
95    for<'a> Option<T::Str<'a>>: AsRawFFI<FFI = bindings::ConstStr>,
96{
97    type Str<'a>
98        = Option<T::Str<'a>>
99    where
100        Self: 'a;
101    fn ffi<'a>(&'a self) -> Self::Str<'a> {
102        self.as_ref().map(|s| s.ffi())
103    }
104}
105
106impl FromFFI for KeygenId {
107    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
108        let s = s.into();
109        KeygenId::new(s.into_owned())
110    }
111}
112
113impl ToFFI for KeygenId {
114    type Str<'a> = &'a str;
115
116    fn ffi<'a>(&'a self) -> Self::Str<'a> {
117        self.as_str()
118    }
119}
120
121impl ToFFI for [u8] {
122    type Str<'a> = String;
123
124    fn ffi<'a>(&'a self) -> Self::Str<'a> {
125        const_hex::encode(self)
126    }
127}
128
129impl FromFFI for KeygenPrivateKey {
130    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
131        Self(FromFFI::parse_ffi(s))
132    }
133}
134
135impl ToFFI for KeygenPrivateKey {
136    type Str<'a> = const_hex::Buffer<32, false>;
137
138    fn ffi<'a>(&'a self) -> Self::Str<'a> {
139        self.0.ffi()
140    }
141}
142
143impl FromFFI for RoomUUID {
144    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
145        let s = s.into();
146        RoomUUID::new(s.into_owned())
147    }
148}
149
150impl ToFFI for RoomUUID {
151    type Str<'a> = &'a str;
152
153    fn ffi<'a>(&'a self) -> Self::Str<'a> {
154        self.0.as_str()
155    }
156}
157
158impl<T> FromFFI for ExtendedPublicKey<T> {
159    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
160        Self(s.into().into_owned(), PhantomData)
161    }
162}
163
164impl<T: 'static> ToFFI for ExtendedPublicKey<T> {
165    type Str<'a> = &'a str;
166
167    fn ffi<'a>(&'a self) -> Self::Str<'a> {
168        self.0.as_str()
169    }
170}
171
172impl<T> FromFFI for ExtendedPrivateKey<T> {
173    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
174        Self(s.into().into_owned(), PhantomData)
175    }
176}
177
178impl<T: 'static> ToFFI for ExtendedPrivateKey<T> {
179    type Str<'a> = &'a str;
180
181    fn ffi<'a>(&'a self) -> Self::Str<'a> {
182        self.0.as_str()
183    }
184}
185
186impl<T> FromFFI for SecretShare<T> {
187    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
188        Self(s.into().into_owned(), PhantomData)
189    }
190}
191
192impl<T: 'static> ToFFI for SecretShare<T> {
193    type Str<'a> = &'a str;
194
195    fn ffi<'a>(&'a self) -> Self::Str<'a> {
196        self.0.as_str()
197    }
198}
199
200impl<T, const N: usize> FromFFI for PublicKey<T, N> {
201    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
202        let arr = const_hex::decode_to_array(&*s.into()).expect(FFI_EXPECT);
203        Self(arr, PhantomData)
204    }
205}
206
207impl FromFFI for [u8; 32] {
208    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
209        const_hex::decode_to_array(&*s.into()).expect(FFI_EXPECT)
210    }
211}
212
213impl ToFFI for [u8; 32] {
214    type Str<'a> = const_hex::Buffer<32, false>;
215
216    fn ffi<'a>(&'a self) -> Self::Str<'a> {
217        let mut buf = const_hex::Buffer::new();
218        buf.format(self);
219        buf
220    }
221}
222
223impl FromFFI for [u8; 64] {
224    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
225        const_hex::decode_to_array(&*s.into()).expect(FFI_EXPECT)
226    }
227}
228
229impl ToFFI for [u8; 64] {
230    type Str<'a> = const_hex::Buffer<64, false>;
231
232    fn ffi<'a>(&'a self) -> Self::Str<'a> {
233        let mut buf = const_hex::Buffer::new();
234        buf.format(self);
235        buf
236    }
237}
238
239impl FromFFI for EcdsaSignature {
240    fn parse_ffi<'a>(s: impl Into<Cow<'a, str>>) -> Self {
241        let s = s.into();
242        let decoded = const_hex::decode(s.as_ref()).expect(FFI_EXPECT);
243        assert!(
244            decoded.len() >= Self::MIN_SIZE && decoded.len() <= Self::MAX_SIZE,
245            "Invalid signature length, expected between {}..{} bytes, found: {}",
246            Self::MIN_SIZE,
247            Self::MAX_SIZE,
248            decoded.len()
249        );
250        Self { expanded_sig: decoded }
251    }
252}
253
254impl ToFFI for MessageHash {
255    type Str<'a> = const_hex::Buffer<32, false>;
256
257    fn ffi<'a>(&'a self) -> Self::Str<'a> {
258        self.0.ffi()
259    }
260}
261
262impl ToFFI for Bip340Tweak {
263    type Str<'a> = const_hex::Buffer<32, false>;
264    fn ffi(&self) -> Self::Str<'_> {
265        self.0.ffi()
266    }
267}