1use chrono::Duration;
2use relay_auth::{
3 PublicKey, RegisterRequest, RegisterResponse, RelayId, RelayVersion, SecretKey, SignatureRef,
4 generate_key_pair, generate_relay_id,
5};
6use serde::Serialize;
7
8use crate::core::{RelayBuf, RelayStr, RelayUuid};
9
10pub struct RelayPublicKey;
12
13pub struct RelaySecretKey;
15
16#[repr(C)]
18pub struct RelayKeyPair {
19 pub public_key: *mut RelayPublicKey,
21 pub secret_key: *mut RelaySecretKey,
23}
24
25pub struct RelayRegisterRequest;
27
28#[unsafe(no_mangle)]
30#[relay_ffi::catch_unwind]
31pub unsafe extern "C" fn relay_publickey_parse(s: *const RelayStr) -> *mut RelayPublicKey {
32 let public_key: PublicKey = unsafe { (*s).as_str() }.parse()?;
33 Box::into_raw(Box::new(public_key)) as *mut RelayPublicKey
34}
35
36#[unsafe(no_mangle)]
38#[relay_ffi::catch_unwind]
39pub unsafe extern "C" fn relay_publickey_free(spk: *mut RelayPublicKey) {
40 if !spk.is_null() {
41 let pk = spk as *mut PublicKey;
42 let _dropped = unsafe { Box::from_raw(pk) };
43 }
44}
45
46#[unsafe(no_mangle)]
48#[relay_ffi::catch_unwind]
49pub unsafe extern "C" fn relay_publickey_to_string(spk: *const RelayPublicKey) -> RelayStr {
50 let pk = spk as *const PublicKey;
51 unsafe { RelayStr::from_string((*pk).to_string()) }
52}
53
54#[unsafe(no_mangle)]
56#[relay_ffi::catch_unwind]
57pub unsafe extern "C" fn relay_publickey_verify(
58 spk: *const RelayPublicKey,
59 data: *const RelayBuf,
60 sig: *const RelayStr,
61) -> bool {
62 let pk = spk as *const PublicKey;
63 let signature = SignatureRef(unsafe { (*sig).as_str() });
64 unsafe { (*pk).verify((*data).as_bytes(), signature) }
65}
66
67#[unsafe(no_mangle)]
69#[relay_ffi::catch_unwind]
70pub unsafe extern "C" fn relay_publickey_verify_timestamp(
71 spk: *const RelayPublicKey,
72 data: *const RelayBuf,
73 sig: *const RelayStr,
74 max_age: u32,
75) -> bool {
76 let pk = spk as *const PublicKey;
77 let max_age = Some(Duration::seconds(i64::from(max_age)));
78 let signature = SignatureRef(unsafe { (*sig).as_str() });
79 unsafe { (*pk).verify_timestamp((*data).as_bytes(), signature, max_age) }
80}
81
82#[unsafe(no_mangle)]
84#[relay_ffi::catch_unwind]
85pub unsafe extern "C" fn relay_secretkey_parse(s: &RelayStr) -> *mut RelaySecretKey {
86 let secret_key: SecretKey = unsafe { s.as_str() }.parse()?;
87 Box::into_raw(Box::new(secret_key)) as *mut RelaySecretKey
88}
89
90#[unsafe(no_mangle)]
92#[relay_ffi::catch_unwind]
93pub unsafe extern "C" fn relay_secretkey_free(spk: *mut RelaySecretKey) {
94 if !spk.is_null() {
95 let pk = spk as *mut SecretKey;
96 let _dropped = unsafe { Box::from_raw(pk) };
97 }
98}
99
100#[unsafe(no_mangle)]
102#[relay_ffi::catch_unwind]
103pub unsafe extern "C" fn relay_secretkey_to_string(spk: *const RelaySecretKey) -> RelayStr {
104 let pk = spk as *const SecretKey;
105 unsafe { RelayStr::from_string((*pk).to_string()) }
106}
107
108#[unsafe(no_mangle)]
110#[relay_ffi::catch_unwind]
111pub unsafe extern "C" fn relay_secretkey_sign(
112 spk: *const RelaySecretKey,
113 data: *const RelayBuf,
114) -> RelayStr {
115 let pk = spk as *const SecretKey;
116 let signature = unsafe { (*pk).sign((*data).as_bytes()) };
117 RelayStr::from_string(signature.0)
118}
119
120#[unsafe(no_mangle)]
122#[relay_ffi::catch_unwind]
123pub unsafe extern "C" fn relay_generate_key_pair() -> RelayKeyPair {
124 let (sk, pk) = generate_key_pair();
125 RelayKeyPair {
126 secret_key: Box::into_raw(Box::new(sk)) as *mut RelaySecretKey,
127 public_key: Box::into_raw(Box::new(pk)) as *mut RelayPublicKey,
128 }
129}
130
131#[unsafe(no_mangle)]
133#[relay_ffi::catch_unwind]
134pub unsafe extern "C" fn relay_generate_relay_id() -> RelayUuid {
135 let relay_id = generate_relay_id();
136 RelayUuid::new(relay_id)
137}
138
139#[unsafe(no_mangle)]
141#[relay_ffi::catch_unwind]
142pub unsafe extern "C" fn relay_create_register_challenge(
143 data: *const RelayBuf,
144 signature: *const RelayStr,
145 secret: *const RelayStr,
146 max_age: u32,
147) -> RelayStr {
148 let max_age = match max_age {
149 0 => None,
150 m => Some(Duration::seconds(i64::from(m))),
151 };
152 let signature = SignatureRef(unsafe { (*signature).as_str() });
153
154 let challenge = unsafe {
155 let req = RegisterRequest::bootstrap_unpack((*data).as_bytes(), signature, max_age)?;
156
157 req.into_challenge((*secret).as_str().as_bytes())
158 };
159
160 RelayStr::from_string(serde_json::to_string(&challenge)?)
161}
162
163#[derive(Serialize)]
164struct RelayRegisterResponse<'a> {
165 pub relay_id: RelayId,
166 pub token: &'a str,
167 pub public_key: &'a PublicKey,
168 pub version: RelayVersion,
169}
170
171#[unsafe(no_mangle)]
173#[relay_ffi::catch_unwind]
174pub unsafe extern "C" fn relay_validate_register_response(
175 data: *const RelayBuf,
176 signature: *const RelayStr,
177 secret: *const RelayStr,
178 max_age: u32,
179) -> RelayStr {
180 let max_age = match max_age {
181 0 => None,
182 m => Some(Duration::seconds(i64::from(m))),
183 };
184
185 let signature = SignatureRef(unsafe { (*signature).as_str() });
186 let (response, state) = RegisterResponse::unpack(
187 unsafe { (*data).as_bytes() },
188 signature,
189 unsafe { (*secret).as_str().as_bytes() },
190 max_age,
191 )?;
192
193 let relay_response = RelayRegisterResponse {
194 relay_id: response.relay_id(),
195 token: response.token(),
196 public_key: state.public_key(),
197 version: response.version(),
198 };
199
200 let json = serde_json::to_string(&relay_response)?;
201 RelayStr::from_string(json)
202}
203
204#[unsafe(no_mangle)]
206#[relay_ffi::catch_unwind]
207pub unsafe extern "C" fn relay_version_supported(version: &RelayStr) -> bool {
208 let relay_version = match unsafe { version.as_str() } {
209 "" => RelayVersion::default(),
210 s => s.parse::<RelayVersion>()?,
211 };
212
213 relay_version.supported()
214}