Bug 1933630 - Enable the partial history state update collection on GeckoView session...
[gecko.git] / security / manager / ssl / builtins / src / internal.rs
blob30bd1fcea74a69ad86d74d8d7d073b274982a64a
1 /* -*- Mode: rust; rust-indent-offset: 4 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 use pkcs11_bindings::nss::*;
7 use pkcs11_bindings::*;
9 use smallvec::SmallVec;
11 use crate::certdata::*;
13 // The token stores 2N+1 objects: one NSS root list object, N certificate objects, and N trust
14 // objects.
16 // Internally, the token identifies each object by its ObjectClass (RootList, Certificate,
17 // or Trust) and its index in the list of objects of the same class.
19 // The PKCS#11 interface, on the other hand, identifies each object with a unique, non-zero,
20 // unsigned long. This ulong is referred to as the object's CK_OBJECT_HANDLE.
22 // We're free to choose the mapping between ObjectHandles and CK_OBJECT_HANDLEs. Currently we
23 // encode the ObjectClass in the low 2 bits of the CK_OBJECT_HANDLE and the index in the higher
24 // bits. We use the values 1, 2, and 3 for ObjectClass to avoid using 0 as a CK_OBJECT_HANDLE.
26 #[derive(Clone, Copy)]
27 pub enum ObjectClass {
28     RootList = 1,
29     Certificate = 2,
30     Trust = 3,
33 #[derive(Clone, Copy)]
34 pub struct ObjectHandle {
35     class: ObjectClass,
36     index: usize,
39 impl TryFrom<CK_OBJECT_HANDLE> for ObjectHandle {
40     type Error = ();
41     fn try_from(handle: CK_OBJECT_HANDLE) -> Result<Self, Self::Error> {
42         if let Ok(handle) = usize::try_from(handle) {
43             let index = handle >> 2;
44             let class = match handle & 3 {
45                 1 if index == 0 => ObjectClass::RootList,
46                 2 if index < BUILTINS.len() => ObjectClass::Certificate,
47                 3 if index < BUILTINS.len() => ObjectClass::Trust,
48                 _ => return Err(()),
49             };
50             Ok(ObjectHandle { class, index })
51         } else {
52             Err(())
53         }
54     }
57 impl From<ObjectHandle> for CK_OBJECT_HANDLE {
58     fn from(object_handle: ObjectHandle) -> CK_OBJECT_HANDLE {
59         match CK_OBJECT_HANDLE::try_from(object_handle.index) {
60             Ok(index) => (index << 2) | (object_handle.class as CK_OBJECT_HANDLE),
61             Err(_) => 0,
62         }
63     }
66 pub fn get_attribute(attribute: CK_ATTRIBUTE_TYPE, object: &ObjectHandle) -> Option<&'static [u8]> {
67     match object.class {
68         ObjectClass::RootList => get_root_list_attribute(attribute),
69         ObjectClass::Certificate => get_cert_attribute(attribute, &BUILTINS[object.index]),
70         ObjectClass::Trust => get_trust_attribute(attribute, &BUILTINS[object.index]),
71     }
74 // Every attribute that appears in certdata.txt must have a corresponding match arm in one of the
75 // get_*_attribute functions.
77 fn get_root_list_attribute(attribute: CK_ATTRIBUTE_TYPE) -> Option<&'static [u8]> {
78     match attribute {
79         CKA_CLASS => Some(CKO_NSS_BUILTIN_ROOT_LIST_BYTES),
80         CKA_TOKEN => Some(CK_TRUE_BYTES),
81         CKA_PRIVATE => Some(CK_FALSE_BYTES),
82         CKA_MODIFIABLE => Some(CK_FALSE_BYTES),
83         CKA_LABEL => Some(&ROOT_LIST_LABEL[..]),
84         _ => None,
85     }
88 fn get_cert_attribute(attribute: CK_ATTRIBUTE_TYPE, cert: &Root) -> Option<&[u8]> {
89     match attribute {
90         CKA_CLASS => Some(CKO_CERTIFICATE_BYTES),
91         CKA_TOKEN => Some(CK_TRUE_BYTES),
92         CKA_PRIVATE => Some(CK_FALSE_BYTES),
93         CKA_MODIFIABLE => Some(CK_FALSE_BYTES),
94         CKA_LABEL => Some(cert.label.as_bytes()),
95         CKA_CERTIFICATE_TYPE => Some(CKC_X_509_BYTES),
96         CKA_SUBJECT => Some(cert.der_name()),
97         CKA_ID => Some(b"0\0"), // null terminated to match C implementation
98         CKA_ISSUER => Some(cert.der_name()),
99         CKA_SERIAL_NUMBER => Some(cert.der_serial()),
100         CKA_VALUE => Some(cert.der_cert),
101         CKA_NSS_MOZILLA_CA_POLICY => cert.mozilla_ca_policy,
102         CKA_NSS_SERVER_DISTRUST_AFTER => cert.server_distrust_after,
103         CKA_NSS_EMAIL_DISTRUST_AFTER => cert.email_distrust_after,
104         _ => None,
105     }
108 fn get_trust_attribute(attribute: CK_ATTRIBUTE_TYPE, cert: &Root) -> Option<&[u8]> {
109     match attribute {
110         CKA_CLASS => Some(CKO_NSS_TRUST_BYTES),
111         CKA_TOKEN => Some(CK_TRUE_BYTES),
112         CKA_PRIVATE => Some(CK_FALSE_BYTES),
113         CKA_MODIFIABLE => Some(CK_FALSE_BYTES),
114         CKA_LABEL => Some(cert.label.as_bytes()),
115         CKA_CERT_SHA1_HASH => Some(&cert.sha1[..]),
116         CKA_CERT_MD5_HASH => Some(&cert.md5[..]),
117         CKA_ISSUER => Some(cert.der_name()),
118         CKA_SERIAL_NUMBER => Some(cert.der_serial()),
119         CKA_TRUST_STEP_UP_APPROVED => Some(CK_FALSE_BYTES),
120         CKA_TRUST_SERVER_AUTH => Some(cert.trust_server),
121         CKA_TRUST_EMAIL_PROTECTION => Some(cert.trust_email),
122         CKA_TRUST_CODE_SIGNING => Some(CKT_NSS_MUST_VERIFY_TRUST_BYTES),
123         _ => None,
124     }
127 // A query matches an object if each term matches some attribute of the object. A search result is
128 // a list of object handles. Typical queries yield zero or one results, so we optimize for this
129 // case.
131 pub type Query<'a> = [(CK_ATTRIBUTE_TYPE, &'a [u8])];
132 pub type SearchResult = SmallVec<[ObjectHandle; 1]>;
134 pub fn search(query: &Query) -> SearchResult {
135     // The BUILTINS list is sorted by name. So if the query includes a CKA_SUBJECT or CKA_ISSUER
136     // field we can binary search.
137     for &(attr, value) in query {
138         if attr == CKA_SUBJECT || attr == CKA_ISSUER {
139             return search_by_name(value, query);
140         }
141     }
143     let mut results: SearchResult = SearchResult::default();
145     // A query with no name term might match the root list object
146     if match_root_list(query) {
147         results.push(ObjectHandle {
148             class: ObjectClass::RootList,
149             index: 0,
150         });
151     }
153     // A query with a CKA_CLASS term matches exactly one type of object, and we should avoid
154     // iterating over BUILTINS when CKO_CLASS is neither CKO_CERTIFICATE_BYTES nor
155     // CKO_NSS_TRUST_BYTES.
156     let mut maybe_cert = true;
157     let mut maybe_trust = true;
158     for &(attr, value) in query {
159         if attr == CKA_CLASS {
160             maybe_cert = value.eq(CKO_CERTIFICATE_BYTES);
161             maybe_trust = value.eq(CKO_NSS_TRUST_BYTES);
162             break;
163         }
164     }
166     if !(maybe_cert || maybe_trust) {
167         return results; // The root list or nothing.
168     }
170     for (index, builtin) in BUILTINS.iter().enumerate() {
171         if maybe_cert && match_cert(query, builtin) {
172             results.push(ObjectHandle {
173                 class: ObjectClass::Certificate,
174                 index,
175             });
176         }
177         if maybe_trust && match_trust(query, builtin) {
178             results.push(ObjectHandle {
179                 class: ObjectClass::Trust,
180                 index,
181             });
182         }
183     }
184     results
187 fn search_by_name(name: &[u8], query: &Query) -> SearchResult {
188     let mut results: SearchResult = SearchResult::default();
190     let index = match BUILTINS.binary_search_by_key(&name, |r| r.der_name()) {
191         Ok(index) => index,
192         _ => return results,
193     };
195     // binary search returned a matching index, but maybe not the smallest
196     let mut min = index;
197     while min > 0 && name.eq(BUILTINS[min - 1].der_name()) {
198         min -= 1;
199     }
201     // ... and maybe not the largest.
202     let mut max = index;
203     while max < BUILTINS.len() - 1 && name.eq(BUILTINS[max + 1].der_name()) {
204         max += 1;
205     }
207     for (index, builtin) in BUILTINS.iter().enumerate().take(max + 1).skip(min) {
208         if match_cert(query, builtin) {
209             results.push(ObjectHandle {
210                 class: ObjectClass::Certificate,
211                 index,
212             });
213         }
214         if match_trust(query, builtin) {
215             results.push(ObjectHandle {
216                 class: ObjectClass::Trust,
217                 index,
218             });
219         }
220     }
222     results
225 fn match_root_list(query: &Query) -> bool {
226     for &(typ, x) in query {
227         match get_root_list_attribute(typ) {
228             Some(y) if x.eq(y) => (),
229             _ => return false,
230         }
231     }
232     true
235 fn match_cert(query: &Query, cert: &Root) -> bool {
236     for &(typ, x) in query {
237         match get_cert_attribute(typ, cert) {
238             Some(y) if x.eq(y) => (),
239             _ => return false,
240         }
241     }
242     true
245 fn match_trust(query: &Query, cert: &Root) -> bool {
246     for &(typ, x) in query {
247         match get_trust_attribute(typ, cert) {
248             Some(y) if x.eq(y) => (),
249             _ => return false,
250         }
251     }
252     true
255 #[cfg(test)]
256 mod internal_tests {
257     use crate::certdata::BUILTINS;
258     use crate::internal::*;
259     use pkcs11_bindings::*;
261     // commented out to avoid vendoring x509_parser
262     // fn is_valid_utctime(utctime: &[u8]) -> bool {
263     //     /* TODO: actual validation */
264     //     utctime.len() == 13
265     // }
266     // #[test]
267     // fn test_certdata() {
268     //     for root in BUILTINS {
269     //         // the der_cert field is valid DER
270     //         let parsed_cert = X509Certificate::from_der(root.der_cert);
271     //         assert!(parsed_cert.is_ok());
273     //         // the der_cert field has no trailing data
274     //         let (trailing, parsed_cert) = parsed_cert.unwrap();
275     //         assert!(trailing.is_empty());
277     //         // the der_serial field matches the encoded serial
278     //         assert!(root.der_serial.len() > 2);
279     //         assert!(root.der_serial[0] == 0x02); // der integer
280     //         assert!(root.der_serial[1] <= 20); // no more than 20 bytes long
281     //         assert!(root.der_serial[1] as usize == root.der_serial.len() - 2);
282     //         assert!(parsed_cert.raw_serial().eq(&root.der_serial[2..]));
284     //         // the der_name field matches the encoded subject
285     //         assert!(parsed_cert.subject.as_raw().eq(root.der_name));
287     //         // the der_name field matches the encoded issuer
288     //         assert!(parsed_cert.issuer.as_raw().eq(root.der_name));
290     //         // The server_distrust_after field is None or a valid UTC time
291     //         if let Some(utctime) = root.server_distrust_after {
292     //             assert!(is_valid_utctime(&utctime));
293     //         }
295     //         // The email_distrust_after field is None or a valid UTC time
296     //         if let Some(utctime) = root.email_distrust_after {
297     //             assert!(is_valid_utctime(&utctime));
298     //         }
300     //         assert!(
301     //             root.trust_server == CKT_NSS_MUST_VERIFY_TRUST_BYTES
302     //                 || root.trust_server == CKT_NSS_TRUSTED_DELEGATOR_BYTES
303     //                 || root.trust_server == CKT_NSS_NOT_TRUSTED_BYTES
304     //         );
305     //         assert!(
306     //             root.trust_email == CKT_NSS_MUST_VERIFY_TRUST_BYTES
307     //                 || root.trust_email == CKT_NSS_TRUSTED_DELEGATOR_BYTES
308     //                 || root.trust_email == CKT_NSS_NOT_TRUSTED_BYTES
309     //         );
310     //     }
311     // }
313     #[test]
314     fn test_builtins_sorted() {
315         for i in 0..(BUILTINS.len() - 1) {
316             assert!(BUILTINS[i].der_name.le(BUILTINS[i + 1].der_name));
317         }
318     }
320     #[test]
321     fn test_search() {
322         // search for an element that will not be found
323         let result = search(&[(CKA_TOKEN, &[CK_FALSE])]);
324         assert_eq!(result.len(), 0);
326         // search for root list
327         let result = search(&[(CKA_CLASS, CKO_NSS_BUILTIN_ROOT_LIST_BYTES)]);
328         assert!(result.len() == 1);
330         // search by name
331         let result = search(&[
332             (CKA_CLASS, CKO_CERTIFICATE_BYTES),
333             (CKA_SUBJECT, BUILTINS[0].der_name),
334         ]);
335         assert!(result.len() >= 1);
337         // search by issuer and serial
338         let result = search(&[
339             (CKA_ISSUER, BUILTINS[0].der_name),
340             (CKA_SERIAL_NUMBER, BUILTINS[0].der_serial),
341         ]);
342         assert!(result.len() >= 1);
343     }