1use alloc::string::String;
21use alloc::vec::Vec;
22
23use zerodds_security::error::{SecurityError, SecurityErrorKind, SecurityResult};
24use ring::digest;
26pub use zerodds_security::token::DataHolder;
27
28pub mod class_id {
34 pub const REQUEST: &str = "DDS:Auth:PKI-DH:1.2+AuthReq";
36 pub const REPLY: &str = "DDS:Auth:PKI-DH:1.2+AuthReply";
38 pub const FINAL: &str = "DDS:Auth:PKI-DH:1.2+AuthFinal";
40}
41
42pub mod algo {
44 pub const ECDSA_SHA256: &str = "ECDSA-SHA256";
46 pub const RSASSA_PSS_SHA256: &str = "RSASSA-PSS-SHA256";
48 pub const ECDHE_CEUM_P256: &str = "ECDHE-CEUM-P256";
50 pub const DH_MODP_2048: &str = "DH+MODP-2048-256";
52 pub const X25519: &str = "X25519";
54}
55
56pub mod prop {
58 pub const C_ID: &str = "c.id";
60 pub const C_PERM: &str = "c.perm";
63 pub const C_PDATA: &str = "c.pdata";
65 pub const C_DSIGN_ALGO: &str = "c.dsign_algo";
67 pub const C_KAGREE_ALGO: &str = "c.kagree_algo";
69 pub const HASH_C1: &str = "hash_c1";
71 pub const HASH_C2: &str = "hash_c2";
73 pub const DH1: &str = "dh1";
75 pub const DH2: &str = "dh2";
77 pub const CHALLENGE1: &str = "challenge1";
79 pub const CHALLENGE2: &str = "challenge2";
81 pub const OCSP_STATUS: &str = "ocsp_status";
83 pub const SIGNATURE: &str = "signature";
85}
86
87pub const MAX_TOKEN_BYTES: usize = 65_536;
93pub const MAX_CERT_DER: usize = 16_384;
95pub const MAX_CHALLENGE: usize = 64;
97pub const MAX_DH_PUB: usize = 256;
99pub const MAX_PROP_VALUE: usize = 4096;
101pub const MAX_PROPS: usize = 64;
103pub const MAX_BIN_PROPS: usize = 64;
105pub const MAX_SIGNATURE: usize = 1024;
107
108#[must_use]
118pub fn compute_hash_c(
119 c_id: &[u8],
120 c_perm: &[u8],
121 c_pdata: &[u8],
122 c_dsign_algo: &str,
123 c_kagree_algo: &str,
124) -> [u8; 32] {
125 let mut ctx = digest::Context::new(&digest::SHA256);
126 fn put(ctx: &mut digest::Context, bytes: &[u8]) {
127 let len = u32::try_from(bytes.len()).unwrap_or(u32::MAX);
128 ctx.update(&len.to_le_bytes());
129 ctx.update(bytes);
130 }
131 put(&mut ctx, c_id);
132 put(&mut ctx, c_perm);
133 put(&mut ctx, c_pdata);
134 put(&mut ctx, c_dsign_algo.as_bytes());
135 put(&mut ctx, c_kagree_algo.as_bytes());
136 let d = ctx.finish();
137 let mut out = [0u8; 32];
138 out.copy_from_slice(d.as_ref());
139 out
140}
141
142#[must_use]
149pub fn signing_bytes(
150 c_kagree_algo: &str,
151 challenge1: &[u8],
152 dh1: &[u8],
153 challenge2: &[u8],
154 dh2: &[u8],
155) -> Vec<u8> {
156 let mut out = Vec::with_capacity(c_kagree_algo.len() + 32 + 64 + 32 + 64 + 4 * 5);
157 fn put(out: &mut Vec<u8>, bytes: &[u8]) {
158 let len = u32::try_from(bytes.len()).unwrap_or(u32::MAX);
159 out.extend_from_slice(&len.to_le_bytes());
160 out.extend_from_slice(bytes);
161 }
162 put(&mut out, c_kagree_algo.as_bytes());
163 put(&mut out, challenge1);
164 put(&mut out, dh1);
165 put(&mut out, challenge2);
166 put(&mut out, dh2);
167 out
168}
169
170#[derive(Debug, Clone)]
176pub struct RequestTokenView {
177 pub cert_der: Vec<u8>,
179 pub permissions: Vec<u8>,
181 pub pdata: Vec<u8>,
183 pub dsign_algo: String,
185 pub kagree_algo: String,
187 pub hash_c1: [u8; 32],
189 pub dh1: Vec<u8>,
191 pub challenge1: [u8; 32],
193 pub ocsp_status: Vec<u8>,
195}
196
197#[derive(Debug, Clone)]
199pub struct ReplyTokenView {
200 pub cert_der: Vec<u8>,
202 pub permissions: Vec<u8>,
204 pub pdata: Vec<u8>,
206 pub dsign_algo: String,
208 pub kagree_algo: String,
210 pub hash_c2: [u8; 32],
212 pub hash_c1: [u8; 32],
214 pub dh2: Vec<u8>,
216 pub dh1: Vec<u8>,
218 pub challenge2: [u8; 32],
220 pub challenge1: [u8; 32],
222 pub ocsp_status: Vec<u8>,
224 pub signature: Vec<u8>,
226}
227
228#[derive(Debug, Clone)]
230pub struct FinalTokenView {
231 pub hash_c1: [u8; 32],
233 pub hash_c2: [u8; 32],
235 pub dh1: Vec<u8>,
237 pub dh2: Vec<u8>,
239 pub challenge1: [u8; 32],
241 pub challenge2: [u8; 32],
243 pub ocsp_status: Vec<u8>,
245 pub signature: Vec<u8>,
247}
248
249pub struct RequestBuildInput<'a> {
251 pub cert_der: &'a [u8],
253 pub permissions: &'a [u8],
255 pub pdata: &'a [u8],
257 pub dsign_algo: &'a str,
259 pub kagree_algo: &'a str,
261 pub dh1: &'a [u8],
263 pub challenge1: &'a [u8; 32],
265 pub ocsp_status: &'a [u8],
267}
268
269pub fn build_request_token(input: &RequestBuildInput<'_>) -> SecurityResult<Vec<u8>> {
274 cap_check_request(input)?;
275 let hash_c1 = compute_hash_c(
276 input.cert_der,
277 input.permissions,
278 input.pdata,
279 input.dsign_algo,
280 input.kagree_algo,
281 );
282 let mut h = DataHolder::new(class_id::REQUEST);
283 h.set_property(prop::C_DSIGN_ALGO, input.dsign_algo.to_owned());
284 h.set_property(prop::C_KAGREE_ALGO, input.kagree_algo.to_owned());
285 h.set_binary_property(prop::C_ID, input.cert_der.to_vec());
286 h.set_binary_property(prop::C_PERM, input.permissions.to_vec());
287 h.set_binary_property(prop::C_PDATA, input.pdata.to_vec());
288 h.set_binary_property(prop::HASH_C1, hash_c1.to_vec());
289 h.set_binary_property(prop::DH1, input.dh1.to_vec());
290 h.set_binary_property(prop::CHALLENGE1, input.challenge1.to_vec());
291 h.set_binary_property(prop::OCSP_STATUS, input.ocsp_status.to_vec());
292 Ok(h.to_cdr_le())
293}
294
295pub fn parse_request_token(bytes: &[u8]) -> SecurityResult<RequestTokenView> {
301 let h = DataHolder::from_cdr_le(bytes)?;
302 if h.class_id != class_id::REQUEST {
303 return Err(SecurityError::new(
304 SecurityErrorKind::AuthenticationFailed,
305 "request: class_id mismatch",
306 ));
307 }
308 let cert_der = take_bin(&h, prop::C_ID, MAX_CERT_DER)?;
309 let permissions = take_bin(&h, prop::C_PERM, MAX_TOKEN_BYTES)?;
310 let pdata = take_bin(&h, prop::C_PDATA, MAX_TOKEN_BYTES)?;
311 let dsign_algo = take_prop(&h, prop::C_DSIGN_ALGO)?.to_owned();
312 let kagree_algo = take_prop(&h, prop::C_KAGREE_ALGO)?.to_owned();
313 let hash_c1 = take_fixed::<32>(&h, prop::HASH_C1)?;
314 let dh1 = take_bin(&h, prop::DH1, MAX_DH_PUB)?;
315 let challenge1 = take_fixed::<32>(&h, prop::CHALLENGE1)?;
316 let ocsp_status = h.binary_property(prop::OCSP_STATUS).unwrap_or(&[]).to_vec();
317
318 let recomputed = compute_hash_c(&cert_der, &permissions, &pdata, &dsign_algo, &kagree_algo);
321 if !ct_eq(&recomputed, &hash_c1) {
322 return Err(SecurityError::new(
323 SecurityErrorKind::AuthenticationFailed,
324 "request: hash_c1 mismatch (token tampered)",
325 ));
326 }
327
328 Ok(RequestTokenView {
329 cert_der,
330 permissions,
331 pdata,
332 dsign_algo,
333 kagree_algo,
334 hash_c1,
335 dh1,
336 challenge1,
337 ocsp_status,
338 })
339}
340
341pub struct ReplyBuildInput<'a> {
343 pub cert_der: &'a [u8],
345 pub permissions: &'a [u8],
347 pub pdata: &'a [u8],
349 pub dsign_algo: &'a str,
351 pub kagree_algo: &'a str,
353 pub dh2: &'a [u8],
355 pub challenge2: &'a [u8; 32],
357 pub hash_c1: &'a [u8; 32],
359 pub dh1: &'a [u8],
361 pub challenge1: &'a [u8; 32],
363 pub ocsp_status: &'a [u8],
365 pub signature: &'a [u8],
367}
368
369pub fn build_reply_token(input: &ReplyBuildInput<'_>) -> SecurityResult<Vec<u8>> {
374 cap_check_reply(input)?;
375 let hash_c2 = compute_hash_c(
376 input.cert_der,
377 input.permissions,
378 input.pdata,
379 input.dsign_algo,
380 input.kagree_algo,
381 );
382 let mut h = DataHolder::new(class_id::REPLY);
383 h.set_property(prop::C_DSIGN_ALGO, input.dsign_algo.to_owned());
384 h.set_property(prop::C_KAGREE_ALGO, input.kagree_algo.to_owned());
385 h.set_binary_property(prop::C_ID, input.cert_der.to_vec());
386 h.set_binary_property(prop::C_PERM, input.permissions.to_vec());
387 h.set_binary_property(prop::C_PDATA, input.pdata.to_vec());
388 h.set_binary_property(prop::HASH_C2, hash_c2.to_vec());
389 h.set_binary_property(prop::HASH_C1, input.hash_c1.to_vec());
390 h.set_binary_property(prop::DH2, input.dh2.to_vec());
391 h.set_binary_property(prop::DH1, input.dh1.to_vec());
392 h.set_binary_property(prop::CHALLENGE2, input.challenge2.to_vec());
393 h.set_binary_property(prop::CHALLENGE1, input.challenge1.to_vec());
394 h.set_binary_property(prop::OCSP_STATUS, input.ocsp_status.to_vec());
395 h.set_binary_property(prop::SIGNATURE, input.signature.to_vec());
396 Ok(h.to_cdr_le())
397}
398
399pub fn parse_reply_token(bytes: &[u8]) -> SecurityResult<ReplyTokenView> {
404 let h = DataHolder::from_cdr_le(bytes)?;
405 if h.class_id != class_id::REPLY {
406 return Err(SecurityError::new(
407 SecurityErrorKind::AuthenticationFailed,
408 "reply: class_id mismatch",
409 ));
410 }
411 let cert_der = take_bin(&h, prop::C_ID, MAX_CERT_DER)?;
412 let permissions = take_bin(&h, prop::C_PERM, MAX_TOKEN_BYTES)?;
413 let pdata = take_bin(&h, prop::C_PDATA, MAX_TOKEN_BYTES)?;
414 let dsign_algo = take_prop(&h, prop::C_DSIGN_ALGO)?.to_owned();
415 let kagree_algo = take_prop(&h, prop::C_KAGREE_ALGO)?.to_owned();
416 let hash_c2 = take_fixed::<32>(&h, prop::HASH_C2)?;
417 let hash_c1 = take_fixed::<32>(&h, prop::HASH_C1)?;
418 let dh2 = take_bin(&h, prop::DH2, MAX_DH_PUB)?;
419 let dh1 = take_bin(&h, prop::DH1, MAX_DH_PUB)?;
420 let challenge2 = take_fixed::<32>(&h, prop::CHALLENGE2)?;
421 let challenge1 = take_fixed::<32>(&h, prop::CHALLENGE1)?;
422 let ocsp_status = h.binary_property(prop::OCSP_STATUS).unwrap_or(&[]).to_vec();
423 let signature = take_bin(&h, prop::SIGNATURE, MAX_SIGNATURE)?;
424
425 let recomputed = compute_hash_c(&cert_der, &permissions, &pdata, &dsign_algo, &kagree_algo);
426 if !ct_eq(&recomputed, &hash_c2) {
427 return Err(SecurityError::new(
428 SecurityErrorKind::AuthenticationFailed,
429 "reply: hash_c2 mismatch",
430 ));
431 }
432
433 Ok(ReplyTokenView {
434 cert_der,
435 permissions,
436 pdata,
437 dsign_algo,
438 kagree_algo,
439 hash_c2,
440 hash_c1,
441 dh2,
442 dh1,
443 challenge2,
444 challenge1,
445 ocsp_status,
446 signature,
447 })
448}
449
450pub struct FinalBuildInput<'a> {
452 pub hash_c1: &'a [u8; 32],
454 pub hash_c2: &'a [u8; 32],
456 pub dh1: &'a [u8],
458 pub dh2: &'a [u8],
460 pub challenge1: &'a [u8; 32],
462 pub challenge2: &'a [u8; 32],
464 pub ocsp_status: &'a [u8],
466 pub signature: &'a [u8],
468}
469
470pub fn build_final_token(input: &FinalBuildInput<'_>) -> SecurityResult<Vec<u8>> {
475 if input.signature.len() > MAX_SIGNATURE {
476 return Err(SecurityError::new(
477 SecurityErrorKind::BadArgument,
478 "final: signature > cap",
479 ));
480 }
481 if input.dh1.len() > MAX_DH_PUB || input.dh2.len() > MAX_DH_PUB {
482 return Err(SecurityError::new(
483 SecurityErrorKind::BadArgument,
484 "final: dh > cap",
485 ));
486 }
487 let mut h = DataHolder::new(class_id::FINAL);
488 h.set_binary_property(prop::HASH_C1, input.hash_c1.to_vec());
489 h.set_binary_property(prop::HASH_C2, input.hash_c2.to_vec());
490 h.set_binary_property(prop::DH1, input.dh1.to_vec());
491 h.set_binary_property(prop::DH2, input.dh2.to_vec());
492 h.set_binary_property(prop::CHALLENGE1, input.challenge1.to_vec());
493 h.set_binary_property(prop::CHALLENGE2, input.challenge2.to_vec());
494 h.set_binary_property(prop::OCSP_STATUS, input.ocsp_status.to_vec());
495 h.set_binary_property(prop::SIGNATURE, input.signature.to_vec());
496 Ok(h.to_cdr_le())
497}
498
499pub fn parse_final_token(bytes: &[u8]) -> SecurityResult<FinalTokenView> {
504 let h = DataHolder::from_cdr_le(bytes)?;
505 if h.class_id != class_id::FINAL {
506 return Err(SecurityError::new(
507 SecurityErrorKind::AuthenticationFailed,
508 "final: class_id mismatch",
509 ));
510 }
511 Ok(FinalTokenView {
512 hash_c1: take_fixed::<32>(&h, prop::HASH_C1)?,
513 hash_c2: take_fixed::<32>(&h, prop::HASH_C2)?,
514 dh1: take_bin(&h, prop::DH1, MAX_DH_PUB)?,
515 dh2: take_bin(&h, prop::DH2, MAX_DH_PUB)?,
516 challenge1: take_fixed::<32>(&h, prop::CHALLENGE1)?,
517 challenge2: take_fixed::<32>(&h, prop::CHALLENGE2)?,
518 ocsp_status: h.binary_property(prop::OCSP_STATUS).unwrap_or(&[]).to_vec(),
519 signature: take_bin(&h, prop::SIGNATURE, MAX_SIGNATURE)?,
520 })
521}
522
523fn cap_check_request(input: &RequestBuildInput<'_>) -> SecurityResult<()> {
528 if input.cert_der.len() > MAX_CERT_DER {
529 return Err(SecurityError::new(
530 SecurityErrorKind::BadArgument,
531 "request: cert > 16 KiB",
532 ));
533 }
534 if input.dh1.len() > MAX_DH_PUB {
535 return Err(SecurityError::new(
536 SecurityErrorKind::BadArgument,
537 "request: dh1 > 256 byte",
538 ));
539 }
540 Ok(())
545}
546
547fn cap_check_reply(input: &ReplyBuildInput<'_>) -> SecurityResult<()> {
548 if input.cert_der.len() > MAX_CERT_DER {
549 return Err(SecurityError::new(
550 SecurityErrorKind::BadArgument,
551 "reply: cert > 16 KiB",
552 ));
553 }
554 if input.dh1.len() > MAX_DH_PUB || input.dh2.len() > MAX_DH_PUB {
555 return Err(SecurityError::new(
556 SecurityErrorKind::BadArgument,
557 "reply: dh > 256 byte",
558 ));
559 }
560 if input.signature.len() > MAX_SIGNATURE {
561 return Err(SecurityError::new(
562 SecurityErrorKind::BadArgument,
563 "reply: signature > cap",
564 ));
565 }
566 Ok(())
567}
568
569fn take_bin(h: &DataHolder, key: &str, max: usize) -> SecurityResult<Vec<u8>> {
570 let v = h.binary_property(key).ok_or_else(|| {
571 SecurityError::new(
572 SecurityErrorKind::AuthenticationFailed,
573 alloc::format!("missing binary property: {key}"),
574 )
575 })?;
576 if v.len() > max {
577 return Err(SecurityError::new(
578 SecurityErrorKind::BadArgument,
579 alloc::format!("property {key} > cap"),
580 ));
581 }
582 Ok(v.to_vec())
583}
584
585fn take_fixed<const N: usize>(h: &DataHolder, key: &str) -> SecurityResult<[u8; N]> {
586 let v = h.binary_property(key).ok_or_else(|| {
587 SecurityError::new(
588 SecurityErrorKind::AuthenticationFailed,
589 alloc::format!("missing binary property: {key}"),
590 )
591 })?;
592 if v.len() != N {
593 return Err(SecurityError::new(
594 SecurityErrorKind::BadArgument,
595 alloc::format!("property {key} expected {N} byte"),
596 ));
597 }
598 let mut out = [0u8; N];
599 out.copy_from_slice(v);
600 Ok(out)
601}
602
603fn take_prop<'a>(h: &'a DataHolder, key: &str) -> SecurityResult<&'a str> {
604 h.property(key).ok_or_else(|| {
605 SecurityError::new(
606 SecurityErrorKind::AuthenticationFailed,
607 alloc::format!("missing property: {key}"),
608 )
609 })
610}
611
612#[must_use]
614pub fn ct_eq(a: &[u8], b: &[u8]) -> bool {
615 if a.len() != b.len() {
616 return false;
617 }
618 let mut acc = 0u8;
619 for (x, y) in a.iter().zip(b.iter()) {
620 acc |= x ^ y;
621 }
622 acc == 0
623}
624
625#[cfg(test)]
626#[allow(clippy::expect_used, clippy::unwrap_used)]
627mod tests {
628 use super::*;
629
630 #[test]
631 fn data_holder_roundtrip() {
632 let mut h = DataHolder::new("DDS:Auth:PKI-DH:1.2+AuthReq");
633 h.set_property("c.dsign_algo", "ECDSA-SHA256");
634 h.set_binary_property("c.id", alloc::vec![0xAA; 200]);
635 let bytes = h.to_cdr_le();
636 let parsed = DataHolder::from_cdr_le(&bytes).unwrap();
637 assert_eq!(parsed.class_id, h.class_id);
638 assert_eq!(parsed.property("c.dsign_algo"), Some("ECDSA-SHA256"));
639 assert_eq!(parsed.binary_property("c.id").unwrap().len(), 200);
640 }
641
642 #[test]
643 fn data_holder_truncation_rejected() {
644 let mut h = DataHolder::new("X");
645 h.set_binary_property("a", alloc::vec![1, 2, 3]);
646 let bytes = h.to_cdr_le();
647 let truncated = &bytes[..bytes.len() - 1];
648 assert!(DataHolder::from_cdr_le(truncated).is_err());
649 }
650
651 #[test]
652 fn data_holder_replace_dup() {
653 let mut h = DataHolder::new("X");
654 h.set_property("k", "v1");
655 h.set_property("k", "v2");
656 assert_eq!(h.properties.len(), 1);
657 assert_eq!(h.property("k"), Some("v2"));
658 }
659
660 #[test]
661 fn hash_c_is_deterministic_and_input_separated() {
662 let h1 = compute_hash_c(b"a", b"b", b"c", "d", "e");
663 let h2 = compute_hash_c(b"a", b"b", b"c", "d", "e");
664 assert_eq!(h1, h2);
665 let h3 = compute_hash_c(b"ab", b"", b"c", "d", "e");
667 assert_ne!(h1, h3);
668 }
669
670 #[test]
671 fn signing_bytes_is_length_prefixed() {
672 let s1 = signing_bytes("X25519", &[0xAA; 32], &[0xBB; 32], &[0xCC; 32], &[0xDD; 32]);
673 let s2 = signing_bytes("X25519", &[0xAA; 32], &[0xBB; 32], &[0xCC; 32], &[0xDD; 32]);
674 assert_eq!(s1, s2);
675 let s3 = signing_bytes("X25519", &[0xAA; 32], &[0xBB; 32], &[0xCC; 32], &[0xDE; 32]);
676 assert_ne!(s1, s3);
677 }
678
679 fn make_request_input<'a>(
684 cert_der: &'a [u8],
685 dh1: &'a [u8],
686 challenge1: &'a [u8; 32],
687 ) -> RequestBuildInput<'a> {
688 RequestBuildInput {
689 cert_der,
690 permissions: &[],
691 pdata: &[],
692 dsign_algo: "ECDSA-SHA256",
693 kagree_algo: "DH+MODP-2048-256",
694 dh1,
695 challenge1,
696 ocsp_status: &[],
697 }
698 }
699
700 #[test]
703 fn cap_check_request_cert_der_at_cap_accepted() {
704 let cert = vec![0u8; MAX_CERT_DER];
705 let dh = vec![0u8; 32];
706 let ch = [0u8; 32];
707 let res = cap_check_request(&make_request_input(&cert, &dh, &ch));
708 assert!(res.is_ok());
709 }
710 #[test]
711 fn cap_check_request_cert_der_over_cap_rejected() {
712 let cert = vec![0u8; MAX_CERT_DER + 1];
713 let dh = vec![0u8; 32];
714 let ch = [0u8; 32];
715 let err = cap_check_request(&make_request_input(&cert, &dh, &ch)).unwrap_err();
716 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
717 }
718
719 #[test]
721 fn cap_check_request_dh1_at_cap_accepted() {
722 let cert = vec![0u8; 100];
723 let dh = vec![0u8; MAX_DH_PUB];
724 let ch = [0u8; 32];
725 assert!(cap_check_request(&make_request_input(&cert, &dh, &ch)).is_ok());
726 }
727 #[test]
728 fn cap_check_request_dh1_over_cap_rejected() {
729 let cert = vec![0u8; 100];
730 let dh = vec![0u8; MAX_DH_PUB + 1];
731 let ch = [0u8; 32];
732 let err = cap_check_request(&make_request_input(&cert, &dh, &ch)).unwrap_err();
733 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
734 }
735
736 #[test]
739 fn build_final_dh_both_exactly_at_cap_accepted() {
740 let dh = vec![0u8; MAX_DH_PUB];
741 let sig = vec![0u8; 64];
742 assert!(build_final_token(&make_final_input(&dh, &dh, &sig)).is_ok());
743 }
744
745 fn make_reply_input<'a>(
746 cert_der: &'a [u8],
747 dh1: &'a [u8],
748 dh2: &'a [u8],
749 signature: &'a [u8],
750 ) -> ReplyBuildInput<'a> {
751 ReplyBuildInput {
752 cert_der,
753 permissions: &[],
754 pdata: &[],
755 dsign_algo: "ECDSA-SHA256",
756 kagree_algo: "DH+MODP-2048-256",
757 dh1,
758 dh2,
759 challenge1: &[0u8; 32],
760 challenge2: &[0u8; 32],
761 ocsp_status: &[],
762 signature,
763 hash_c1: &[0u8; 32],
764 }
765 }
766
767 #[test]
769 fn cap_check_reply_cert_der_at_and_over_cap() {
770 let dh = vec![0u8; 32];
771 let sig = vec![0u8; 64];
772
773 let cert_at = vec![0u8; MAX_CERT_DER];
774 assert!(cap_check_reply(&make_reply_input(&cert_at, &dh, &dh, &sig)).is_ok());
775
776 let cert_over = vec![0u8; MAX_CERT_DER + 1];
777 let err = cap_check_reply(&make_reply_input(&cert_over, &dh, &dh, &sig)).unwrap_err();
778 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
779 }
780
781 #[test]
785 fn cap_check_reply_dh1_only_over_cap_rejected() {
786 let cert = vec![0u8; 100];
787 let dh1 = vec![0u8; MAX_DH_PUB + 1];
788 let dh2 = vec![0u8; MAX_DH_PUB];
789 let sig = vec![0u8; 64];
790 let err = cap_check_reply(&make_reply_input(&cert, &dh1, &dh2, &sig)).unwrap_err();
791 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
792 }
793 #[test]
794 fn cap_check_reply_dh2_only_over_cap_rejected() {
795 let cert = vec![0u8; 100];
796 let dh1 = vec![0u8; MAX_DH_PUB];
797 let dh2 = vec![0u8; MAX_DH_PUB + 1];
798 let sig = vec![0u8; 64];
799 let err = cap_check_reply(&make_reply_input(&cert, &dh1, &dh2, &sig)).unwrap_err();
800 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
801 }
802 #[test]
803 fn cap_check_reply_dh_both_at_cap_accepted() {
804 let cert = vec![0u8; 100];
805 let dh = vec![0u8; MAX_DH_PUB];
806 let sig = vec![0u8; 64];
807 assert!(cap_check_reply(&make_reply_input(&cert, &dh, &dh, &sig)).is_ok());
808 }
809
810 #[test]
812 fn cap_check_reply_signature_at_and_over_cap() {
813 let cert = vec![0u8; 100];
814 let dh = vec![0u8; 32];
815
816 let sig_at = vec![0u8; MAX_SIGNATURE];
817 assert!(cap_check_reply(&make_reply_input(&cert, &dh, &dh, &sig_at)).is_ok());
818
819 let sig_over = vec![0u8; MAX_SIGNATURE + 1];
820 let err = cap_check_reply(&make_reply_input(&cert, &dh, &dh, &sig_over)).unwrap_err();
821 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
822 }
823
824 fn make_final_input<'a>(dh1: &'a [u8], dh2: &'a [u8], sig: &'a [u8]) -> FinalBuildInput<'a> {
826 FinalBuildInput {
827 hash_c1: &[0u8; 32],
828 hash_c2: &[0u8; 32],
829 dh1,
830 dh2,
831 challenge1: &[0u8; 32],
832 challenge2: &[0u8; 32],
833 ocsp_status: &[],
834 signature: sig,
835 }
836 }
837
838 #[test]
839 fn build_final_signature_at_and_over_cap() {
840 let dh = vec![0u8; 32];
841
842 let sig_at = vec![0u8; MAX_SIGNATURE];
843 assert!(build_final_token(&make_final_input(&dh, &dh, &sig_at)).is_ok());
844
845 let sig_over = vec![0u8; MAX_SIGNATURE + 1];
846 let err = build_final_token(&make_final_input(&dh, &dh, &sig_over)).unwrap_err();
847 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
848 }
849
850 #[test]
852 fn build_final_dh1_only_over_cap_rejected() {
853 let dh1 = vec![0u8; MAX_DH_PUB + 1];
854 let dh2 = vec![0u8; MAX_DH_PUB];
855 let sig = vec![0u8; 64];
856 let err = build_final_token(&make_final_input(&dh1, &dh2, &sig)).unwrap_err();
857 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
858 }
859 #[test]
860 fn build_final_dh2_only_over_cap_rejected() {
861 let dh1 = vec![0u8; MAX_DH_PUB];
862 let dh2 = vec![0u8; MAX_DH_PUB + 1];
863 let sig = vec![0u8; 64];
864 let err = build_final_token(&make_final_input(&dh1, &dh2, &sig)).unwrap_err();
865 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
866 }
867
868 #[test]
871 fn take_bin_at_and_over_max() {
872 let mut h = DataHolder::new("test");
873 h.set_binary_property("k", vec![0u8; 100]);
874 assert!(take_bin(&h, "k", 100).is_ok());
876 let err = take_bin(&h, "k", 99).unwrap_err();
878 assert_eq!(err.kind, SecurityErrorKind::BadArgument);
879 }
880}