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