1use alloc::collections::{BTreeMap, BTreeSet};
16use alloc::vec::Vec;
17use core::sync::atomic::{AtomicU64, Ordering};
18
19use ring::rand::{SecureRandom, SystemRandom};
20use ring::signature;
21use rustls_pki_types::CertificateDer;
22use zerodds_security::authentication::{
23 AuthenticationPlugin, HandshakeHandle, HandshakeStepOutcome, IdentityHandle,
24 SharedSecretHandle, SharedSecretProvider,
25};
26use zerodds_security::error::{SecurityError, SecurityErrorKind, SecurityResult};
27use zerodds_security::properties::PropertyList;
28use zerodds_security_keyexchange::KeyExchange;
29
30use crate::handshake_token::{
31 self as ht, FinalBuildInput, ReplyBuildInput, RequestBuildInput, ct_eq, signing_bytes,
32};
33use crate::identity::{CertKeyAlgo, IdentityConfig, ParsedIdentity, PkiError};
34
35mod keys {
39 pub const IDENTITY_CERT: &str = "dds.sec.auth.identity_certificate";
43 pub const IDENTITY_CA: &str = "dds.sec.auth.identity_ca";
45 pub const IDENTITY_KEY: &str = "dds.sec.auth.private_key";
47}
48
49const REPLAY_CACHE_CAP: usize = 1024;
52
53pub struct PkiAuthenticationPlugin {
64 next_handle: AtomicU64,
65 identities: BTreeMap<IdentityHandle, ParsedIdentity>,
66 pending_initiator: BTreeMap<HandshakeHandle, InitiatorState>,
68 pending_replier: BTreeMap<HandshakeHandle, ReplierState>,
70 handshake_to_secret: BTreeMap<HandshakeHandle, SharedSecretHandle>,
72 secrets: BTreeMap<SharedSecretHandle, Vec<u8>>,
74 replay_cache: BTreeMap<IdentityHandle, BTreeSet<[u8; 32]>>,
77 replay_order: BTreeMap<IdentityHandle, Vec<[u8; 32]>>,
79}
80
81struct InitiatorState {
82 local: IdentityHandle,
84 kx: Option<KeyExchange>,
86 dh1: Vec<u8>,
88 challenge1: [u8; 32],
90 hash_c1: [u8; 32],
92 #[allow(dead_code)]
96 permissions: Vec<u8>,
97 #[allow(dead_code)]
99 pdata: Vec<u8>,
100 kagree_algo: alloc::string::String,
102 #[allow(dead_code)]
106 dsign_algo: alloc::string::String,
107}
108
109struct ReplierState {
110 local: IdentityHandle,
112 kagree_algo: alloc::string::String,
114 challenge1: [u8; 32],
116 challenge2: [u8; 32],
118 dh1: Vec<u8>,
120 dh2: Vec<u8>,
122 hash_c1: [u8; 32],
124 hash_c2: [u8; 32],
126 secret_handle: SharedSecretHandle,
128 initiator_cert_der: Vec<u8>,
130 initiator_key_algo: CertKeyAlgo,
132}
133
134impl Default for PkiAuthenticationPlugin {
135 fn default() -> Self {
136 Self::new()
137 }
138}
139
140impl PkiAuthenticationPlugin {
141 #[must_use]
143 pub fn new() -> Self {
144 Self {
145 next_handle: AtomicU64::new(0),
146 identities: BTreeMap::new(),
147 pending_initiator: BTreeMap::new(),
148 pending_replier: BTreeMap::new(),
149 handshake_to_secret: BTreeMap::new(),
150 secrets: BTreeMap::new(),
151 replay_cache: BTreeMap::new(),
152 replay_order: BTreeMap::new(),
153 }
154 }
155
156 fn next_id(&self) -> u64 {
157 self.next_handle.fetch_add(1, Ordering::Relaxed) + 1
158 }
159
160 pub fn validate_with_config(
167 &mut self,
168 cfg: IdentityConfig,
169 _participant_guid: [u8; 16],
170 ) -> SecurityResult<IdentityHandle> {
171 let parsed = ParsedIdentity::from_config(&cfg).map_err(pki_to_security)?;
172 let handle = IdentityHandle(self.next_id());
173 self.identities.insert(handle, parsed);
174 Ok(handle)
175 }
176
177 #[must_use]
182 pub fn secret_bytes(&self, handle: SharedSecretHandle) -> Option<&[u8]> {
183 self.secrets.get(&handle).map(Vec::as_slice)
184 }
185
186 fn store_secret(&mut self, _h: HandshakeHandle, bytes: Vec<u8>) -> SharedSecretHandle {
187 let handle = SharedSecretHandle(self.next_id());
188 self.secrets.insert(handle, bytes);
189 handle
190 }
191
192 fn record_challenge(&mut self, local: IdentityHandle, c: [u8; 32]) -> SecurityResult<()> {
193 let cache = self.replay_cache.entry(local).or_default();
194 if cache.contains(&c) {
195 return Err(SecurityError::new(
196 SecurityErrorKind::AuthenticationFailed,
197 "pki: replayed challenge1 detected",
198 ));
199 }
200 cache.insert(c);
201 let order = self.replay_order.entry(local).or_default();
202 order.push(c);
203 if order.len() > REPLAY_CACHE_CAP {
204 let dropped = order.remove(0);
205 cache.remove(&dropped);
206 }
207 Ok(())
208 }
209}
210
211impl SharedSecretProvider for PkiAuthenticationPlugin {
212 fn get_shared_secret(&self, handle: SharedSecretHandle) -> Option<Vec<u8>> {
213 self.secrets.get(&handle).cloned()
214 }
215}
216
217fn pki_to_security(e: PkiError) -> SecurityError {
218 let kind = match &e {
219 PkiError::InvalidPem(_) | PkiError::NoCertInPem => SecurityErrorKind::BadArgument,
220 PkiError::CertInvalid(_) => SecurityErrorKind::AuthenticationFailed,
221 PkiError::EmptyTrustAnchors => SecurityErrorKind::InvalidConfiguration,
222 };
223 SecurityError::new(kind, alloc::format!("pki: {e}"))
224}
225
226fn random_challenge() -> SecurityResult<[u8; 32]> {
227 let rng = SystemRandom::new();
228 let mut buf = [0u8; 32];
229 rng.fill(&mut buf).map_err(|_| {
230 SecurityError::new(
231 SecurityErrorKind::CryptoFailed,
232 "pki: SystemRandom not available",
233 )
234 })?;
235 Ok(buf)
236}
237
238fn algo_for(key_algo: CertKeyAlgo) -> SecurityResult<&'static str> {
239 match key_algo {
240 CertKeyAlgo::EcdsaP256Sha256 => Ok(ht::algo::ECDSA_SHA256),
241 CertKeyAlgo::RsaPssSha256 => Ok(ht::algo::RSASSA_PSS_SHA256),
242 CertKeyAlgo::Unknown => Err(SecurityError::new(
243 SecurityErrorKind::InvalidConfiguration,
244 "pki: cert public-key algo unsupported",
245 )),
246 }
247}
248
249fn check_dsign_matches(declared: &str, detected: CertKeyAlgo) -> SecurityResult<()> {
250 let expected = algo_for(detected)?;
251 if !declared.eq_ignore_ascii_case(expected) {
252 return Err(SecurityError::new(
253 SecurityErrorKind::InvalidConfiguration,
254 alloc::format!("pki: c.dsign_algo {declared} doesn't match cert (expected {expected})"),
255 ));
256 }
257 Ok(())
258}
259
260fn sign_with(key_algo: CertKeyAlgo, pkcs8: &[u8], msg: &[u8]) -> SecurityResult<Vec<u8>> {
261 let rng = SystemRandom::new();
262 match key_algo {
263 CertKeyAlgo::EcdsaP256Sha256 => {
264 let key = signature::EcdsaKeyPair::from_pkcs8(
265 &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
266 pkcs8,
267 &rng,
268 )
269 .map_err(|e| {
270 SecurityError::new(
271 SecurityErrorKind::CryptoFailed,
272 alloc::format!("pki: ecdsa key-parse failed: {e}"),
273 )
274 })?;
275 let sig = key.sign(&rng, msg).map_err(|e| {
276 SecurityError::new(
277 SecurityErrorKind::CryptoFailed,
278 alloc::format!("pki: ecdsa sign failed: {e}"),
279 )
280 })?;
281 Ok(sig.as_ref().to_vec())
282 }
283 CertKeyAlgo::RsaPssSha256 => {
284 let key = signature::RsaKeyPair::from_pkcs8(pkcs8).map_err(|e| {
285 SecurityError::new(
286 SecurityErrorKind::CryptoFailed,
287 alloc::format!("pki: rsa key-parse failed: {e}"),
288 )
289 })?;
290 let mut out = alloc::vec![0u8; key.public().modulus_len()];
291 key.sign(&signature::RSA_PSS_SHA256, &rng, msg, &mut out)
292 .map_err(|e| {
293 SecurityError::new(
294 SecurityErrorKind::CryptoFailed,
295 alloc::format!("pki: rsa sign failed: {e}"),
296 )
297 })?;
298 Ok(out)
299 }
300 CertKeyAlgo::Unknown => Err(SecurityError::new(
301 SecurityErrorKind::InvalidConfiguration,
302 "pki: cannot sign — unsupported key algo",
303 )),
304 }
305}
306
307fn verify_signature_with_cert(
308 cert_der: &[u8],
309 key_algo: CertKeyAlgo,
310 msg: &[u8],
311 sig: &[u8],
312) -> SecurityResult<()> {
313 let cert = CertificateDer::from_slice(cert_der);
314 let ee = webpki::EndEntityCert::try_from(&cert).map_err(|e| {
315 SecurityError::new(
316 SecurityErrorKind::AuthenticationFailed,
317 alloc::format!("pki: peer cert parse failed: {e:?}"),
318 )
319 })?;
320 let alg: &dyn rustls_pki_types::SignatureVerificationAlgorithm = match key_algo {
321 CertKeyAlgo::EcdsaP256Sha256 => webpki::ring::ECDSA_P256_SHA256,
322 CertKeyAlgo::RsaPssSha256 => webpki::ring::RSA_PSS_2048_8192_SHA256_LEGACY_KEY,
323 CertKeyAlgo::Unknown => {
324 return Err(SecurityError::new(
325 SecurityErrorKind::InvalidConfiguration,
326 "pki: peer cert algo unsupported",
327 ));
328 }
329 };
330 ee.verify_signature(alg, msg, sig).map_err(|e| {
331 SecurityError::new(
332 SecurityErrorKind::AuthenticationFailed,
333 alloc::format!("pki: signature verify failed: {e:?}"),
334 )
335 })
336}
337
338fn detect_peer_algo(cert_der: &[u8]) -> CertKeyAlgo {
342 crate::identity::detect_cert_algo_pub(cert_der)
343}
344
345fn derive_shared_secret(
348 raw_dh: &[u8],
349 challenge1: &[u8; 32],
350 challenge2: &[u8; 32],
351) -> SecurityResult<Vec<u8>> {
352 use ring::hkdf;
353 let mut salt = [0u8; 64];
354 salt[..32].copy_from_slice(challenge1);
355 salt[32..].copy_from_slice(challenge2);
356 let salt_obj = hkdf::Salt::new(hkdf::HKDF_SHA256, &salt);
357 let prk = salt_obj.extract(raw_dh);
358 let info = [b"DDS-Security-1.2-SharedSecret".as_slice()];
359 let okm = prk.expand(&info, hkdf::HKDF_SHA256).map_err(|_| {
360 SecurityError::new(SecurityErrorKind::CryptoFailed, "pki: HKDF expand failed")
361 })?;
362 let mut out = [0u8; 32];
363 okm.fill(&mut out).map_err(|_| {
364 SecurityError::new(SecurityErrorKind::CryptoFailed, "pki: HKDF fill failed")
365 })?;
366 Ok(out.to_vec())
367}
368
369impl AuthenticationPlugin for PkiAuthenticationPlugin {
370 fn validate_local_identity(
371 &mut self,
372 props: &PropertyList,
373 participant_guid: [u8; 16],
374 ) -> SecurityResult<IdentityHandle> {
375 let cert = props.get(keys::IDENTITY_CERT).ok_or_else(|| {
376 SecurityError::new(
377 SecurityErrorKind::InvalidConfiguration,
378 "pki: fehlt dds.sec.auth.identity_certificate",
379 )
380 })?;
381 let ca = props.get(keys::IDENTITY_CA).ok_or_else(|| {
382 SecurityError::new(
383 SecurityErrorKind::InvalidConfiguration,
384 "pki: fehlt dds.sec.auth.identity_ca",
385 )
386 })?;
387 let cfg = IdentityConfig {
388 identity_cert_pem: cert.as_bytes().to_vec(),
389 identity_ca_pem: ca.as_bytes().to_vec(),
390 identity_key_pem: props.get(keys::IDENTITY_KEY).map(|s| s.as_bytes().to_vec()),
391 };
392 self.validate_with_config(cfg, participant_guid)
393 }
394
395 fn validate_remote_identity(
396 &mut self,
397 local: IdentityHandle,
398 _remote_participant_guid: [u8; 16],
399 remote_auth_token: &[u8],
400 ) -> SecurityResult<IdentityHandle> {
401 let parsed = self.identities.get(&local).ok_or_else(|| {
402 SecurityError::new(
403 SecurityErrorKind::BadArgument,
404 "pki: unbekannter lokaler IdentityHandle",
405 )
406 })?;
407 parsed
408 .verify_remote_der(remote_auth_token)
409 .map_err(pki_to_security)?;
410 let handle = IdentityHandle(self.next_id());
411 Ok(handle)
412 }
413
414 fn begin_handshake_request(
415 &mut self,
416 initiator: IdentityHandle,
417 _replier: IdentityHandle,
418 ) -> SecurityResult<(HandshakeHandle, HandshakeStepOutcome)> {
419 let parsed = self.identities.get(&initiator).ok_or_else(|| {
420 SecurityError::new(
421 SecurityErrorKind::BadArgument,
422 "pki: unbekannter Initiator-IdentityHandle",
423 )
424 })?;
425 let cert_der = parsed.cert_der.clone();
426 let key_algo = parsed.key_algo;
427 let dsign_algo = algo_for(key_algo)?.to_owned();
428 let kagree_algo = ht::algo::X25519.to_owned();
429
430 let kx = KeyExchange::new()?;
431 let dh1 = kx.public_key().to_vec();
432 let challenge1 = random_challenge()?;
433 let permissions: Vec<u8> = Vec::new();
434 let pdata: Vec<u8> = Vec::new();
435
436 let bytes = ht::build_request_token(&RequestBuildInput {
437 cert_der: &cert_der,
438 permissions: &permissions,
439 pdata: &pdata,
440 dsign_algo: &dsign_algo,
441 kagree_algo: &kagree_algo,
442 dh1: &dh1,
443 challenge1: &challenge1,
444 ocsp_status: &[],
445 })?;
446
447 let hash_c1 =
448 ht::compute_hash_c(&cert_der, &permissions, &pdata, &dsign_algo, &kagree_algo);
449
450 let handle = HandshakeHandle(self.next_id());
451 self.pending_initiator.insert(
452 handle,
453 InitiatorState {
454 local: initiator,
455 kx: Some(kx),
456 dh1,
457 challenge1,
458 hash_c1,
459 permissions,
460 pdata,
461 kagree_algo,
462 dsign_algo,
463 },
464 );
465 Ok((handle, HandshakeStepOutcome::SendMessage { token: bytes }))
466 }
467
468 fn begin_handshake_reply(
469 &mut self,
470 replier: IdentityHandle,
471 _initiator: IdentityHandle,
472 request_token: &[u8],
473 ) -> SecurityResult<(HandshakeHandle, HandshakeStepOutcome)> {
474 let req = ht::parse_request_token(request_token)?;
476
477 let parsed = self.identities.get(&replier).ok_or_else(|| {
479 SecurityError::new(
480 SecurityErrorKind::BadArgument,
481 "pki: unbekannter Replier-IdentityHandle",
482 )
483 })?;
484 parsed
485 .verify_remote_der(&req.cert_der)
486 .map_err(pki_to_security)?;
487
488 self.record_challenge(replier, req.challenge1)?;
490
491 let initiator_key_algo = detect_peer_algo(&req.cert_der);
493 check_dsign_matches(&req.dsign_algo, initiator_key_algo)?;
494
495 let kx = KeyExchange::new()?;
497 let dh2 = kx.public_key().to_vec();
498 let challenge2 = random_challenge()?;
499
500 let parsed = self.identities.get(&replier).ok_or_else(|| {
502 SecurityError::new(SecurityErrorKind::Internal, "pki: replier identity gone")
503 })?;
504 let priv_key = parsed.private_key_pkcs8_der.clone().ok_or_else(|| {
506 SecurityError::new(
507 SecurityErrorKind::InvalidConfiguration,
508 "pki: replier hat keinen private-key konfiguriert (kann nicht signieren)",
509 )
510 })?;
511 let replier_cert_der = parsed.cert_der.clone();
512 let replier_dsign = algo_for(parsed.key_algo)?.to_owned();
513 let replier_key_algo = parsed.key_algo;
514
515 let secret_bytes = kx.derive_shared_secret(&req.dh1)?;
517 let final_secret = derive_shared_secret(&secret_bytes, &req.challenge1, &challenge2)?;
519
520 let to_sign = signing_bytes(
522 &req.kagree_algo,
523 &req.challenge1,
524 &req.dh1,
525 &challenge2,
526 &dh2,
527 );
528 let signature = sign_with(replier_key_algo, &priv_key, &to_sign)?;
529
530 let permissions: Vec<u8> = Vec::new();
533 let pdata: Vec<u8> = Vec::new();
534 let hash_c2 = ht::compute_hash_c(
535 &replier_cert_der,
536 &permissions,
537 &pdata,
538 &replier_dsign,
539 &req.kagree_algo,
540 );
541
542 let reply_bytes = ht::build_reply_token(&ReplyBuildInput {
544 cert_der: &replier_cert_der,
545 permissions: &permissions,
546 pdata: &pdata,
547 dsign_algo: &replier_dsign,
548 kagree_algo: &req.kagree_algo,
549 dh2: &dh2,
550 challenge2: &challenge2,
551 hash_c1: &req.hash_c1,
552 dh1: &req.dh1,
553 challenge1: &req.challenge1,
554 ocsp_status: &[],
555 signature: &signature,
556 })?;
557
558 let handle = HandshakeHandle(self.next_id());
560 let secret_handle = self.store_secret(handle, final_secret);
561 self.handshake_to_secret.insert(handle, secret_handle);
562 self.pending_replier.insert(
563 handle,
564 ReplierState {
565 local: replier,
566 kagree_algo: req.kagree_algo,
567 challenge1: req.challenge1,
568 challenge2,
569 dh1: req.dh1,
570 dh2,
571 hash_c1: req.hash_c1,
572 hash_c2,
573 secret_handle,
574 initiator_cert_der: req.cert_der,
575 initiator_key_algo,
576 },
577 );
578
579 Ok((
580 handle,
581 HandshakeStepOutcome::SendMessage { token: reply_bytes },
582 ))
583 }
584
585 fn process_handshake(
586 &mut self,
587 handshake: HandshakeHandle,
588 token: &[u8],
589 ) -> SecurityResult<HandshakeStepOutcome> {
590 if self.pending_initiator.contains_key(&handshake) {
592 return self.process_reply_on_initiator(handshake, token);
593 }
594 if self.pending_replier.contains_key(&handshake) {
596 return self.process_final_on_replier(handshake, token);
597 }
598 Err(SecurityError::new(
599 SecurityErrorKind::BadArgument,
600 "pki: unbekannter HandshakeHandle",
601 ))
602 }
603
604 fn shared_secret(&self, handshake: HandshakeHandle) -> SecurityResult<SharedSecretHandle> {
605 self.handshake_to_secret
606 .get(&handshake)
607 .copied()
608 .ok_or_else(|| {
609 SecurityError::new(
610 SecurityErrorKind::BadArgument,
611 "pki: handshake-handle unbekannt oder noch nicht completed",
612 )
613 })
614 }
615
616 fn plugin_class_id(&self) -> &str {
617 "DDS:Auth:PKI-DH:1.2"
618 }
619}
620
621impl PkiAuthenticationPlugin {
622 fn process_reply_on_initiator(
623 &mut self,
624 handshake: HandshakeHandle,
625 token: &[u8],
626 ) -> SecurityResult<HandshakeStepOutcome> {
627 let reply = ht::parse_reply_token(token)?;
628
629 let st = self.pending_initiator.remove(&handshake).ok_or_else(|| {
630 SecurityError::new(SecurityErrorKind::BadArgument, "pki: initiator state gone")
631 })?;
632
633 if !ct_eq(&reply.hash_c1, &st.hash_c1) {
635 return Err(SecurityError::new(
636 SecurityErrorKind::AuthenticationFailed,
637 "reply: hash_c1 echo mismatch (cert-bind broken)",
638 ));
639 }
640 if !ct_eq(&reply.dh1, &st.dh1) {
641 return Err(SecurityError::new(
642 SecurityErrorKind::AuthenticationFailed,
643 "reply: dh1 echo mismatch",
644 ));
645 }
646 if !ct_eq(&reply.challenge1, &st.challenge1) {
647 return Err(SecurityError::new(
648 SecurityErrorKind::AuthenticationFailed,
649 "reply: challenge1 echo mismatch",
650 ));
651 }
652 if reply.kagree_algo != st.kagree_algo {
653 return Err(SecurityError::new(
654 SecurityErrorKind::AuthenticationFailed,
655 "reply: kagree_algo mismatch",
656 ));
657 }
658
659 let (priv_key, initiator_key_algo) = {
663 let parsed = self.identities.get(&st.local).ok_or_else(|| {
664 SecurityError::new(SecurityErrorKind::Internal, "pki: initiator identity gone")
665 })?;
666 parsed
667 .verify_remote_der(&reply.cert_der)
668 .map_err(pki_to_security)?;
669 let pk = parsed.private_key_pkcs8_der.clone().ok_or_else(|| {
670 SecurityError::new(
671 SecurityErrorKind::InvalidConfiguration,
672 "pki: initiator hat keinen private-key (final-sign nicht moeglich)",
673 )
674 })?;
675 (pk, parsed.key_algo)
676 };
677 let replier_key_algo = detect_peer_algo(&reply.cert_der);
678 check_dsign_matches(&reply.dsign_algo, replier_key_algo)?;
679
680 let to_verify = signing_bytes(
682 &reply.kagree_algo,
683 &reply.challenge1,
684 &reply.dh1,
685 &reply.challenge2,
686 &reply.dh2,
687 );
688 verify_signature_with_cert(
689 &reply.cert_der,
690 replier_key_algo,
691 &to_verify,
692 &reply.signature,
693 )?;
694
695 let kx = st.kx.ok_or_else(|| {
697 SecurityError::new(SecurityErrorKind::Internal, "pki: ephemeral kx gone")
698 })?;
699 let raw = kx.derive_shared_secret(&reply.dh2)?;
700 let final_secret = derive_shared_secret(&raw, &st.challenge1, &reply.challenge2)?;
701
702 let secret_handle = self.store_secret(handshake, final_secret);
703 self.handshake_to_secret.insert(handshake, secret_handle);
704
705 let to_sign = signing_bytes(
707 &reply.kagree_algo,
708 &reply.challenge2,
709 &reply.dh2,
710 &reply.challenge1,
711 &reply.dh1,
712 );
713 let signature = sign_with(initiator_key_algo, &priv_key, &to_sign)?;
714 let final_token = ht::build_final_token(&FinalBuildInput {
715 hash_c1: &reply.hash_c1,
716 hash_c2: &reply.hash_c2,
717 dh1: &reply.dh1,
718 dh2: &reply.dh2,
719 challenge1: &reply.challenge1,
720 challenge2: &reply.challenge2,
721 ocsp_status: &[],
722 signature: &signature,
723 })?;
724
725 Ok(HandshakeStepOutcome::SendMessage { token: final_token })
731 }
732
733 fn process_final_on_replier(
734 &mut self,
735 handshake: HandshakeHandle,
736 token: &[u8],
737 ) -> SecurityResult<HandshakeStepOutcome> {
738 let final_tok = ht::parse_final_token(token)?;
739 let st = self.pending_replier.remove(&handshake).ok_or_else(|| {
740 SecurityError::new(SecurityErrorKind::BadArgument, "pki: replier state gone")
741 })?;
742
743 if !ct_eq(&final_tok.hash_c1, &st.hash_c1)
745 || !ct_eq(&final_tok.hash_c2, &st.hash_c2)
746 || !ct_eq(&final_tok.dh1, &st.dh1)
747 || !ct_eq(&final_tok.dh2, &st.dh2)
748 || !ct_eq(&final_tok.challenge1, &st.challenge1)
749 || !ct_eq(&final_tok.challenge2, &st.challenge2)
750 {
751 return Err(SecurityError::new(
752 SecurityErrorKind::AuthenticationFailed,
753 "final: echo mismatch",
754 ));
755 }
756
757 let to_verify = signing_bytes(
759 &st.kagree_algo,
760 &st.challenge2,
761 &st.dh2,
762 &st.challenge1,
763 &st.dh1,
764 );
765 verify_signature_with_cert(
766 &st.initiator_cert_der,
767 st.initiator_key_algo,
768 &to_verify,
769 &final_tok.signature,
770 )?;
771
772 let _ = st.local;
776 Ok(HandshakeStepOutcome::Complete {
777 secret: st.secret_handle,
778 })
779 }
780}
781
782#[cfg(test)]
783#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
784mod tests {
785 use super::*;
786 use zerodds_security::properties::Property;
787
788 fn make_signed_cert_ca_key() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
791 use rcgen::{CertificateParams, KeyPair};
792
793 let mut ca_params = CertificateParams::new(vec!["ZeroDDS Test CA".into()]).unwrap();
794 ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
795 let ca_key = KeyPair::generate().unwrap();
796 let ca_cert = ca_params.self_signed(&ca_key).unwrap();
797 let ca_pem = ca_cert.pem();
798
799 let mut ee_params = CertificateParams::new(vec!["zerodds-node".into()]).unwrap();
800 ee_params.is_ca = rcgen::IsCa::NoCa;
801 let ee_key = KeyPair::generate().unwrap();
802 let ee_cert = ee_params.signed_by(&ee_key, &ca_cert, &ca_key).unwrap();
803 let ee_pem = ee_cert.pem();
804 let ee_key_pem = ee_key.serialize_pem();
805
806 (
807 ee_pem.into_bytes(),
808 ca_pem.into_bytes(),
809 ee_key_pem.into_bytes(),
810 )
811 }
812
813 fn make_cert_with_wrong_ca() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
814 use rcgen::{CertificateParams, KeyPair};
815
816 let mut trusted_ca_params = CertificateParams::new(vec!["Trusted CA".into()]).unwrap();
817 trusted_ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
818 let trusted_ca_key = KeyPair::generate().unwrap();
819 let trusted_ca_cert = trusted_ca_params.self_signed(&trusted_ca_key).unwrap();
820
821 let mut rogue_ca_params = CertificateParams::new(vec!["Rogue CA".into()]).unwrap();
822 rogue_ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
823 let rogue_ca_key = KeyPair::generate().unwrap();
824 let rogue_ca_cert = rogue_ca_params.self_signed(&rogue_ca_key).unwrap();
825
826 let mut ee_params = CertificateParams::new(vec!["impersonator".into()]).unwrap();
827 ee_params.is_ca = rcgen::IsCa::NoCa;
828 let ee_key = KeyPair::generate().unwrap();
829 let ee_cert = ee_params
830 .signed_by(&ee_key, &rogue_ca_cert, &rogue_ca_key)
831 .unwrap();
832
833 (
834 ee_cert.pem().into_bytes(),
835 trusted_ca_cert.pem().into_bytes(),
836 ee_key.serialize_pem().into_bytes(),
837 )
838 }
839
840 fn alice_bob() -> (
841 PkiAuthenticationPlugin,
842 PkiAuthenticationPlugin,
843 IdentityHandle,
844 IdentityHandle,
845 IdentityHandle,
846 IdentityHandle,
847 ) {
848 let (a_cert, ca, a_key) = make_signed_cert_ca_key();
849 let (b_cert, _ca2, b_key) = make_signed_cert_ca_key();
850 let _ = (b_cert, b_key);
856
857 use rcgen::{CertificateParams, KeyPair};
859 let mut ca_params = CertificateParams::new(vec!["Common CA".into()]).unwrap();
860 ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
861 let ca_key = KeyPair::generate().unwrap();
862 let ca_cert = ca_params.self_signed(&ca_key).unwrap();
863 let ca_pem = ca_cert.pem().into_bytes();
864
865 let mut alice_params = CertificateParams::new(vec!["alice".into()]).unwrap();
866 alice_params.is_ca = rcgen::IsCa::NoCa;
867 let alice_key_pair = KeyPair::generate().unwrap();
868 let alice_cert = alice_params
869 .signed_by(&alice_key_pair, &ca_cert, &ca_key)
870 .unwrap();
871 let alice_cert_pem = alice_cert.pem().into_bytes();
872 let alice_key_pem = alice_key_pair.serialize_pem().into_bytes();
873
874 let mut bob_params = CertificateParams::new(vec!["bob".into()]).unwrap();
875 bob_params.is_ca = rcgen::IsCa::NoCa;
876 let bob_key_pair = KeyPair::generate().unwrap();
877 let bob_cert = bob_params
878 .signed_by(&bob_key_pair, &ca_cert, &ca_key)
879 .unwrap();
880 let bob_cert_pem = bob_cert.pem().into_bytes();
881 let bob_key_pem = bob_key_pair.serialize_pem().into_bytes();
882
883 let mut alice = PkiAuthenticationPlugin::new();
884 let mut bob = PkiAuthenticationPlugin::new();
885 let alice_h = alice
886 .validate_with_config(
887 IdentityConfig {
888 identity_cert_pem: alice_cert_pem.clone(),
889 identity_ca_pem: ca_pem.clone(),
890 identity_key_pem: Some(alice_key_pem),
891 },
892 [0xAA; 16],
893 )
894 .unwrap();
895 let alice_remote_for_bob = alice
896 .validate_remote_identity(alice_h, [0xBB; 16], &cert_der_from_pem(&bob_cert_pem))
897 .unwrap();
898 let bob_h = bob
899 .validate_with_config(
900 IdentityConfig {
901 identity_cert_pem: bob_cert_pem.clone(),
902 identity_ca_pem: ca_pem,
903 identity_key_pem: Some(bob_key_pem),
904 },
905 [0xBB; 16],
906 )
907 .unwrap();
908 let bob_remote_for_alice = bob
909 .validate_remote_identity(bob_h, [0xAA; 16], &cert_der_from_pem(&alice_cert_pem))
910 .unwrap();
911 let _ = (a_cert, ca, a_key);
912 (
913 alice,
914 bob,
915 alice_h,
916 alice_remote_for_bob,
917 bob_h,
918 bob_remote_for_alice,
919 )
920 }
921
922 fn cert_der_from_pem(pem: &[u8]) -> Vec<u8> {
923 use rustls_pki_types::CertificateDer;
924 use rustls_pki_types::pem::PemObject;
925 CertificateDer::pem_slice_iter(pem)
926 .next()
927 .unwrap()
928 .unwrap()
929 .as_ref()
930 .to_vec()
931 }
932
933 #[test]
934 fn plugin_class_id_matches_spec() {
935 let p = PkiAuthenticationPlugin::new();
936 assert_eq!(p.plugin_class_id(), "DDS:Auth:PKI-DH:1.2");
937 }
938
939 #[test]
940 fn validate_local_identity_accepts_ca_signed_cert() {
941 let (cert_pem, ca_pem, key_pem) = make_signed_cert_ca_key();
942 let mut plugin = PkiAuthenticationPlugin::new();
943 let cfg = IdentityConfig {
944 identity_cert_pem: cert_pem,
945 identity_ca_pem: ca_pem,
946 identity_key_pem: Some(key_pem),
947 };
948 let handle = plugin
949 .validate_with_config(cfg, [0xAA; 16])
950 .expect("signed cert must validate");
951 assert_eq!(handle, IdentityHandle(1));
952 }
953
954 #[test]
955 fn validate_local_identity_rejects_wrong_ca() {
956 let (cert_pem, trusted_ca_pem, key_pem) = make_cert_with_wrong_ca();
957 let mut plugin = PkiAuthenticationPlugin::new();
958 let cfg = IdentityConfig {
959 identity_cert_pem: cert_pem,
960 identity_ca_pem: trusted_ca_pem,
961 identity_key_pem: Some(key_pem),
962 };
963 let err = plugin
964 .validate_with_config(cfg, [0xAA; 16])
965 .expect_err("rogue-CA cert must not validate");
966 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
967 }
968
969 #[test]
970 fn validate_local_identity_rejects_empty_trust_anchors() {
971 let (cert_pem, _, _) = make_signed_cert_ca_key();
972 let mut plugin = PkiAuthenticationPlugin::new();
973 let cfg = IdentityConfig {
974 identity_cert_pem: cert_pem,
975 identity_ca_pem: b"".to_vec(),
976 identity_key_pem: None,
977 };
978 let err = plugin.validate_with_config(cfg, [0xAA; 16]).unwrap_err();
979 assert_eq!(err.kind, SecurityErrorKind::InvalidConfiguration);
980 }
981
982 #[test]
983 fn validate_local_identity_via_property_list() {
984 let (cert_pem, ca_pem, key_pem) = make_signed_cert_ca_key();
985 let mut plugin = PkiAuthenticationPlugin::new();
986 let cert_str = std::str::from_utf8(&cert_pem).unwrap().to_owned();
987 let ca_str = std::str::from_utf8(&ca_pem).unwrap().to_owned();
988 let key_str = std::str::from_utf8(&key_pem).unwrap().to_owned();
989 let props = PropertyList::new()
990 .with(Property::local(
991 "dds.sec.auth.identity_certificate",
992 cert_str,
993 ))
994 .with(Property::local("dds.sec.auth.identity_ca", ca_str))
995 .with(Property::local("dds.sec.auth.private_key", key_str));
996 let handle = plugin
997 .validate_local_identity(&props, [0xAA; 16])
998 .expect("validate via props");
999 assert!(handle.0 >= 1);
1000 }
1001
1002 #[test]
1007 fn full_three_round_handshake_alice_bob() {
1008 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1009
1010 let (alice_hs, out1) = alice
1012 .begin_handshake_request(alice_h, alice_remote_bob)
1013 .unwrap();
1014 let req_token = match out1 {
1015 HandshakeStepOutcome::SendMessage { token } => token,
1016 _ => panic!("expected SendMessage"),
1017 };
1018 assert!(req_token.len() > 100, "request token enthaelt cert + DH");
1019
1020 let (bob_hs, out2) = bob
1022 .begin_handshake_reply(bob_h, bob_remote_alice, &req_token)
1023 .unwrap();
1024 let reply_token = match out2 {
1025 HandshakeStepOutcome::SendMessage { token } => token,
1026 _ => panic!("expected SendMessage"),
1027 };
1028
1029 let out3 = alice.process_handshake(alice_hs, &reply_token).unwrap();
1031 let final_token = match out3 {
1032 HandshakeStepOutcome::SendMessage { token } => token,
1033 _ => panic!("alice expected to send final token"),
1034 };
1035
1036 let out4 = bob.process_handshake(bob_hs, &final_token).unwrap();
1038 let bob_secret = match out4 {
1039 HandshakeStepOutcome::Complete { secret } => secret,
1040 _ => panic!("expected Complete"),
1041 };
1042
1043 let alice_secret = alice.shared_secret(alice_hs).unwrap();
1044 let a_bytes = alice.secret_bytes(alice_secret).unwrap();
1045 let b_bytes = bob.secret_bytes(bob_secret).unwrap();
1046 assert_eq!(a_bytes.len(), 32);
1047 assert_eq!(
1048 a_bytes, b_bytes,
1049 "alice + bob muessen identisches secret ableiten"
1050 );
1051 }
1052
1053 #[test]
1054 fn request_token_has_spec_class_id_and_properties() {
1055 let (mut alice, _bob, alice_h, alice_remote_bob, _, _) = alice_bob();
1056 let (_, out) = alice
1057 .begin_handshake_request(alice_h, alice_remote_bob)
1058 .unwrap();
1059 let token = match out {
1060 HandshakeStepOutcome::SendMessage { token } => token,
1061 _ => panic!(),
1062 };
1063 let parsed = ht::DataHolder::from_cdr_le(&token).unwrap();
1064 assert_eq!(parsed.class_id, "DDS:Auth:PKI-DH:1.2+AuthReq");
1065 assert!(parsed.property("c.dsign_algo").is_some());
1066 assert!(parsed.property("c.kagree_algo").is_some());
1067 for k in [
1068 "c.id",
1069 "c.perm",
1070 "c.pdata",
1071 "hash_c1",
1072 "dh1",
1073 "challenge1",
1074 "ocsp_status",
1075 ] {
1076 assert!(
1077 parsed.binary_property(k).is_some(),
1078 "missing binary prop: {k}"
1079 );
1080 }
1081 }
1082
1083 #[test]
1084 fn cert_bind_replier_modifies_initiator_cert_in_reply_rejected() {
1085 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1086 let (alice_hs, out1) = alice
1087 .begin_handshake_request(alice_h, alice_remote_bob)
1088 .unwrap();
1089 let req = match out1 {
1090 HandshakeStepOutcome::SendMessage { token } => token,
1091 _ => panic!(),
1092 };
1093 let (_bob_hs, out2) = bob
1094 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1095 .unwrap();
1096 let mut reply_token = match out2 {
1097 HandshakeStepOutcome::SendMessage { token } => token,
1098 _ => panic!(),
1099 };
1100
1101 let mut h = ht::DataHolder::from_cdr_le(&reply_token).unwrap();
1103 let mut hash_c1 = h.binary_property("hash_c1").unwrap().to_vec();
1104 hash_c1[0] ^= 0x01;
1105 h.set_binary_property("hash_c1", hash_c1);
1106 reply_token = h.to_cdr_le();
1107
1108 let err = alice.process_handshake(alice_hs, &reply_token).unwrap_err();
1109 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1110 }
1111
1112 #[test]
1113 fn signature_tamper_rejected_by_initiator() {
1114 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1115 let (alice_hs, out1) = alice
1116 .begin_handshake_request(alice_h, alice_remote_bob)
1117 .unwrap();
1118 let req = match out1 {
1119 HandshakeStepOutcome::SendMessage { token } => token,
1120 _ => panic!(),
1121 };
1122 let (_, out2) = bob
1123 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1124 .unwrap();
1125 let mut reply = match out2 {
1126 HandshakeStepOutcome::SendMessage { token } => token,
1127 _ => panic!(),
1128 };
1129
1130 let mut h = ht::DataHolder::from_cdr_le(&reply).unwrap();
1131 let mut sig = h.binary_property("signature").unwrap().to_vec();
1132 sig[0] ^= 0x01;
1133 h.set_binary_property("signature", sig);
1134 reply = h.to_cdr_le();
1135
1136 let err = alice.process_handshake(alice_hs, &reply).unwrap_err();
1137 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1138 }
1139
1140 #[test]
1141 fn dh_tamper_in_reply_breaks_final_signature() {
1142 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1143 let (alice_hs, out1) = alice
1144 .begin_handshake_request(alice_h, alice_remote_bob)
1145 .unwrap();
1146 let req = match out1 {
1147 HandshakeStepOutcome::SendMessage { token } => token,
1148 _ => panic!(),
1149 };
1150 let (bob_hs, out2) = bob
1151 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1152 .unwrap();
1153 let mut reply = match out2 {
1154 HandshakeStepOutcome::SendMessage { token } => token,
1155 _ => panic!(),
1156 };
1157
1158 let mut h = ht::DataHolder::from_cdr_le(&reply).unwrap();
1162 let mut dh2 = h.binary_property("dh2").unwrap().to_vec();
1163 dh2[0] ^= 0x01;
1164 h.set_binary_property("dh2", dh2);
1165 reply = h.to_cdr_le();
1166
1167 let err = alice.process_handshake(alice_hs, &reply).unwrap_err();
1168 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1169 let _ = bob_hs;
1170 }
1171
1172 #[test]
1173 fn wrong_ca_initiator_rejected_by_replier() {
1174 let (rogue_cert, _trusted_ca, rogue_key) = make_cert_with_wrong_ca();
1175 use rcgen::{CertificateParams, KeyPair};
1188 let mut alice_ca_params = CertificateParams::new(vec!["AliceCA".into()]).unwrap();
1190 alice_ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1191 let alice_ca_key = KeyPair::generate().unwrap();
1192 let alice_ca_cert = alice_ca_params.self_signed(&alice_ca_key).unwrap();
1193 let alice_ca_pem = alice_ca_cert.pem();
1194
1195 let mut alice_params = CertificateParams::new(vec!["alice".into()]).unwrap();
1196 alice_params.is_ca = rcgen::IsCa::NoCa;
1197 let alice_key = KeyPair::generate().unwrap();
1198 let alice_cert = alice_params
1199 .signed_by(&alice_key, &alice_ca_cert, &alice_ca_key)
1200 .unwrap();
1201 let alice_cert_pem = alice_cert.pem().into_bytes();
1202 let alice_key_pem = alice_key.serialize_pem().into_bytes();
1203
1204 let mut bob_ca_params = CertificateParams::new(vec!["BobCA".into()]).unwrap();
1206 bob_ca_params.is_ca = rcgen::IsCa::Ca(rcgen::BasicConstraints::Unconstrained);
1207 let bob_ca_key = KeyPair::generate().unwrap();
1208 let bob_ca_cert = bob_ca_params.self_signed(&bob_ca_key).unwrap();
1209 let bob_ca_pem = bob_ca_cert.pem();
1210 let mut bob_params = CertificateParams::new(vec!["bob".into()]).unwrap();
1211 bob_params.is_ca = rcgen::IsCa::NoCa;
1212 let bob_key = KeyPair::generate().unwrap();
1213 let bob_cert = bob_params
1214 .signed_by(&bob_key, &bob_ca_cert, &bob_ca_key)
1215 .unwrap();
1216 let bob_cert_pem = bob_cert.pem().into_bytes();
1217 let bob_key_pem = bob_key.serialize_pem().into_bytes();
1218
1219 let mut alice = PkiAuthenticationPlugin::new();
1220 let mut bob = PkiAuthenticationPlugin::new();
1221 let alice_h = alice
1222 .validate_with_config(
1223 IdentityConfig {
1224 identity_cert_pem: alice_cert_pem.clone(),
1225 identity_ca_pem: alice_ca_pem.into_bytes(),
1226 identity_key_pem: Some(alice_key_pem),
1227 },
1228 [0xAA; 16],
1229 )
1230 .unwrap();
1231 let bob_h = bob
1232 .validate_with_config(
1233 IdentityConfig {
1234 identity_cert_pem: bob_cert_pem.clone(),
1235 identity_ca_pem: bob_ca_pem.into_bytes(),
1236 identity_key_pem: Some(bob_key_pem),
1237 },
1238 [0xBB; 16],
1239 )
1240 .unwrap();
1241
1242 let (_alice_hs, out1) = alice
1248 .begin_handshake_request(alice_h, IdentityHandle(99))
1249 .unwrap();
1250 let req = match out1 {
1251 HandshakeStepOutcome::SendMessage { token } => token,
1252 _ => panic!(),
1253 };
1254 let err = bob
1256 .begin_handshake_reply(bob_h, IdentityHandle(99), &req)
1257 .unwrap_err();
1258 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1259 let _ = (rogue_cert, rogue_key);
1260 }
1261
1262 #[test]
1263 fn replay_initiator_request_rejected_second_time() {
1264 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1265 let (_alice_hs, out1) = alice
1266 .begin_handshake_request(alice_h, alice_remote_bob)
1267 .unwrap();
1268 let req = match out1 {
1269 HandshakeStepOutcome::SendMessage { token } => token,
1270 _ => panic!(),
1271 };
1272 let _ = bob
1274 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1275 .unwrap();
1276 let err = bob
1278 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1279 .unwrap_err();
1280 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1281 }
1282
1283 #[test]
1284 fn truncated_request_token_rejected() {
1285 let (_alice, mut bob, _alice_h, _, bob_h, bob_remote_alice) = alice_bob();
1286 let err = bob
1287 .begin_handshake_reply(bob_h, bob_remote_alice, &[0u8, 1u8, 2u8, 3u8, 4u8])
1288 .unwrap_err();
1289 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
1290 }
1291
1292 #[test]
1293 fn hash_c1_mismatch_in_request_rejected() {
1294 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1295 let (_, out1) = alice
1296 .begin_handshake_request(alice_h, alice_remote_bob)
1297 .unwrap();
1298 let req = match out1 {
1299 HandshakeStepOutcome::SendMessage { token } => token,
1300 _ => panic!(),
1301 };
1302 let mut h = ht::DataHolder::from_cdr_le(&req).unwrap();
1304 let mut hc = h.binary_property("hash_c1").unwrap().to_vec();
1305 hc[5] ^= 0xFF;
1306 h.set_binary_property("hash_c1", hc);
1307 let bad = h.to_cdr_le();
1308 let err = bob
1309 .begin_handshake_reply(bob_h, bob_remote_alice, &bad)
1310 .unwrap_err();
1311 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1312 }
1313
1314 #[test]
1315 fn cross_algorithm_dsign_mismatch_rejected() {
1316 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1317 let (_alice_hs, out1) = alice
1318 .begin_handshake_request(alice_h, alice_remote_bob)
1319 .unwrap();
1320 let mut req = match out1 {
1321 HandshakeStepOutcome::SendMessage { token } => token,
1322 _ => panic!(),
1323 };
1324 let mut h = ht::DataHolder::from_cdr_le(&req).unwrap();
1330 h.set_property("c.dsign_algo", "RSASSA-PSS-SHA256");
1331 let cert_der = h.binary_property("c.id").unwrap().to_vec();
1332 let perm = h.binary_property("c.perm").unwrap().to_vec();
1333 let pdata = h.binary_property("c.pdata").unwrap().to_vec();
1334 let kagree = h.property("c.kagree_algo").unwrap().to_owned();
1335 let new_hash = ht::compute_hash_c(&cert_der, &perm, &pdata, "RSASSA-PSS-SHA256", &kagree);
1336 h.set_binary_property("hash_c1", new_hash.to_vec());
1337 req = h.to_cdr_le();
1338 let err = bob
1339 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1340 .unwrap_err();
1341 assert_eq!(err.kind, SecurityErrorKind::InvalidConfiguration);
1342 }
1343
1344 #[test]
1345 fn extra_unknown_properties_in_reply_accepted_forward_compat() {
1346 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1347 let (alice_hs, out1) = alice
1348 .begin_handshake_request(alice_h, alice_remote_bob)
1349 .unwrap();
1350 let req = match out1 {
1351 HandshakeStepOutcome::SendMessage { token } => token,
1352 _ => panic!(),
1353 };
1354 let (_, out2) = bob
1355 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1356 .unwrap();
1357 let reply = match out2 {
1358 HandshakeStepOutcome::SendMessage { token } => token,
1359 _ => panic!(),
1360 };
1361 let mut h = ht::DataHolder::from_cdr_le(&reply).unwrap();
1363 h.set_property("zerodds.future.feature", "yes");
1364 h.set_binary_property("zerodds.future.opaque", alloc::vec![0xFF; 8]);
1365 let new_reply = h.to_cdr_le();
1368 let res = alice.process_handshake(alice_hs, &new_reply);
1369 assert!(
1370 res.is_ok(),
1371 "forward-compat extra props muessen akzeptiert werden"
1372 );
1373 }
1374
1375 #[test]
1376 fn empty_permissions_accepted_in_phase3_mvp() {
1377 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1378 let (alice_hs, out1) = alice
1379 .begin_handshake_request(alice_h, alice_remote_bob)
1380 .unwrap();
1381 let req = match out1 {
1382 HandshakeStepOutcome::SendMessage { token } => token,
1383 _ => panic!(),
1384 };
1385 let h = ht::DataHolder::from_cdr_le(&req).unwrap();
1388 assert_eq!(h.binary_property("c.perm").unwrap().len(), 0);
1389 let _ = bob
1390 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1391 .unwrap();
1392 let _ = alice_hs;
1393 }
1394
1395 #[test]
1396 fn shared_secret_returns_bad_argument_for_unknown_handle() {
1397 let plugin = PkiAuthenticationPlugin::new();
1398 let err = plugin.shared_secret(HandshakeHandle(42)).unwrap_err();
1399 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
1400 }
1401
1402 #[test]
1403 fn validate_remote_identity_accepts_trusted_cert_der() {
1404 let (cert_pem, ca_pem, key_pem) = make_signed_cert_ca_key();
1405 let mut plugin = PkiAuthenticationPlugin::new();
1406 let local = plugin
1407 .validate_with_config(
1408 IdentityConfig {
1409 identity_cert_pem: cert_pem.clone(),
1410 identity_ca_pem: ca_pem,
1411 identity_key_pem: Some(key_pem),
1412 },
1413 [0xAA; 16],
1414 )
1415 .unwrap();
1416
1417 let remote_der = cert_der_from_pem(&cert_pem);
1418 let remote = plugin
1419 .validate_remote_identity(local, [0xBB; 16], &remote_der)
1420 .expect("trusted remote must be accepted");
1421 assert_ne!(remote, local);
1422 }
1423
1424 #[test]
1438 fn replay_cache_holds_exactly_cap_entries_before_eviction() {
1439 let (mut alice, _bob, alice_h, _, _, _) = alice_bob();
1440 for i in 0..REPLAY_CACHE_CAP {
1441 let mut c = [0u8; 32];
1442 c[0..8].copy_from_slice(&(i as u64).to_le_bytes());
1443 alice.record_challenge(alice_h, c).unwrap();
1444 }
1445 let mut c_first = [0u8; 32];
1447 c_first[0..8].copy_from_slice(&0u64.to_le_bytes());
1448 let err = alice.record_challenge(alice_h, c_first).unwrap_err();
1449 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1450 }
1451
1452 #[test]
1455 fn replay_cache_evicts_oldest_at_cap_plus_one() {
1456 let (mut alice, _bob, alice_h, _, _, _) = alice_bob();
1457 for i in 0..REPLAY_CACHE_CAP {
1458 let mut c = [0u8; 32];
1459 c[0..8].copy_from_slice(&(i as u64).to_le_bytes());
1460 alice.record_challenge(alice_h, c).unwrap();
1461 }
1462 let mut c_extra = [0u8; 32];
1464 c_extra[0..8].copy_from_slice(&(REPLAY_CACHE_CAP as u64).to_le_bytes());
1465 alice.record_challenge(alice_h, c_extra).unwrap();
1466
1467 let mut c_first = [0u8; 32];
1469 c_first[0..8].copy_from_slice(&0u64.to_le_bytes());
1470 alice
1471 .record_challenge(alice_h, c_first)
1472 .expect("after CAP+1 inserts, oldest must be evicted");
1473
1474 let mut c_recent = [0u8; 32];
1478 c_recent[0..8].copy_from_slice(&(REPLAY_CACHE_CAP as u64).to_le_bytes());
1479 let err = alice.record_challenge(alice_h, c_recent).unwrap_err();
1480 assert_eq!(err.kind, SecurityErrorKind::AuthenticationFailed);
1481 }
1482
1483 #[test]
1488 fn get_shared_secret_returns_stored_value() {
1489 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1490
1491 let (alice_hs, out1) = alice
1492 .begin_handshake_request(alice_h, alice_remote_bob)
1493 .unwrap();
1494 let req = match out1 {
1495 HandshakeStepOutcome::SendMessage { token } => token,
1496 _ => panic!(),
1497 };
1498 let (bob_hs, out2) = bob
1499 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1500 .unwrap();
1501 let reply = match out2 {
1502 HandshakeStepOutcome::SendMessage { token } => token,
1503 _ => panic!(),
1504 };
1505 let out3 = alice.process_handshake(alice_hs, &reply).unwrap();
1506 let final_token = match out3 {
1507 HandshakeStepOutcome::SendMessage { token } => token,
1508 _ => panic!(),
1509 };
1510 let out4 = bob.process_handshake(bob_hs, &final_token).unwrap();
1511 let bob_handle = match out4 {
1512 HandshakeStepOutcome::Complete { secret } => secret,
1513 _ => panic!(),
1514 };
1515 let alice_handle = alice.shared_secret(alice_hs).unwrap();
1516
1517 let alice_bytes = SharedSecretProvider::get_shared_secret(&alice, alice_handle).unwrap();
1519 let bob_bytes = SharedSecretProvider::get_shared_secret(&bob, bob_handle).unwrap();
1520 assert_eq!(alice_bytes.len(), 32);
1524 assert_eq!(bob_bytes.len(), 32);
1525 assert_eq!(
1526 alice_bytes, bob_bytes,
1527 "shared secrets muessen identisch + nicht trivial sein"
1528 );
1529 assert!(alice_bytes.iter().any(|&b| b != 0));
1530 assert!(alice_bytes.iter().any(|&b| b != 1));
1531
1532 let bogus_handle = zerodds_security::authentication::SharedSecretHandle(0xDEAD_BEEF);
1534 assert!(SharedSecretProvider::get_shared_secret(&alice, bogus_handle).is_none());
1535 }
1536
1537 fn run_handshake_tampered_final<M>(mutate: M)
1542 where
1543 M: FnOnce(&mut ht::DataHolder),
1544 {
1545 let (mut alice, mut bob, alice_h, alice_remote_bob, bob_h, bob_remote_alice) = alice_bob();
1546 let (alice_hs, out1) = alice
1547 .begin_handshake_request(alice_h, alice_remote_bob)
1548 .unwrap();
1549 let req = match out1 {
1550 HandshakeStepOutcome::SendMessage { token } => token,
1551 _ => panic!(),
1552 };
1553 let (bob_hs, out2) = bob
1554 .begin_handshake_reply(bob_h, bob_remote_alice, &req)
1555 .unwrap();
1556 let reply = match out2 {
1557 HandshakeStepOutcome::SendMessage { token } => token,
1558 _ => panic!(),
1559 };
1560 let out3 = alice.process_handshake(alice_hs, &reply).unwrap();
1561 let final_token = match out3 {
1562 HandshakeStepOutcome::SendMessage { token } => token,
1563 _ => panic!(),
1564 };
1565
1566 let mut h = ht::DataHolder::from_cdr_le(&final_token).unwrap();
1567 mutate(&mut h);
1568 let tampered = h.to_cdr_le();
1569 let err = bob.process_handshake(bob_hs, &tampered).unwrap_err();
1570 assert_eq!(
1571 err.kind,
1572 SecurityErrorKind::AuthenticationFailed,
1573 "tampered final-token muss AuthFailed liefern"
1574 );
1575 }
1576
1577 fn flip_first_byte(h: &mut ht::DataHolder, prop: &str) {
1578 let mut v = h.binary_property(prop).unwrap().to_vec();
1579 v[0] ^= 0x01;
1580 h.set_binary_property(prop, v);
1581 }
1582
1583 #[test]
1584 fn final_token_hash_c1_tamper_rejected() {
1585 run_handshake_tampered_final(|h| flip_first_byte(h, "hash_c1"));
1586 }
1587 #[test]
1588 fn final_token_hash_c2_tamper_rejected() {
1589 run_handshake_tampered_final(|h| flip_first_byte(h, "hash_c2"));
1590 }
1591 #[test]
1592 fn final_token_dh1_tamper_rejected() {
1593 run_handshake_tampered_final(|h| flip_first_byte(h, "dh1"));
1594 }
1595 #[test]
1596 fn final_token_dh2_tamper_rejected() {
1597 run_handshake_tampered_final(|h| flip_first_byte(h, "dh2"));
1598 }
1599 #[test]
1600 fn final_token_challenge1_tamper_rejected() {
1601 run_handshake_tampered_final(|h| flip_first_byte(h, "challenge1"));
1602 }
1603 #[test]
1604 fn final_token_challenge2_tamper_rejected() {
1605 run_handshake_tampered_final(|h| flip_first_byte(h, "challenge2"));
1606 }
1607}