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
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 {
33 #[derive(Clone, Copy)]
34 pub struct ObjectHandle {
39 impl TryFrom<CK_OBJECT_HANDLE> for ObjectHandle {
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,
50 Ok(ObjectHandle { class, index })
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),
66 pub fn get_attribute(attribute: CK_ATTRIBUTE_TYPE, object: &ObjectHandle) -> Option<&'static [u8]> {
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]),
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]> {
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[..]),
88 fn get_cert_attribute(attribute: CK_ATTRIBUTE_TYPE, cert: &Root) -> Option<&[u8]> {
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,
108 fn get_trust_attribute(attribute: CK_ATTRIBUTE_TYPE, cert: &Root) -> Option<&[u8]> {
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),
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
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);
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,
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);
166 if !(maybe_cert || maybe_trust) {
167 return results; // The root list or nothing.
170 for (index, builtin) in BUILTINS.iter().enumerate() {
171 if maybe_cert && match_cert(query, builtin) {
172 results.push(ObjectHandle {
173 class: ObjectClass::Certificate,
177 if maybe_trust && match_trust(query, builtin) {
178 results.push(ObjectHandle {
179 class: ObjectClass::Trust,
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()) {
195 // binary search returned a matching index, but maybe not the smallest
197 while min > 0 && name.eq(BUILTINS[min - 1].der_name()) {
201 // ... and maybe not the largest.
203 while max < BUILTINS.len() - 1 && name.eq(BUILTINS[max + 1].der_name()) {
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,
214 if match_trust(query, builtin) {
215 results.push(ObjectHandle {
216 class: ObjectClass::Trust,
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) => (),
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) => (),
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) => (),
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
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));
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));
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
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
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));
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);
331 let result = search(&[
332 (CKA_CLASS, CKO_CERTIFICATE_BYTES),
333 (CKA_SUBJECT, BUILTINS[0].der_name),
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),
342 assert!(result.len() >= 1);