nss_certdata_parser/
structured.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5use std::fmt;
6use std::ops::Deref;
7use std::result;
8
9use reader::RawObject;
10use syntax::Value;
11
12#[derive(Debug, Clone)]
13pub enum Object {
14    Trust(Trust),
15    Certificate(Certificate),
16}
17
18// Type alias as documentation.
19pub type Asn1 = Blob;
20
21// This is basically just to have a custom Debug impl that adds an &.
22#[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
23pub struct Blob(Vec<u8>);
24
25impl Deref for Blob {
26    type Target = Vec<u8>;
27    fn deref(&self) -> &Vec<u8> {
28        &self.0
29    }
30}
31
32impl fmt::Debug for Blob {
33    fn fmt(&self, fmt: &mut fmt::Formatter) -> result::Result<(), fmt::Error> {
34        // This ignores the provided "alternate" flag; printing one
35        // `u8` per line for a blob that might be several kB, which is
36        // what `{:#?}` would do here, is not an improvement in human
37        // readability.
38        write!(fmt, "&{:?}", self.0)
39    }
40}
41
42#[derive(Debug, Clone)]
43pub struct Trust {
44    // TODO: factor out these three fields, for list-of-distrusts use cases?
45    pub label: String,
46    pub issuer: Asn1,
47    pub serial: Asn1,
48    pub tls_server_trust: TrustLevel,
49    pub email_trust: TrustLevel,
50    pub code_signing_trust: TrustLevel,
51    // FIXME: should these really be included?  `certdata.txt` seems
52    // to include them only in cases where it already includes the
53    // actual certificate, which doesn't really add any value.
54    // Also, their lengths are known; could be Option<Box<[u8; 16]>> etc.
55    // (But then I'd need an error variant for bad lengths, sigh.)
56    pub md5: Option<Blob>,
57    pub sha1: Option<Blob>,
58}
59
60#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
61pub enum TrustLevel {
62    Distrust,
63    MustVerify,
64    TrustedDelegator,
65}
66
67impl TrustLevel {
68    pub fn from_str(s: &str) -> Option<Self> {
69        match s {
70            "CKT_NSS_NOT_TRUSTED" => Some(TrustLevel::Distrust),
71            "CKT_NSS_MUST_VERIFY_TRUST" => Some(TrustLevel::MustVerify),
72            "CKT_NSS_TRUSTED_DELEGATOR" => Some(TrustLevel::TrustedDelegator),
73            _ => None
74        }
75    }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq)]
79pub enum Usage {
80    TlsServer,
81    Email,
82    CodeSigning,
83}
84
85impl Trust {
86    pub fn trust_level(&self, usage: Usage) -> TrustLevel {
87        match usage {
88            Usage::TlsServer => self.tls_server_trust,
89            Usage::Email => self.email_trust,
90            Usage::CodeSigning => self.code_signing_trust,
91        }
92    }
93}
94
95#[derive(Debug, Clone)]
96pub struct Certificate {
97    pub label: String,
98    pub cert: Asn1,
99    pub issuer: Asn1,
100    pub serial: Asn1,
101    pub subject: Asn1,
102}
103
104#[derive(Debug, Clone, PartialEq, Eq)]
105// TODO: will impl'ing Display make this print more usefully?
106pub struct TypeError {
107    pub got: String,
108    pub expected: &'static str,
109    pub key: &'static str,
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
113// TODO: will impl'ing Display make this print more usefully?
114pub struct ValueError {
115    pub got: String,
116    pub attr_type: &'static str,
117    pub key: &'static str,
118}
119
120quick_error!{
121    #[derive(Debug, Clone, PartialEq, Eq)]
122    pub enum StructureError {
123        MissingKey(key: &'static str) {
124            description("missing key")
125            from()
126        }
127        TypeError(err: TypeError) {
128            description("unexpected attribute type")
129            from()
130        }
131        ValueError(err: ValueError) {
132            description("unexpected attribute value")
133            from()
134        }
135    }
136}
137use self::StructureError::MissingKey;
138
139pub type Result<T> = result::Result<T, StructureError>;
140
141fn take_bin(obj: &mut RawObject, key: &'static str) -> Result<Blob> {
142    match obj.remove(key) {
143        None => Err(MissingKey(key)),
144        Some(Value::Binary(val)) => Ok(Blob(val)),
145        Some(val) => Err(TypeError {
146            got: val.into_type(),
147            expected: "MULTILINE_OCTAL",
148            key: key
149        }.into()),
150    } 
151}
152fn take_str(obj: &mut RawObject, key: &'static str) -> Result<String> {
153    match obj.remove(key) {
154        None => Err(MissingKey(key)),
155        Some(Value::String(val)) => Ok(val),
156        Some(val) => Err(TypeError {
157            got: val.into_type(),
158            expected: "UTF8",
159            key: key
160        }.into()),
161    } 
162}
163fn take_tok<R, F>(obj: &mut RawObject, key: &'static str, exp_ty: &'static str, xlate: F)
164                  -> Result<R>
165    where F: for<'a> FnOnce(&'a str) -> Option<R>
166{
167    let type_error = |got_ty| Err(TypeError {
168        got: got_ty,
169        expected: exp_ty,
170        key: key,
171    }.into());
172    match obj.remove(key) {
173        None => Err(MissingKey(key)),
174        Some(Value::Token(got_ty, val)) => if got_ty == exp_ty {
175            match xlate(&val) {
176                Some(res) => Ok(res),
177                None => Err(ValueError {
178                    got: val,
179                    attr_type: exp_ty,
180                    key: key
181                }.into())
182            }
183        } else {
184            type_error(got_ty)
185        },
186        Some(val) => type_error(val.into_type()),
187    }
188}
189
190fn optionalize<T>(r: Result<T>) -> Result<Option<T>> {
191    match r {
192        Ok(thing) => Ok(Some(thing)),
193        Err(MissingKey(_)) => Ok(None),
194        Err(err) => Err(err)
195    }
196}
197
198impl Certificate {
199    pub fn from_raw(mut obj: RawObject) -> Result<Certificate> {
200        let obj = &mut obj;
201        try!(take_tok(obj, "CKA_CERTIFICATE_TYPE", "CK_CERTIFICATE_TYPE", |cert_type| {
202            if cert_type == "CKC_X_509" { Some(()) } else { None }
203        }));
204        Ok(Certificate {
205            cert: try!(take_bin(obj, "CKA_VALUE")),
206            label: try!(take_str(obj, "CKA_LABEL")),
207            issuer: try!(take_bin(obj, "CKA_ISSUER")),
208            serial: try!(take_bin(obj, "CKA_SERIAL_NUMBER")),
209            subject: try!(take_bin(obj, "CKA_SUBJECT")),
210        })
211    }
212}
213
214fn take_trust_level(obj: &mut RawObject, key: &'static str) -> Result<TrustLevel> {
215    take_tok(obj, key, "CK_TRUST", TrustLevel::from_str)
216}
217
218impl Trust {
219    pub fn from_raw(mut obj: RawObject) -> Result<Trust> {
220        let obj = &mut obj;
221        Ok(Trust {
222            label: try!(take_str(obj, "CKA_LABEL")),
223            issuer: try!(take_bin(obj, "CKA_ISSUER")),
224            serial: try!(take_bin(obj, "CKA_SERIAL_NUMBER")),
225            tls_server_trust: try!(take_trust_level(obj, "CKA_TRUST_SERVER_AUTH")),
226            email_trust: try!(take_trust_level(obj, "CKA_TRUST_EMAIL_PROTECTION")),
227            code_signing_trust: try!(take_trust_level(obj, "CKA_TRUST_CODE_SIGNING")),
228            md5: try!(optionalize(take_bin(obj, "CKA_CERT_MD5_HASH"))),
229            sha1: try!(optionalize(take_bin(obj, "CKA_CERT_SHA1_HASH"))),
230        }) 
231    }
232}
233
234enum ObjClass {
235    Certificate,
236    Trust,
237    Other,
238}
239
240fn take_class(obj: &mut RawObject) -> Result<ObjClass> {
241    take_tok(obj, "CKA_CLASS", "CK_OBJECT_CLASS", |cls| Some(match cls {
242        "CKO_CERTIFICATE" => ObjClass::Certificate,
243        "CKO_NSS_TRUST" => ObjClass::Trust,
244        _ => ObjClass::Other,
245    }))
246}
247
248impl Object {
249    pub fn from_raw(mut obj: RawObject) -> Result<Option<Object>> {
250        match try!(take_class(&mut obj)) {
251            ObjClass::Certificate =>
252                Ok(Some(Object::Certificate(try!(Certificate::from_raw(obj))))),
253            ObjClass::Trust =>
254                Ok(Some(Object::Trust(try!(Trust::from_raw(obj))))),
255            // Ignore CKO_NSS_BUILTIN_ROOT_LIST (and any other unexpected objects?)
256            _ => Ok(None),
257        }
258    }
259}