relay_cabi/
auth.rs

1use chrono::Duration;
2use relay_auth::{
3    generate_key_pair, generate_relay_id, PublicKey, RegisterRequest, RegisterResponse, RelayId,
4    RelayVersion, SecretKey,
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#[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 = (*s).as_str().parse()?;
33    Box::into_raw(Box::new(public_key)) as *mut RelayPublicKey
34}
35
36/// Frees a public key.
37#[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 = Box::from_raw(pk);
43    }
44}
45
46/// Converts a public key into a string.
47#[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    RelayStr::from_string((*pk).to_string())
52}
53
54/// Verifies a signature
55#[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    (*pk).verify((*data).as_bytes(), (*sig).as_str())
64}
65
66/// Verifies a signature
67#[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    (*pk).verify_timestamp((*data).as_bytes(), (*sig).as_str(), max_age)
78}
79
80/// Parses a secret key from a string.
81#[no_mangle]
82#[relay_ffi::catch_unwind]
83pub unsafe extern "C" fn relay_secretkey_parse(s: &RelayStr) -> *mut RelaySecretKey {
84    let secret_key: SecretKey = s.as_str().parse()?;
85    Box::into_raw(Box::new(secret_key)) as *mut RelaySecretKey
86}
87
88/// Frees a secret key.
89#[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 = Box::from_raw(pk);
95    }
96}
97
98/// Converts a secret key into a string.
99#[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    RelayStr::from_string((*pk).to_string())
104}
105
106/// Verifies a signature
107#[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    RelayStr::from_string((*pk).sign((*data).as_bytes()))
115}
116
117/// Generates a secret, public key pair.
118#[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/// Randomly generates an relay id
129#[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/// Creates a challenge from a register request and returns JSON.
137#[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 req =
151        RegisterRequest::bootstrap_unpack((*data).as_bytes(), (*signature).as_str(), max_age)?;
152
153    let challenge = req.into_challenge((*secret).as_str().as_bytes());
154    RelayStr::from_string(serde_json::to_string(&challenge)?)
155}
156
157#[derive(Serialize)]
158struct RelayRegisterResponse<'a> {
159    pub relay_id: RelayId,
160    pub token: &'a str,
161    pub public_key: &'a PublicKey,
162    pub version: RelayVersion,
163}
164
165/// Validates a register response.
166#[no_mangle]
167#[relay_ffi::catch_unwind]
168pub unsafe extern "C" fn relay_validate_register_response(
169    data: *const RelayBuf,
170    signature: *const RelayStr,
171    secret: *const RelayStr,
172    max_age: u32,
173) -> RelayStr {
174    let max_age = match max_age {
175        0 => None,
176        m => Some(Duration::seconds(i64::from(m))),
177    };
178
179    let (response, state) = RegisterResponse::unpack(
180        (*data).as_bytes(),
181        (*signature).as_str(),
182        (*secret).as_str().as_bytes(),
183        max_age,
184    )?;
185
186    let relay_response = RelayRegisterResponse {
187        relay_id: response.relay_id(),
188        token: response.token(),
189        public_key: state.public_key(),
190        version: response.version(),
191    };
192
193    let json = serde_json::to_string(&relay_response)?;
194    RelayStr::from_string(json)
195}
196
197/// Returns true if the given version is supported by this library.
198#[no_mangle]
199#[relay_ffi::catch_unwind]
200pub unsafe extern "C" fn relay_version_supported(version: &RelayStr) -> bool {
201    let relay_version = match version.as_str() {
202        "" => RelayVersion::default(),
203        s => s.parse::<RelayVersion>()?,
204    };
205
206    relay_version.supported()
207}