relay_cabi/
auth.rs

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
10/// Represents a public key in Relay.
11pub struct RelayPublicKey;
12
13/// Represents a secret key in Relay.
14pub struct RelaySecretKey;
15
16/// Represents a key pair from key generation.
17#[repr(C)]
18pub struct RelayKeyPair {
19    /// The public key used for verifying Relay signatures.
20    pub public_key: *mut RelayPublicKey,
21    /// The secret key used for signing Relay requests.
22    pub secret_key: *mut RelaySecretKey,
23}
24
25/// Represents a register request.
26pub struct RelayRegisterRequest;
27
28/// Parses a public key from a string.
29#[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/// Frees a public key.
37#[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/// Converts a public key into a string.
47#[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/// Verifies a signature
55#[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/// Verifies a signature
68#[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/// Parses a secret key from a string.
83#[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/// Frees a secret key.
91#[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/// Converts a secret key into a string.
101#[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/// Verifies a signature
109#[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/// Generates a secret, public key pair.
121#[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/// Randomly generates an relay id
132#[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/// Creates a challenge from a register request and returns JSON.
140#[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/// Validates a register response.
172#[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/// Returns true if the given version is supported by this library.
205#[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}