Skip to main content

relay_cabi/
auth.rs

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
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 {
65        (*pk)
66            .verify((*data).as_bytes(), signature, Utc::now(), Duration::MAX)
67            .is_ok()
68    }
69}
70
71/// Verifies a signature
72#[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/// Parses a secret key from a string.
91#[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/// Frees a secret key.
99#[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/// Converts a secret key into a string.
109#[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/// Verifies a signature
117#[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/// Generates a secret, public key pair.
129#[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/// Randomly generates an relay id
140#[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/// Creates a challenge from a register request and returns JSON.
148#[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/// Validates a register response.
181#[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/// Returns true if the given version is supported by this library.
215#[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}