cert_helper/certificate/
usage.rs1#[cfg(feature = "pqc")]
2use super::key::{is_mlkem_pkey, is_pqc_pkey};
3#[cfg(feature = "pqc")]
4use openssl::pkey::PKey;
5use openssl::x509::extension::{ExtendedKeyUsage, KeyUsage};
6use std::collections::HashSet;
7#[allow(non_camel_case_types)]
9#[derive(Hash, Eq, PartialEq, Debug, Clone)]
10pub enum Usage {
11 certsign,
13 crlsign,
15 encipherment,
17 clientauth,
19 serverauth,
21 signature,
23 contentcommitment,
25}
26
27pub(crate) struct TrackedKeyUsage {
28 inner: KeyUsage,
29 used: bool,
30}
31
32impl TrackedKeyUsage {
33 fn new() -> Self {
34 let mut inner = KeyUsage::new();
35 inner.critical();
36 Self { inner, used: false }
37 }
38
39 fn digital_signature(&mut self) {
40 self.inner.digital_signature();
41 self.used = true;
42 }
43
44 fn non_repudiation(&mut self) {
45 self.inner.non_repudiation();
46 self.used = true;
47 }
48
49 fn key_encipherment(&mut self) {
50 self.inner.key_encipherment();
51 self.used = true;
52 }
53
54 fn key_cert_sign(&mut self) {
55 self.inner.key_cert_sign();
56 self.used = true;
57 }
58
59 fn crl_sign(&mut self) {
60 self.inner.crl_sign();
61 self.used = true;
62 }
63
64 pub(crate) fn is_used(&self) -> bool {
65 self.used
66 }
67
68 pub(crate) fn into_inner(self) -> KeyUsage {
69 self.inner
70 }
71}
72
73pub(crate) struct TrackedExtendedKeyUsage {
74 inner: ExtendedKeyUsage,
75 used: bool,
76}
77
78impl TrackedExtendedKeyUsage {
79 fn new() -> Self {
80 Self {
81 inner: ExtendedKeyUsage::new(),
82 used: false,
83 }
84 }
85
86 fn client_auth(&mut self) {
87 self.inner.client_auth();
88 self.used = true;
89 }
90
91 fn server_auth(&mut self) {
92 self.inner.server_auth();
93 self.used = true;
94 }
95
96 pub(crate) fn is_used(&self) -> bool {
97 self.used
98 }
99
100 pub(crate) fn into_inner(self) -> ExtendedKeyUsage {
101 self.inner
102 }
103}
104
105pub(crate) fn get_key_usage(
106 usage: &Option<HashSet<Usage>>,
107) -> (TrackedKeyUsage, TrackedExtendedKeyUsage) {
108 let mut ku = TrackedKeyUsage::new();
109 let mut eku = TrackedExtendedKeyUsage::new();
110 if let Some(usages) = usage {
111 for u in usages {
112 match u {
113 Usage::contentcommitment => {
114 ku.non_repudiation();
115 }
116 Usage::encipherment => {
117 ku.key_encipherment();
118 }
119 Usage::certsign => {
120 ku.key_cert_sign();
121 }
122 Usage::clientauth => {
123 eku.client_auth();
124 }
125 Usage::signature => {
126 ku.digital_signature();
127 }
128 Usage::crlsign => {
129 ku.crl_sign();
130 }
131 Usage::serverauth => {
132 eku.server_auth();
133 }
134 }
135 }
136 }
137
138 (ku, eku)
139}
140
141#[cfg(feature = "pqc")]
150pub(crate) fn validate_pqc_key_usage<T>(
151 pkey: &PKey<T>,
152 key_usage: &HashSet<Usage>,
153) -> Result<(), Box<dyn std::error::Error>> {
154 if is_pqc_pkey(pkey) && key_usage.contains(&Usage::encipherment) {
155 return Err("keyEncipherment (Usage::encipherment) is not valid for a \
156 post-quantum signature key (ML-DSA / SLH-DSA): these algorithms are \
157 signature-only and cannot perform key encipherment. Use Usage::signature \
158 (and certsign/crlsign for a CA) instead."
159 .into());
160 }
161
162 if is_mlkem_pkey(pkey)
163 && !key_usage.is_empty()
164 && *key_usage != HashSet::from([Usage::encipherment])
165 {
166 return Err("an ML-KEM (FIPS 203) key may only assert keyEncipherment \
167 (Usage::encipherment) in its KeyUsage — no other bit is permitted \
168 (no digitalSignature, keyAgreement, dataEncipherment, certsign, or \
169 crlsign). Set key_usage to exactly {Usage::encipherment}, or omit it."
170 .into());
171 }
172
173 Ok(())
174}