1use std::sync::Arc;
24
25use axum::extract::State;
26use axum::http::{HeaderValue, StatusCode, header};
27use axum::response::{IntoResponse, Response};
28
29use crate::auth::OptionalAuth;
30use crate::config::{CsrTemplate, CsrTemplateRdn};
31use crate::error::KipukaError;
32use crate::routes::LabelExtractor;
33use crate::routes::est::{content_types, encode_est_base64};
34use crate::state::AppState;
35
36pub mod oids {
38 pub const CHALLENGE_PASSWORD: &str = "1.2.840.113549.1.9.7";
40
41 pub const EXTENSION_REQUEST: &str = "1.2.840.113549.1.9.14";
43
44 pub const EC_PUBLIC_KEY: &str = "1.2.840.10045.2.1";
46
47 pub const RSA_ENCRYPTION: &str = "1.2.840.113549.1.1.1";
49
50 pub const SECP256R1: &str = "1.2.840.10045.3.1.7";
52
53 pub const SECP384R1: &str = "1.3.132.0.34";
55
56 pub const KEY_USAGE: &str = "2.5.29.15";
58
59 pub const EXT_KEY_USAGE: &str = "2.5.29.37";
61
62 pub const SUBJECT_ALT_NAME: &str = "2.5.29.17";
64
65 pub const CSR_TEMPLATE: &str = "1.2.840.113549.1.9.16.2.61";
68
69 pub const CSR_TEMPLATE_COMPONENTS: &[u32] = &[1, 2, 840, 113549, 1, 9, 16, 2, 61];
71
72 pub const EXTENSION_REQ_TEMPLATE: &str = "1.2.840.113549.1.9.16.2.62";
75
76 pub const EXTENSION_REQ_TEMPLATE_COMPONENTS: &[u32] = &[1, 2, 840, 113549, 1, 9, 16, 2, 62];
78
79 pub const COMMON_NAME: &str = "2.5.4.3";
83
84 pub const ORGANIZATION_NAME: &str = "2.5.4.10";
86
87 pub const COUNTRY_NAME: &str = "2.5.4.6";
89
90 pub const STATE_OR_PROVINCE: &str = "2.5.4.8";
92
93 pub const LOCALITY_NAME: &str = "2.5.4.7";
95
96 pub const ORG_UNIT_NAME: &str = "2.5.4.11";
98}
99
100pub async fn get_csrattrs(
132 _auth: OptionalAuth,
133 label: LabelExtractor,
134 State(state): State<Arc<AppState>>,
135) -> Result<Response, KipukaError> {
136 let ca_id = label.ca_id();
137
138 tracing::debug!(
139 ca_id = %ca_id,
140 label = %label.label,
141 "serving CSR attributes"
142 );
143
144 let attributes = if !label.csr_attributes.is_empty() {
146 &label.csr_attributes
147 } else {
148 &state.config.est.csr_attributes
149 };
150
151 let template = label
153 .csr_template
154 .as_ref()
155 .or(state.config.est.csr_template.as_ref());
156
157 let has_template = template.is_some_and(|t| t.has_content());
160 if attributes.is_empty() && !has_template {
161 return Ok(StatusCode::NO_CONTENT.into_response());
162 }
163
164 let csrattrs_der = encode_csr_attrs_with_template(attributes, template)?;
168
169 let body = encode_est_base64(&csrattrs_der);
170
171 let mut resp = (StatusCode::OK, body).into_response();
172 resp.headers_mut().insert(
173 header::CONTENT_TYPE,
174 HeaderValue::from_static(content_types::CSR_ATTRS),
175 );
176 resp.headers_mut().insert(
177 header::HeaderName::from_static("content-transfer-encoding"),
178 HeaderValue::from_static(content_types::TRANSFER_ENCODING_BASE64),
179 );
180
181 Ok(resp)
182}
183
184pub(crate) fn encode_csr_attrs(oid_strings: &[String]) -> Result<Vec<u8>, KipukaError> {
200 use synta::{Encoder, Encoding, ObjectIdentifier, Tag, tag};
201
202 if oid_strings.is_empty() {
203 return Ok(Vec::new());
204 }
205
206 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
207 let mut enc = Encoder::new(Encoding::Der);
208
209 enc.start_constructed_no_guard(seq_tag)
211 .map_err(|e| KipukaError::Internal(format!("CsrAttrs SEQUENCE: {e}")))?;
212
213 for oid_str in oid_strings {
214 let components = resolve_oid_components(oid_str)?;
217 let oid = ObjectIdentifier::new(&components)
218 .map_err(|e| KipukaError::Internal(format!("invalid OID '{oid_str}': {e}")))?;
219
220 enc.encode(&oid)
221 .map_err(|e| KipukaError::Internal(format!("OID encode '{oid_str}': {e}")))?;
222 }
223
224 enc.end_constructed()
226 .map_err(|e| KipukaError::Internal(format!("CsrAttrs end: {e}")))?;
227
228 let der_bytes = enc
229 .finish()
230 .map_err(|e| KipukaError::Internal(format!("CsrAttrs DER finish: {e}")))?;
231
232 tracing::debug!(
233 num_oids = oid_strings.len(),
234 der_len = der_bytes.len(),
235 "encoded CSR attributes"
236 );
237
238 Ok(der_bytes)
239}
240
241fn resolve_oid_components(oid_str: &str) -> Result<Vec<u32>, KipukaError> {
246 let components: &[u32] = match oid_str {
248 oids::CHALLENGE_PASSWORD => synta_certificate::oids::PKCS9_CHALLENGE_PASSWORD,
249 oids::EXTENSION_REQUEST => synta_certificate::oids::PKCS9_EXTENSION_REQUEST,
250 oids::EC_PUBLIC_KEY => synta_certificate::oids::EC_PUBLIC_KEY,
251 oids::RSA_ENCRYPTION => synta_certificate::oids::RSA_ENCRYPTION,
252 oids::KEY_USAGE => synta_certificate::oids::KEY_USAGE,
253 oids::EXT_KEY_USAGE => synta_certificate::oids::EXTENDED_KEY_USAGE,
254 oids::SUBJECT_ALT_NAME => synta_certificate::oids::SUBJECT_ALT_NAME,
255 oids::SECP256R1 => synta_certificate::oids::EC_CURVE_P256,
256 oids::SECP384R1 => synta_certificate::oids::EC_CURVE_P384,
257 _ => {
258 let parts: Result<Vec<u32>, _> = oid_str.split('.').map(|s| s.parse::<u32>()).collect();
260 return parts.map_err(|e| {
261 KipukaError::Internal(format!("invalid OID string '{oid_str}': {e}"))
262 });
263 }
264 };
265 Ok(components.to_vec())
266}
267
268pub(crate) fn encode_csr_attrs_with_template(
276 oid_strings: &[String],
277 template: Option<&CsrTemplate>,
278) -> Result<Vec<u8>, KipukaError> {
279 use synta::{Encoder, Encoding, ObjectIdentifier, Tag, tag};
280
281 let template = template.filter(|t| t.has_content());
283
284 if oid_strings.is_empty() && template.is_none() {
285 return Ok(Vec::new());
286 }
287
288 let Some(template) = template else {
290 return encode_csr_attrs(oid_strings);
291 };
292
293 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
294 let mut enc = Encoder::new(Encoding::Der);
295
296 enc.start_constructed_no_guard(seq_tag)
298 .map_err(|e| KipukaError::Internal(format!("CsrAttrs SEQUENCE: {e}")))?;
299
300 for oid_str in oid_strings {
302 let components = resolve_oid_components(oid_str)?;
303 let oid = ObjectIdentifier::new(&components)
304 .map_err(|e| KipukaError::Internal(format!("invalid OID '{oid_str}': {e}")))?;
305 enc.encode(&oid)
306 .map_err(|e| KipukaError::Internal(format!("OID encode '{oid_str}': {e}")))?;
307 }
308
309 let attr_der = encode_template_attribute(template)?;
311 enc.write_bytes(&attr_der);
312
313 enc.end_constructed()
314 .map_err(|e| KipukaError::Internal(format!("CsrAttrs end: {e}")))?;
315
316 let der_bytes = enc
317 .finish()
318 .map_err(|e| KipukaError::Internal(format!("CsrAttrs DER finish: {e}")))?;
319
320 tracing::debug!(
321 num_oids = oid_strings.len(),
322 has_template = true,
323 der_len = der_bytes.len(),
324 "encoded CSR attributes with template"
325 );
326
327 Ok(der_bytes)
328}
329
330fn encode_template_attribute(template: &CsrTemplate) -> Result<Vec<u8>, KipukaError> {
339 use synta::{Encoder, Encoding, ObjectIdentifier, Tag, tag};
340
341 let mut enc = Encoder::new(Encoding::Der);
342 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
343 let set_tag = Tag::universal_constructed(tag::TAG_SET);
344
345 enc.start_constructed_no_guard(seq_tag)
347 .map_err(|e| KipukaError::Internal(format!("Attribute SEQUENCE: {e}")))?;
348
349 let oid = ObjectIdentifier::new(oids::CSR_TEMPLATE_COMPONENTS)
351 .map_err(|e| KipukaError::Internal(format!("template OID: {e}")))?;
352 enc.encode(&oid)
353 .map_err(|e| KipukaError::Internal(format!("template OID encode: {e}")))?;
354
355 enc.start_constructed_no_guard(set_tag)
357 .map_err(|e| KipukaError::Internal(format!("attrValues SET: {e}")))?;
358
359 let cri_der = encode_cri_template(template)?;
360 enc.write_bytes(&cri_der);
361
362 enc.end_constructed()
363 .map_err(|e| KipukaError::Internal(format!("attrValues SET end: {e}")))?;
364
365 enc.end_constructed()
367 .map_err(|e| KipukaError::Internal(format!("Attribute end: {e}")))?;
368
369 enc.finish()
370 .map_err(|e| KipukaError::Internal(format!("Attribute DER finish: {e}")))
371}
372
373fn encode_cri_template(template: &CsrTemplate) -> Result<Vec<u8>, KipukaError> {
388 use synta::{Encoder, Encoding, Integer, Tag, tag};
389
390 let mut enc = Encoder::new(Encoding::Der);
391 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
392
393 enc.start_constructed_no_guard(seq_tag)
394 .map_err(|e| KipukaError::Internal(format!("CRI template SEQUENCE: {e}")))?;
395
396 let version = Integer::from_i64(0);
398 enc.encode(&version)
399 .map_err(|e| KipukaError::Internal(format!("CRI version: {e}")))?;
400
401 if !template.subject.is_empty() {
403 let name_der = encode_subject_template(&template.subject)?;
404 enc.write_bytes(&name_der);
405 }
406
407 if let Some(ref key_alg) = template.key_algorithm {
409 let spki_der = encode_spki_template(key_alg)?;
410 let ctx0 = Tag::context_specific_constructed(0);
411 enc.start_constructed_no_guard(ctx0)
412 .map_err(|e| KipukaError::Internal(format!("[0] SPKI tag: {e}")))?;
413 enc.write_bytes(&spki_der);
414 enc.end_constructed()
415 .map_err(|e| KipukaError::Internal(format!("[0] SPKI end: {e}")))?;
416 }
417
418 let ctx1 = Tag::context_specific_constructed(1);
420 if !template.required_extensions.is_empty() {
421 let ext_der = encode_extension_template(&template.required_extensions)?;
422 enc.start_constructed_no_guard(ctx1)
423 .map_err(|e| KipukaError::Internal(format!("[1] attrs tag: {e}")))?;
424 enc.write_bytes(&ext_der);
425 enc.end_constructed()
426 .map_err(|e| KipukaError::Internal(format!("[1] attrs end: {e}")))?;
427 } else {
428 enc.start_constructed_no_guard(ctx1)
430 .map_err(|e| KipukaError::Internal(format!("[1] empty attrs tag: {e}")))?;
431 enc.end_constructed()
432 .map_err(|e| KipukaError::Internal(format!("[1] empty attrs end: {e}")))?;
433 }
434
435 enc.end_constructed()
436 .map_err(|e| KipukaError::Internal(format!("CRI template end: {e}")))?;
437
438 enc.finish()
439 .map_err(|e| KipukaError::Internal(format!("CRI template DER finish: {e}")))
440}
441
442fn encode_subject_template(rdns: &[CsrTemplateRdn]) -> Result<Vec<u8>, KipukaError> {
455 use synta::types::string::Utf8StringRef;
456 use synta::{Encoder, Encoding, ObjectIdentifier, Tag, tag};
457
458 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
459 let set_tag = Tag::universal_constructed(tag::TAG_SET);
460
461 let mut enc = Encoder::new(Encoding::Der);
462
463 enc.start_constructed_no_guard(seq_tag)
465 .map_err(|e| KipukaError::Internal(format!("Name SEQUENCE: {e}")))?;
466
467 for rdn in rdns {
468 let components = resolve_oid_components(&rdn.oid)?;
469 let oid = ObjectIdentifier::new(&components)
470 .map_err(|e| KipukaError::Internal(format!("RDN OID '{}': {e}", rdn.oid)))?;
471
472 enc.start_constructed_no_guard(set_tag)
474 .map_err(|e| KipukaError::Internal(format!("RDN SET: {e}")))?;
475
476 enc.start_constructed_no_guard(seq_tag)
478 .map_err(|e| KipukaError::Internal(format!("AttrTypeAndValue SEQUENCE: {e}")))?;
479
480 enc.encode(&oid)
481 .map_err(|e| KipukaError::Internal(format!("RDN OID encode: {e}")))?;
482
483 if let Some(value) = &rdn.value {
485 let val = Utf8StringRef::new(value.as_str());
486 enc.encode(&val)
487 .map_err(|e| KipukaError::Internal(format!("RDN value encode: {e}")))?;
488 }
489
490 enc.end_constructed()
491 .map_err(|e| KipukaError::Internal(format!("AttrTypeAndValue end: {e}")))?;
492
493 enc.end_constructed()
494 .map_err(|e| KipukaError::Internal(format!("RDN SET end: {e}")))?;
495 }
496
497 enc.end_constructed()
498 .map_err(|e| KipukaError::Internal(format!("Name end: {e}")))?;
499
500 enc.finish()
501 .map_err(|e| KipukaError::Internal(format!("Name DER finish: {e}")))
502}
503
504fn encode_spki_template(key_alg: &str) -> Result<Vec<u8>, KipukaError> {
520 use synta::{Encoder, Encoding, Null, ObjectIdentifier, Tag, tag};
521
522 let (alg_oid_components, param_oid_components) = parse_key_algorithm(key_alg)?;
523 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
524
525 let mut enc = Encoder::new(Encoding::Der);
526
527 enc.start_constructed_no_guard(seq_tag)
529 .map_err(|e| KipukaError::Internal(format!("AlgId SEQUENCE: {e}")))?;
530
531 let alg_oid = ObjectIdentifier::new(&alg_oid_components)
532 .map_err(|e| KipukaError::Internal(format!("AlgId OID: {e}")))?;
533 enc.encode(&alg_oid)
534 .map_err(|e| KipukaError::Internal(format!("AlgId OID encode: {e}")))?;
535
536 if let Some(ref param_comps) = param_oid_components {
538 let param_oid = ObjectIdentifier::new(param_comps)
539 .map_err(|e| KipukaError::Internal(format!("curve OID: {e}")))?;
540 enc.encode(¶m_oid)
541 .map_err(|e| KipukaError::Internal(format!("curve OID encode: {e}")))?;
542 } else {
543 enc.encode(&Null)
544 .map_err(|e| KipukaError::Internal(format!("NULL param encode: {e}")))?;
545 }
546
547 enc.end_constructed()
548 .map_err(|e| KipukaError::Internal(format!("AlgId end: {e}")))?;
549
550 enc.finish()
553 .map_err(|e| KipukaError::Internal(format!("SPKI DER finish: {e}")))
554}
555
556fn parse_key_algorithm(spec: &str) -> Result<(Vec<u32>, Option<Vec<u32>>), KipukaError> {
566 let parts: Vec<&str> = spec.splitn(2, ':').collect();
567 if parts.len() != 2 {
568 return Err(KipukaError::Internal(format!(
569 "invalid key_algorithm format '{spec}': expected 'ec:<curve>' or 'rsa:<bits>'"
570 )));
571 }
572
573 match parts[0].to_lowercase().as_str() {
574 "ec" | "ecdsa" => {
575 let curve_oid = match parts[1] {
576 "P-256" | "p-256" | "secp256r1" | "prime256v1" => {
577 synta_certificate::oids::EC_CURVE_P256.to_vec()
578 }
579 "P-384" | "p-384" | "secp384r1" => synta_certificate::oids::EC_CURVE_P384.to_vec(),
580 "P-521" | "p-521" | "secp521r1" => synta_certificate::oids::EC_CURVE_P521.to_vec(),
581 other => {
582 return Err(KipukaError::Internal(format!(
583 "unsupported EC curve '{other}'"
584 )));
585 }
586 };
587 Ok((
588 synta_certificate::oids::EC_PUBLIC_KEY.to_vec(),
589 Some(curve_oid),
590 ))
591 }
592 "rsa" => Ok((synta_certificate::oids::RSA_ENCRYPTION.to_vec(), None)),
593 other => Err(KipukaError::Internal(format!(
594 "unsupported key algorithm family '{other}'"
595 ))),
596 }
597}
598
599fn encode_extension_template(ext_oids: &[String]) -> Result<Vec<u8>, KipukaError> {
617 use synta::{Encoder, Encoding, ObjectIdentifier, Tag, tag};
618
619 let seq_tag = Tag::universal_constructed(tag::TAG_SEQUENCE);
620 let set_tag = Tag::universal_constructed(tag::TAG_SET);
621
622 let mut enc = Encoder::new(Encoding::Der);
623
624 enc.start_constructed_no_guard(seq_tag)
626 .map_err(|e| KipukaError::Internal(format!("ExtReq Attribute SEQUENCE: {e}")))?;
627
628 let ext_req_oid = ObjectIdentifier::new(oids::EXTENSION_REQ_TEMPLATE_COMPONENTS)
630 .map_err(|e| KipukaError::Internal(format!("extensionReqTemplate OID: {e}")))?;
631 enc.encode(&ext_req_oid)
632 .map_err(|e| KipukaError::Internal(format!("extensionReqTemplate OID encode: {e}")))?;
633
634 enc.start_constructed_no_guard(set_tag)
636 .map_err(|e| KipukaError::Internal(format!("ExtReq SET: {e}")))?;
637
638 enc.start_constructed_no_guard(seq_tag)
640 .map_err(|e| KipukaError::Internal(format!("ExtensionTemplates SEQUENCE: {e}")))?;
641
642 for ext_oid_str in ext_oids {
643 let components = resolve_oid_components(ext_oid_str)?;
644 let oid = ObjectIdentifier::new(&components)
645 .map_err(|e| KipukaError::Internal(format!("ext OID '{ext_oid_str}': {e}")))?;
646
647 enc.start_constructed_no_guard(seq_tag)
650 .map_err(|e| KipukaError::Internal(format!("ExtensionTemplate SEQUENCE: {e}")))?;
651 enc.encode(&oid)
652 .map_err(|e| KipukaError::Internal(format!("ext OID encode: {e}")))?;
653 enc.end_constructed()
654 .map_err(|e| KipukaError::Internal(format!("ExtensionTemplate end: {e}")))?;
655 }
656
657 enc.end_constructed()
658 .map_err(|e| KipukaError::Internal(format!("ExtensionTemplates end: {e}")))?;
659
660 enc.end_constructed()
661 .map_err(|e| KipukaError::Internal(format!("ExtReq SET end: {e}")))?;
662
663 enc.end_constructed()
664 .map_err(|e| KipukaError::Internal(format!("ExtReq Attribute end: {e}")))?;
665
666 enc.finish()
667 .map_err(|e| KipukaError::Internal(format!("ExtReq DER finish: {e}")))
668}
669
670#[cfg(test)]
671mod tests {
672 use super::*;
673 use synta::{Decoder, Encoding, Length, ObjectIdentifier, Tag, tag};
674
675 fn decode_oid_at(decoder: &mut Decoder<'_>) -> ObjectIdentifier {
676 decoder.decode::<ObjectIdentifier>().expect("decode OID")
677 }
678
679 fn read_tl(decoder: &mut Decoder<'_>) -> (Tag, Length) {
680 let tag = decoder.read_tag().expect("read tag");
681 let len = decoder.read_length().expect("read length");
682 (tag, len)
683 }
684
685 #[test]
686 fn oid_constant_matches_rfc9908() {
687 assert_eq!(oids::CSR_TEMPLATE, "1.2.840.113549.1.9.16.2.61");
688 assert_eq!(
689 oids::CSR_TEMPLATE_COMPONENTS,
690 &[1, 2, 840, 113549, 1, 9, 16, 2, 61]
691 );
692 assert_eq!(oids::EXTENSION_REQ_TEMPLATE, "1.2.840.113549.1.9.16.2.62");
693 }
694
695 #[test]
696 fn encode_template_subject_only() {
697 let template = CsrTemplate {
698 subject: vec![
699 CsrTemplateRdn {
700 oid: oids::ORGANIZATION_NAME.to_string(),
701 value: Some("Example Corp".to_string()),
702 },
703 CsrTemplateRdn {
704 oid: oids::COMMON_NAME.to_string(),
705 value: None,
706 },
707 ],
708 key_algorithm: None,
709 required_extensions: vec![],
710 };
711
712 let der = encode_csr_attrs_with_template(&[], Some(&template)).unwrap();
713 assert!(!der.is_empty());
714
715 let mut dec = Decoder::new(&der, Encoding::Der);
717 let (tag, _len) = read_tl(&mut dec);
718 assert!(tag.is_constructed());
719 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
720
721 let (tag, _len) = read_tl(&mut dec);
723 assert!(tag.is_constructed());
724 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
725
726 let attr_oid = decode_oid_at(&mut dec);
728 let expected = ObjectIdentifier::new(oids::CSR_TEMPLATE_COMPONENTS).unwrap();
729 assert_eq!(attr_oid, expected);
730 }
731
732 #[test]
733 fn encode_template_ec_p256() {
734 let template = CsrTemplate {
735 subject: vec![],
736 key_algorithm: Some("ec:P-256".to_string()),
737 required_extensions: vec![],
738 };
739
740 let der = encode_csr_attrs_with_template(&[], Some(&template)).unwrap();
741 assert!(!der.is_empty());
742
743 let inner = encode_cri_template(&template).unwrap();
746 let mut dec = Decoder::new(&inner, Encoding::Der);
747
748 let (tag, _) = read_tl(&mut dec);
750 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
751
752 let version: synta::Integer = dec.decode().unwrap();
754 assert_eq!(version.as_i64().unwrap(), 0);
755
756 let (tag, _) = read_tl(&mut dec);
758 assert_eq!(tag.class(), synta::TagClass::ContextSpecific);
759 assert_eq!(tag.number(), 0);
760
761 let (tag, _) = read_tl(&mut dec);
763 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
764
765 let alg_oid = decode_oid_at(&mut dec);
767 let ec_pk = ObjectIdentifier::new(synta_certificate::oids::EC_PUBLIC_KEY).unwrap();
768 assert_eq!(alg_oid, ec_pk);
769
770 let curve_oid = decode_oid_at(&mut dec);
772 let p256 = ObjectIdentifier::new(synta_certificate::oids::EC_CURVE_P256).unwrap();
773 assert_eq!(curve_oid, p256);
774 }
775
776 #[test]
777 fn encode_template_rsa() {
778 let template = CsrTemplate {
779 subject: vec![],
780 key_algorithm: Some("rsa:2048".to_string()),
781 required_extensions: vec![],
782 };
783
784 let inner = encode_cri_template(&template).unwrap();
785 let mut dec = Decoder::new(&inner, Encoding::Der);
786
787 read_tl(&mut dec);
789 let _: synta::Integer = dec.decode().unwrap();
791 read_tl(&mut dec);
793 read_tl(&mut dec);
795
796 let alg_oid = decode_oid_at(&mut dec);
797 let rsa = ObjectIdentifier::new(synta_certificate::oids::RSA_ENCRYPTION).unwrap();
798 assert_eq!(alg_oid, rsa);
799
800 let null: synta::Null = dec.decode().unwrap();
802 assert_eq!(null, synta::Null);
803 }
804
805 #[test]
806 fn encode_template_extensions_only() {
807 let template = CsrTemplate {
808 subject: vec![],
809 key_algorithm: None,
810 required_extensions: vec![
811 oids::SUBJECT_ALT_NAME.to_string(),
812 oids::KEY_USAGE.to_string(),
813 ],
814 };
815
816 let der = encode_csr_attrs_with_template(&[], Some(&template)).unwrap();
817 assert!(!der.is_empty());
818
819 let inner = encode_cri_template(&template).unwrap();
821 let mut dec = Decoder::new(&inner, Encoding::Der);
822
823 read_tl(&mut dec);
825 let _: synta::Integer = dec.decode().unwrap();
827 let (tag, _) = read_tl(&mut dec);
829 assert_eq!(tag.class(), synta::TagClass::ContextSpecific);
830 assert_eq!(tag.number(), 1);
831 }
832
833 #[test]
834 fn encode_template_combined_with_oids() {
835 let template = CsrTemplate {
836 subject: vec![CsrTemplateRdn {
837 oid: oids::COMMON_NAME.to_string(),
838 value: Some("test.example.com".to_string()),
839 }],
840 key_algorithm: Some("ec:P-384".to_string()),
841 required_extensions: vec![oids::SUBJECT_ALT_NAME.to_string()],
842 };
843
844 let oid_list = vec![
845 oids::CHALLENGE_PASSWORD.to_string(),
846 oids::EXTENSION_REQUEST.to_string(),
847 ];
848
849 let der = encode_csr_attrs_with_template(&oid_list, Some(&template)).unwrap();
850 assert!(!der.is_empty());
851
852 let mut dec = Decoder::new(&der, Encoding::Der);
854 let (tag, _) = read_tl(&mut dec);
855 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
856
857 let oid1 = decode_oid_at(&mut dec);
859 let challenge =
860 ObjectIdentifier::new(synta_certificate::oids::PKCS9_CHALLENGE_PASSWORD).unwrap();
861 assert_eq!(oid1, challenge);
862
863 let oid2 = decode_oid_at(&mut dec);
864 let ext_req =
865 ObjectIdentifier::new(synta_certificate::oids::PKCS9_EXTENSION_REQUEST).unwrap();
866 assert_eq!(oid2, ext_req);
867
868 let (tag, _) = read_tl(&mut dec);
870 assert!(tag.is_constructed());
871 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
872 }
873
874 #[test]
875 fn absent_rdn_value_omits_value_field() {
876 let rdns = vec![CsrTemplateRdn {
877 oid: oids::COMMON_NAME.to_string(),
878 value: None,
879 }];
880 let name_der = encode_subject_template(&rdns).unwrap();
881
882 let mut dec = Decoder::new(&name_der, Encoding::Der);
884 read_tl(&mut dec);
886 read_tl(&mut dec);
888 let (_, atv_len) = read_tl(&mut dec);
890 let atv_len = atv_len.definite().unwrap();
891
892 let oid = decode_oid_at(&mut dec);
894 let cn = ObjectIdentifier::new(synta_certificate::oids::attr::COMMON_NAME).unwrap();
895 assert_eq!(oid, cn);
896
897 let oid_encoded_len = synta::ToDer::to_der(&oid).unwrap().len();
899 assert_eq!(atv_len, oid_encoded_len);
900 }
901
902 #[test]
903 fn present_rdn_value_includes_utf8string() {
904 let rdns = vec![CsrTemplateRdn {
905 oid: oids::ORGANIZATION_NAME.to_string(),
906 value: Some("Test Org".to_string()),
907 }];
908 let name_der = encode_subject_template(&rdns).unwrap();
909
910 let mut dec = Decoder::new(&name_der, Encoding::Der);
911 read_tl(&mut dec);
913 read_tl(&mut dec);
914 let (_, atv_len) = read_tl(&mut dec);
915 let atv_len = atv_len.definite().unwrap();
916
917 let oid = decode_oid_at(&mut dec);
918 let org = ObjectIdentifier::new(synta_certificate::oids::attr::ORGANIZATION).unwrap();
919 assert_eq!(oid, org);
920
921 let oid_len = synta::ToDer::to_der(&oid).unwrap().len();
923 assert!(atv_len > oid_len);
924 }
925
926 #[test]
927 fn parse_key_algorithm_valid() {
928 let (alg, param) = parse_key_algorithm("ec:P-256").unwrap();
929 assert_eq!(alg, synta_certificate::oids::EC_PUBLIC_KEY);
930 assert_eq!(param.unwrap(), synta_certificate::oids::EC_CURVE_P256);
931
932 let (alg, param) = parse_key_algorithm("ec:P-384").unwrap();
933 assert_eq!(alg, synta_certificate::oids::EC_PUBLIC_KEY);
934 assert_eq!(param.unwrap(), synta_certificate::oids::EC_CURVE_P384);
935
936 let (alg, param) = parse_key_algorithm("ec:P-521").unwrap();
937 assert_eq!(alg, synta_certificate::oids::EC_PUBLIC_KEY);
938 assert_eq!(param.unwrap(), synta_certificate::oids::EC_CURVE_P521);
939
940 let (alg, param) = parse_key_algorithm("rsa:2048").unwrap();
941 assert_eq!(alg, synta_certificate::oids::RSA_ENCRYPTION);
942 assert!(param.is_none());
943
944 let (alg, param) = parse_key_algorithm("rsa:4096").unwrap();
945 assert_eq!(alg, synta_certificate::oids::RSA_ENCRYPTION);
946 assert!(param.is_none());
947 }
948
949 #[test]
950 fn parse_key_algorithm_invalid() {
951 assert!(parse_key_algorithm("dsa:1024").is_err());
952 assert!(parse_key_algorithm("ec:P-999").is_err());
953 assert!(parse_key_algorithm("no-colon").is_err());
954 }
955
956 #[test]
957 fn empty_template_returns_empty() {
958 let template = CsrTemplate {
959 subject: vec![],
960 key_algorithm: None,
961 required_extensions: vec![],
962 };
963 let der = encode_csr_attrs_with_template(&[], Some(&template)).unwrap();
964 assert!(der.is_empty());
965 }
966
967 #[test]
968 fn none_template_delegates_to_oid_only() {
969 let oid_list = vec![oids::CHALLENGE_PASSWORD.to_string()];
970 let with_template = encode_csr_attrs_with_template(&oid_list, None).unwrap();
971 let oid_only = encode_csr_attrs(&oid_list).unwrap();
972 assert_eq!(with_template, oid_only);
973 }
974
975 #[test]
976 fn mandatory_attributes_field_present_when_no_extensions() {
977 let template = CsrTemplate {
978 subject: vec![CsrTemplateRdn {
979 oid: oids::COMMON_NAME.to_string(),
980 value: None,
981 }],
982 key_algorithm: None,
983 required_extensions: vec![],
984 };
985
986 let inner = encode_cri_template(&template).unwrap();
987 let mut dec = Decoder::new(&inner, Encoding::Der);
988
989 read_tl(&mut dec);
991 let _: synta::Integer = dec.decode().unwrap();
993 let (tag, _) = read_tl(&mut dec);
995 assert_eq!(tag.number(), tag::TAG_SEQUENCE);
996
997 let inner2 = encode_cri_template(&template).unwrap();
1000
1001 assert!(inner2.len() >= 2);
1003 let tail = &inner2[inner2.len() - 2..];
1004 assert_eq!(tail, &[0xA1, 0x00], "mandatory [1] empty SET missing");
1005 }
1006}