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