1use chrono::Duration;
2use relay_auth::{
3 PublicKey, RegisterRequest, RegisterResponse, RelayId, RelayVersion, SecretKey,
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 unsafe { (*pk).verify((*data).as_bytes(), (*sig).as_str()) }
64}
65
66#[unsafe(no_mangle)]
68#[relay_ffi::catch_unwind]
69pub unsafe extern "C" fn relay_publickey_verify_timestamp(
70 spk: *const RelayPublicKey,
71 data: *const RelayBuf,
72 sig: *const RelayStr,
73 max_age: u32,
74) -> bool {
75 let pk = spk as *const PublicKey;
76 let max_age = Some(Duration::seconds(i64::from(max_age)));
77 unsafe { (*pk).verify_timestamp((*data).as_bytes(), (*sig).as_str(), max_age) }
78}
79
80#[unsafe(no_mangle)]
82#[relay_ffi::catch_unwind]
83pub unsafe extern "C" fn relay_secretkey_parse(s: &RelayStr) -> *mut RelaySecretKey {
84 let secret_key: SecretKey = unsafe { s.as_str() }.parse()?;
85 Box::into_raw(Box::new(secret_key)) as *mut RelaySecretKey
86}
87
88#[unsafe(no_mangle)]
90#[relay_ffi::catch_unwind]
91pub unsafe extern "C" fn relay_secretkey_free(spk: *mut RelaySecretKey) {
92 if !spk.is_null() {
93 let pk = spk as *mut SecretKey;
94 let _dropped = unsafe { Box::from_raw(pk) };
95 }
96}
97
98#[unsafe(no_mangle)]
100#[relay_ffi::catch_unwind]
101pub unsafe extern "C" fn relay_secretkey_to_string(spk: *const RelaySecretKey) -> RelayStr {
102 let pk = spk as *const SecretKey;
103 unsafe { RelayStr::from_string((*pk).to_string()) }
104}
105
106#[unsafe(no_mangle)]
108#[relay_ffi::catch_unwind]
109pub unsafe extern "C" fn relay_secretkey_sign(
110 spk: *const RelaySecretKey,
111 data: *const RelayBuf,
112) -> RelayStr {
113 let pk = spk as *const SecretKey;
114 unsafe { RelayStr::from_string((*pk).sign((*data).as_bytes())) }
115}
116
117#[unsafe(no_mangle)]
119#[relay_ffi::catch_unwind]
120pub unsafe extern "C" fn relay_generate_key_pair() -> RelayKeyPair {
121 let (sk, pk) = generate_key_pair();
122 RelayKeyPair {
123 secret_key: Box::into_raw(Box::new(sk)) as *mut RelaySecretKey,
124 public_key: Box::into_raw(Box::new(pk)) as *mut RelayPublicKey,
125 }
126}
127
128#[unsafe(no_mangle)]
130#[relay_ffi::catch_unwind]
131pub unsafe extern "C" fn relay_generate_relay_id() -> RelayUuid {
132 let relay_id = generate_relay_id();
133 RelayUuid::new(relay_id)
134}
135
136#[unsafe(no_mangle)]
138#[relay_ffi::catch_unwind]
139pub unsafe extern "C" fn relay_create_register_challenge(
140 data: *const RelayBuf,
141 signature: *const RelayStr,
142 secret: *const RelayStr,
143 max_age: u32,
144) -> RelayStr {
145 let max_age = match max_age {
146 0 => None,
147 m => Some(Duration::seconds(i64::from(m))),
148 };
149
150 let challenge = unsafe {
151 let req =
152 RegisterRequest::bootstrap_unpack((*data).as_bytes(), (*signature).as_str(), max_age)?;
153
154 req.into_challenge((*secret).as_str().as_bytes())
155 };
156
157 RelayStr::from_string(serde_json::to_string(&challenge)?)
158}
159
160#[derive(Serialize)]
161struct RelayRegisterResponse<'a> {
162 pub relay_id: RelayId,
163 pub token: &'a str,
164 pub public_key: &'a PublicKey,
165 pub version: RelayVersion,
166}
167
168#[unsafe(no_mangle)]
170#[relay_ffi::catch_unwind]
171pub unsafe extern "C" fn relay_validate_register_response(
172 data: *const RelayBuf,
173 signature: *const RelayStr,
174 secret: *const RelayStr,
175 max_age: u32,
176) -> RelayStr {
177 let max_age = match max_age {
178 0 => None,
179 m => Some(Duration::seconds(i64::from(m))),
180 };
181
182 let (response, state) = RegisterResponse::unpack(
183 unsafe { (*data).as_bytes() },
184 unsafe { (*signature).as_str() },
185 unsafe { (*secret).as_str().as_bytes() },
186 max_age,
187 )?;
188
189 let relay_response = RelayRegisterResponse {
190 relay_id: response.relay_id(),
191 token: response.token(),
192 public_key: state.public_key(),
193 version: response.version(),
194 };
195
196 let json = serde_json::to_string(&relay_response)?;
197 RelayStr::from_string(json)
198}
199
200#[unsafe(no_mangle)]
202#[relay_ffi::catch_unwind]
203pub unsafe extern "C" fn relay_version_supported(version: &RelayStr) -> bool {
204 let relay_version = match unsafe { version.as_str() } {
205 "" => RelayVersion::default(),
206 s => s.parse::<RelayVersion>()?,
207 };
208
209 relay_version.supported()
210}