1use derive_more::Display;
10#[cfg(zmq_has = "gssapi")]
11pub use gssapi::GssApiNametype;
12
13use crate::{
14 ZmqError, ZmqResult, sealed,
15 socket::{Socket, SocketOption},
16 zmq_sys_crate,
17};
18
19#[derive(Default, Debug, Display, PartialEq, Eq, Clone, Hash)]
20#[cfg_attr(feature = "builder", derive(serde::Deserialize, serde::Serialize))]
21#[repr(i32)]
22#[non_exhaustive]
23pub enum SecurityMechanism {
27 #[default]
28 Null,
30 #[display("Plain {{ username = {username}, password = {password} }}")]
31 Plain { username: String, password: String },
33 #[cfg(zmq_has = "curve")]
34 #[display("CurveClient {{ ... }}")]
35 CurveClient {
37 server_key: Vec<u8>,
38 public_key: Vec<u8>,
39 secret_key: Vec<u8>,
40 },
41 #[cfg(zmq_has = "curve")]
42 #[display("CurveServer {{ ... }}")]
43 CurveServer { secret_key: Vec<u8> },
45 #[cfg(zmq_has = "gssapi")]
46 #[display("GssApiClient {{ ... }}")]
47 GssApiClient { service_principal: String },
49 #[cfg(zmq_has = "gssapi")]
50 #[display("GssApiServer {{ ... }}")]
51 GssApiServer,
53}
54
55impl SecurityMechanism {
56 pub fn apply<T: sealed::SocketType>(&self, socket: &Socket<T>) -> ZmqResult<()> {
58 match self {
59 SecurityMechanism::Null => socket.set_sockopt_bool(SocketOption::PlainServer, false)?,
60 SecurityMechanism::Plain { username, password } => {
61 socket.set_sockopt_bool(SocketOption::PlainServer, true)?;
62 socket.set_sockopt_string(SocketOption::PlainUsername, username)?;
63 socket.set_sockopt_string(SocketOption::PlainPassword, password)?;
64 }
65 #[cfg(zmq_has = "curve")]
66 SecurityMechanism::CurveServer { secret_key } => {
67 socket.set_sockopt_bool(SocketOption::CurveServer, true)?;
68 socket.set_sockopt_bytes(SocketOption::CurveSecretKey, secret_key)?;
69 }
70 #[cfg(zmq_has = "curve")]
71 SecurityMechanism::CurveClient {
72 server_key,
73 public_key,
74 secret_key,
75 } => {
76 socket.set_sockopt_bytes(SocketOption::CurveServerKey, server_key)?;
77 socket.set_sockopt_bytes(SocketOption::CurvePublicKey, public_key)?;
78 socket.set_sockopt_bytes(SocketOption::CurveSecretKey, secret_key)?;
79 }
80 #[cfg(zmq_has = "gssapi")]
81 SecurityMechanism::GssApiClient { service_principal } => {
82 socket
83 .set_sockopt_string(SocketOption::GssApiServicePrincipal, service_principal)?;
84 }
85 #[cfg(zmq_has = "gssapi")]
86 SecurityMechanism::GssApiServer => {
87 socket.set_sockopt_bool(SocketOption::GssApiServer, true)?;
88 }
89 }
90 Ok(())
91 }
92}
93
94impl<T: sealed::SocketType> TryFrom<&Socket<T>> for SecurityMechanism {
95 type Error = ZmqError;
96
97 fn try_from(socket: &Socket<T>) -> Result<Self, Self::Error> {
98 match socket.get_sockopt_int::<i32>(SocketOption::Mechanism)? {
99 value if value == zmq_sys_crate::ZMQ_NULL as i32 => Ok(Self::Null),
100 value if value == zmq_sys_crate::ZMQ_PLAIN as i32 => {
101 let username = socket.get_sockopt_string(SocketOption::PlainUsername)?;
102 let password = socket.get_sockopt_string(SocketOption::PlainPassword)?;
103 Ok(Self::Plain { username, password })
104 }
105 #[cfg(zmq_has = "curve")]
106 value if value == zmq_sys_crate::ZMQ_CURVE as i32 => {
107 let secret_key = socket.get_sockopt_curve(SocketOption::CurveSecretKey)?;
108 if socket.get_sockopt_bool(SocketOption::CurveServer)? {
109 Ok(Self::CurveServer { secret_key })
110 } else {
111 let server_key = socket.get_sockopt_curve(SocketOption::CurveServerKey)?;
112 let public_key = socket.get_sockopt_curve(SocketOption::CurvePublicKey)?;
113 Ok(Self::CurveClient {
114 server_key,
115 public_key,
116 secret_key,
117 })
118 }
119 }
120 #[cfg(zmq_has = "gssapi")]
121 value if value == zmq_sys_crate::ZMQ_GSSAPI as i32 => {
122 if socket.get_sockopt_bool(SocketOption::GssApiServer)? {
123 Ok(Self::GssApiServer)
124 } else {
125 let service_principal =
126 socket.get_sockopt_string(SocketOption::GssApiServicePrincipal)?;
127 Ok(Self::GssApiClient { service_principal })
128 }
129 }
130 _ => Err(ZmqError::Unsupported),
131 }
132 }
133}
134
135#[cfg(test)]
136mod security_mechanism_tests {
137 use super::SecurityMechanism;
138 #[cfg(zmq_has = "curve")]
139 use super::curve::curve_keypair;
140 use crate::{
141 prelude::{Context, DealerSocket, SocketOption, ZmqResult},
142 zmq_sys_crate,
143 };
144
145 #[test]
146 fn apply_null_security() -> ZmqResult<()> {
147 let context = Context::new()?;
148
149 let socket = DealerSocket::from_context(&context)?;
150
151 SecurityMechanism::Null.apply(&socket)?;
152
153 assert_eq!(
154 socket.get_sockopt_int::<i32>(SocketOption::Mechanism)?,
155 zmq_sys_crate::ZMQ_NULL as i32
156 );
157
158 Ok(())
159 }
160
161 #[test]
162 fn apply_plain_security() -> ZmqResult<()> {
163 let context = Context::new()?;
164
165 let socket = DealerSocket::from_context(&context)?;
166 let security = SecurityMechanism::Plain {
167 username: "username".to_string(),
168 password: "password".to_string(),
169 };
170
171 security.apply(&socket)?;
172
173 assert_eq!(
174 socket.get_sockopt_int::<i32>(SocketOption::Mechanism)?,
175 zmq_sys_crate::ZMQ_PLAIN as i32
176 );
177 assert_eq!(
178 socket.get_sockopt_string(SocketOption::PlainUsername)?,
179 "username"
180 );
181 assert_eq!(
182 socket.get_sockopt_string(SocketOption::PlainPassword)?,
183 "password"
184 );
185
186 Ok(())
187 }
188
189 #[cfg(zmq_has = "curve")]
190 #[test]
191 fn apply_curve_server_security() -> ZmqResult<()> {
192 let (_, secret_key) = curve_keypair()?;
193
194 let context = Context::new()?;
195
196 let socket = DealerSocket::from_context(&context)?;
197 let security = SecurityMechanism::CurveServer {
198 secret_key: secret_key.clone(),
199 };
200 security.apply(&socket)?;
201
202 assert_eq!(
203 socket.get_sockopt_int::<i32>(SocketOption::Mechanism)?,
204 zmq_sys_crate::ZMQ_CURVE as i32
205 );
206 assert!(socket.get_sockopt_bool(SocketOption::CurveServer)?);
207 assert_eq!(
208 socket.get_sockopt_curve(SocketOption::CurveSecretKey)?,
209 secret_key
210 );
211
212 Ok(())
213 }
214
215 #[cfg(zmq_has = "curve")]
216 #[test]
217 fn apply_curve_client_security() -> ZmqResult<()> {
218 let (_, server_key) = curve_keypair()?;
219 let (public_key, secret_key) = curve_keypair()?;
220
221 let context = Context::new()?;
222
223 let socket = DealerSocket::from_context(&context)?;
224 let security = SecurityMechanism::CurveClient {
225 server_key: server_key.clone(),
226 public_key: public_key.clone(),
227 secret_key: secret_key.clone(),
228 };
229 security.apply(&socket)?;
230
231 assert_eq!(
232 socket.get_sockopt_int::<i32>(SocketOption::Mechanism)?,
233 zmq_sys_crate::ZMQ_CURVE as i32
234 );
235 assert!(!socket.get_sockopt_bool(SocketOption::CurveServer)?);
236 assert_eq!(
237 socket.get_sockopt_curve(SocketOption::CurveServerKey)?,
238 server_key
239 );
240 assert_eq!(
241 socket.get_sockopt_curve(SocketOption::CurvePublicKey)?,
242 public_key
243 );
244 assert_eq!(
245 socket.get_sockopt_curve(SocketOption::CurveSecretKey)?,
246 secret_key
247 );
248
249 Ok(())
250 }
251
252 #[cfg(zmq_has = "gssapi")]
253 #[test]
254 fn apply_gssapi_server_security() -> ZmqResult<()> {
255 let context = Context::new()?;
256
257 let socket = DealerSocket::from_context(&context)?;
258 let security = SecurityMechanism::GssApiServer;
259 security.apply(&socket)?;
260
261 assert!(socket.get_sockopt_bool(SocketOption::GssApiServer)?);
262
263 Ok(())
264 }
265
266 #[cfg(zmq_has = "gssapi")]
267 #[test]
268 fn apply_gssapi_client_security() -> ZmqResult<()> {
269 let context = Context::new()?;
270
271 let socket = DealerSocket::from_context(&context)?;
272 let security = SecurityMechanism::GssApiClient {
273 service_principal: "service_principal".to_string(),
274 };
275 security.apply(&socket)?;
276
277 assert_eq!(
278 socket.get_sockopt_string(SocketOption::GssApiServicePrincipal)?,
279 "service_principal"
280 );
281
282 Ok(())
283 }
284
285 #[test]
286 fn try_from_socket_with_no_security() -> ZmqResult<()> {
287 let context = Context::new()?;
288
289 let socket = DealerSocket::from_context(&context)?;
290
291 assert_eq!(
292 SecurityMechanism::try_from(&socket)?,
293 SecurityMechanism::Null
294 );
295
296 Ok(())
297 }
298
299 #[test]
300 fn try_from_socket_with_plain_security() -> ZmqResult<()> {
301 let context = Context::new()?;
302
303 let socket = DealerSocket::from_context(&context)?;
304 socket.set_sockopt_string(SocketOption::PlainUsername, "username")?;
305 socket.set_sockopt_string(SocketOption::PlainPassword, "password")?;
306
307 assert_eq!(
308 SecurityMechanism::try_from(&socket)?,
309 SecurityMechanism::Plain {
310 username: "username".to_string(),
311 password: "password".to_string(),
312 }
313 );
314
315 Ok(())
316 }
317
318 #[cfg(zmq_has = "curve")]
319 #[test]
320 fn try_from_socket_with_curve_security() -> ZmqResult<()> {
321 let (_, secret_key) = curve_keypair()?;
322
323 let context = Context::new()?;
324
325 let socket = DealerSocket::from_context(&context)?;
326
327 socket.set_sockopt_bytes(SocketOption::CurveSecretKey, secret_key.clone())?;
328 socket.set_sockopt_bool(SocketOption::CurveServer, true)?;
329 assert_eq!(
330 SecurityMechanism::try_from(&socket)?,
331 SecurityMechanism::CurveServer {
332 secret_key: secret_key.clone(),
333 }
334 );
335
336 Ok(())
337 }
338
339 #[cfg(zmq_has = "curve")]
340 #[test]
341 fn try_from_socket_with_curve_client_security() -> ZmqResult<()> {
342 let (_, server_key) = curve_keypair()?;
343 let (public_key, secret_key) = curve_keypair()?;
344
345 let context = Context::new()?;
346
347 let socket = DealerSocket::from_context(&context)?;
348 socket.set_sockopt_bool(SocketOption::CurveServer, false)?;
349 socket.set_sockopt_bytes(SocketOption::CurveServerKey, server_key.clone())?;
350 socket.set_sockopt_bytes(SocketOption::CurvePublicKey, public_key.clone())?;
351 socket.set_sockopt_bytes(SocketOption::CurveSecretKey, secret_key.clone())?;
352 assert_eq!(
353 SecurityMechanism::try_from(&socket)?,
354 SecurityMechanism::CurveClient {
355 server_key: server_key.clone(),
356 public_key: public_key.clone(),
357 secret_key: secret_key.clone(),
358 }
359 );
360
361 Ok(())
362 }
363
364 #[cfg(zmq_has = "gssapi")]
365 #[test]
366 fn try_from_socket_with_gssapi_security() -> ZmqResult<()> {
367 let context = Context::new()?;
368
369 let socket = DealerSocket::from_context(&context)?;
370 socket.set_sockopt_string(SocketOption::GssApiServicePrincipal, "service_principal")?;
371 socket.set_sockopt_bool(SocketOption::GssApiServer, true)?;
372 assert_eq!(
373 SecurityMechanism::try_from(&socket)?,
374 SecurityMechanism::GssApiServer
375 );
376
377 Ok(())
378 }
379
380 #[cfg(zmq_has = "gssapi")]
381 #[test]
382 fn try_from_socket_with_gssapi_client_security() -> ZmqResult<()> {
383 let context = Context::new()?;
384
385 let socket = DealerSocket::from_context(&context)?;
386 socket.set_sockopt_string(SocketOption::GssApiServicePrincipal, "service_principal")?;
387 socket.set_sockopt_bool(SocketOption::GssApiServer, false)?;
388 assert_eq!(
389 SecurityMechanism::try_from(&socket)?,
390 SecurityMechanism::GssApiClient {
391 service_principal: "service_principal".to_string()
392 }
393 );
394
395 Ok(())
396 }
397}
398
399#[cfg(zmq_has = "curve")]
401pub mod curve {
402 use alloc::ffi::CString;
403 use core::ffi::c_char;
404 #[cfg(nightly)]
405 use core::hint::cold_path;
406
407 use derive_more::Display;
408 use thiserror::Error;
409
410 use crate::{
411 prelude::{ZmqError, ZmqResult},
412 zmq_sys_crate,
413 };
414
415 #[derive(Debug, PartialEq, Eq, Clone, Hash, Error, Display)]
416 pub enum EncodeError {
418 BadLength,
420 EncodingFailed,
422 Utf8Error,
424 }
425
426 pub fn encode<T>(data: T) -> Result<String, EncodeError>
434 where
435 T: AsRef<[u8]>,
436 {
437 let input = data.as_ref();
438 let input_len = input.len();
439 if input_len % 4 != 0 {
440 return Err(EncodeError::BadLength);
441 }
442
443 let len = input_len * 5 / 4 + 1;
444 let mut dest = vec![0u8; len];
445
446 if unsafe {
447 zmq_sys_crate::zmq_z85_encode(
448 dest.as_mut_ptr() as *mut c_char,
449 input.as_ptr(),
450 input.len(),
451 )
452 }
453 .is_null()
454 {
455 #[cfg(nightly)]
456 cold_path();
457 return Err(EncodeError::EncodingFailed);
458 }
459
460 dest.truncate(len - 1);
461 String::from_utf8(dest).map_err(|_| EncodeError::Utf8Error)
462 }
463
464 #[cfg(test)]
465 mod z85_encode_tests {
466 use super::{EncodeError, encode};
467
468 #[test]
469 fn z85_encode_for_empty_input() -> Result<(), EncodeError> {
470 let encoded_string = encode(vec![])?;
471 assert_eq!(encoded_string, "");
472 Ok(())
473 }
474
475 #[test]
476 fn z85_encode_for_invalid_input_length() {
477 let result = encode(b"a");
478 assert!(result.is_err_and(|err| err == EncodeError::BadLength));
479 }
480
481 #[test]
482 fn z85_encode_for_valid_input() -> Result<(), EncodeError> {
483 let encoded_string = encode(b"Hello World!")?;
484 assert_eq!(encoded_string, "nm=QNzY&b1A+]nf");
485
486 Ok(())
487 }
488 }
489
490 #[derive(Debug, PartialEq, Eq, Clone, Hash, Error, Display)]
491 pub enum DecodeError {
493 InvalidLength,
495 DecodingFailed,
497 }
498
499 pub fn decode<T>(string: T) -> Result<Vec<u8>, DecodeError>
506 where
507 T: AsRef<str>,
508 {
509 let input = string.as_ref();
510 let input_len = input.len();
511 if input_len == 0 {
512 return Ok(vec![]);
513 }
514
515 if input_len % 5 != 0 {
516 return Err(DecodeError::InvalidLength);
517 }
518
519 let dest_len = input_len * 4 / 5;
520 let mut dest = vec![0; dest_len];
521
522 let c_str = CString::new(input).map_err(|_| DecodeError::DecodingFailed)?;
523
524 if unsafe { zmq_sys_crate::zmq_z85_decode(dest.as_mut_ptr(), c_str.into_raw()) }.is_null() {
525 #[cfg(nightly)]
526 cold_path();
527 return Err(DecodeError::DecodingFailed);
528 }
529
530 Ok(dest)
531 }
532
533 #[cfg(test)]
534 mod z85_decode_tests {
535 use super::{DecodeError, decode};
536
537 #[test]
538 fn z85_decode_z85_encoded_string() -> Result<(), DecodeError> {
539 let encoded_string = "nm=QNzY&b1A+]nf";
540 let decoded_string = decode(encoded_string)?;
541
542 assert_eq!(decoded_string, b"Hello World!");
543
544 Ok(())
545 }
546
547 #[test]
548 fn z85_decode_for_empty_input() -> Result<(), DecodeError> {
549 let encoded_string = "";
550 let decoded_string = decode(encoded_string)?;
551
552 assert_eq!(decoded_string, vec![]);
553
554 Ok(())
555 }
556
557 #[test]
558 fn z85_decode_for_invalid_input_length() {
559 let encoded_string = "a";
560 let result = decode(encoded_string);
561
562 assert!(result.is_err_and(|err| err == DecodeError::InvalidLength));
563 }
564 }
565
566 pub fn curve_keypair() -> ZmqResult<(Vec<u8>, Vec<u8>)> {
574 let mut public_key: [u8; 41] = [0; 41];
575 let mut secret_key: [u8; 41] = [0; 41];
576
577 if unsafe {
578 zmq_sys_crate::zmq_curve_keypair(
579 public_key.as_mut_ptr() as *mut c_char,
580 secret_key.as_mut_ptr() as *mut c_char,
581 )
582 } == -1
583 {
584 #[cfg(nightly)]
585 cold_path();
586 match unsafe { zmq_sys_crate::zmq_errno() } {
587 errno @ zmq_sys_crate::errno::ENOTSUP => return Err(ZmqError::from(errno)),
588 _ => unreachable!(),
589 }
590 }
591
592 Ok((public_key.to_vec(), secret_key.to_vec()))
593 }
594
595 pub fn curve_public<T>(mut secret_key: T) -> ZmqResult<Vec<u8>>
603 where
604 T: AsMut<[u8]>,
605 {
606 let mut public_key: [u8; 41] = [0; 41];
607 let secret_key_array = secret_key.as_mut();
608
609 if unsafe {
610 zmq_sys_crate::zmq_curve_public(
611 public_key.as_mut_ptr() as *mut c_char,
612 secret_key_array.as_ptr() as *const c_char,
613 )
614 } == -1
615 {
616 #[cfg(nightly)]
617 cold_path();
618 match unsafe { zmq_sys_crate::zmq_errno() } {
619 errno @ zmq_sys_crate::errno::ENOTSUP => return Err(ZmqError::from(errno)),
620 _ => unreachable!(),
621 }
622 }
623
624 Ok(public_key.to_vec())
625 }
626
627 #[cfg(test)]
628 mod curve_keypair_tests {
629 use super::{curve_keypair, curve_public};
630 use crate::prelude::ZmqResult;
631
632 #[test]
633 fn curve_keypair_generate_curve_keypair() -> ZmqResult<()> {
634 let (public_key, secret_key) = curve_keypair()?;
635
636 let pub_key = curve_public(secret_key)?;
637
638 assert_eq!(public_key, pub_key);
639
640 Ok(())
641 }
642 }
643}
644
645#[cfg(zmq_has = "gssapi")]
646mod gssapi {
647 use derive_more::Display;
648
649 use crate::{prelude::ZmqError, zmq_sys_crate};
650
651 #[derive(Debug, Display, PartialEq, Eq, Clone, Hash)]
652 #[repr(i32)]
653 pub enum GssApiNametype {
655 NtHostbased,
657 NtUsername,
659 NtKrb5Principal,
662 }
663
664 impl TryFrom<i32> for GssApiNametype {
665 type Error = ZmqError;
666
667 fn try_from(value: i32) -> Result<Self, Self::Error> {
668 match value {
669 _ if value == zmq_sys_crate::ZMQ_GSSAPI_NT_HOSTBASED as i32 => {
670 Ok(Self::NtHostbased)
671 }
672 _ if value == zmq_sys_crate::ZMQ_GSSAPI_NT_USER_NAME as i32 => Ok(Self::NtUsername),
673 _ if value == zmq_sys_crate::ZMQ_GSSAPI_NT_KRB5_PRINCIPAL as i32 => {
674 Ok(Self::NtKrb5Principal)
675 }
676 _ => Err(ZmqError::Unsupported),
677 }
678 }
679 }
680
681 #[cfg(test)]
682 mod gss_api_nametype_tests {
683 use rstest::*;
684
685 use super::GssApiNametype;
686 use crate::{
687 prelude::{ZmqError, ZmqResult},
688 zmq_sys_crate,
689 };
690
691 #[rstest]
692 #[case(zmq_sys_crate::ZMQ_GSSAPI_NT_HOSTBASED as i32, Ok(GssApiNametype::NtHostbased))]
693 #[case(zmq_sys_crate::ZMQ_GSSAPI_NT_USER_NAME as i32, Ok(GssApiNametype::NtUsername))]
694 #[case(zmq_sys_crate::ZMQ_GSSAPI_NT_KRB5_PRINCIPAL as i32, Ok(GssApiNametype::NtKrb5Principal)
695 )]
696 #[case(666, Err(ZmqError::Unsupported))]
697 fn nametype_try_from(#[case] value: i32, #[case] expected: ZmqResult<GssApiNametype>) {
698 assert_eq!(expected, GssApiNametype::try_from(value));
699 }
700 }
701}