1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
//! Certificate Revocation Lists for RPKI.
//!
//! Much like for certificates, RPKI reuses X.509 for its certifcate
//! revocation lists (CRLs), limiting the values that are allowed in the
//! various fields.
//!
//! This module implements the CRLs themselves via the type [`Crl`] as well
//! as a [`CrlStore`] that can keep several CRLs which may be helpful during
//! validation.
//!
//! The RPKI CRL profile is defined in RFC 6487 based on the Internet RPIX
//! profile defined in RFC 5280.
//!
//! [`Crl`]: struct.Crl.html
//! [`CrlStore`]: struct.CrlStore.html

use std::collections::HashSet;
use bcder::{decode, encode};
use bcder::{Captured, Mode, OctetString, Oid, Tag, Unsigned};
use crate::oid;
use crate::uri;
use crate::x509::{Name, SignedData, Time, ValidationError};
use crate::crypto::{PublicKey, SignatureAlgorithm};
use crate::cert::ext::{AuthorityKeyIdentifier, CrlNumber};


//------------ Crl -----------------------------------------------------------

/// An RPKI certificate revocation list.
///
/// A value of this type is the result of parsing a CRL file found in the
/// RPKI repository. You can use the `decode` function for parsing a CRL out
/// of such a file.
#[derive(Clone, Debug)]
pub struct Crl {
    /// The outer structure of the CRL.
    signed_data: SignedData,

    /// The algorithm used for signing the certificate.
    signature: SignatureAlgorithm,

    /// The name of the issuer.
    ///
    /// This isn’t really used in RPKI at all.
    issuer: Name,

    /// The time this version of the CRL was created.
    this_update: Time,

    /// The time the next version of the CRL is likely to be created.
    next_update: Option<Time>,

    /// The list of revoked certificates.
    revoked_certs: RevokedCertificates,

    /// The CRL extensions.
    extensions: Extensions,

    /// An optional cache of the serial numbers in the CRL.
    serials: Option<HashSet<Unsigned>>,
}

impl Crl {
    /// Parses a source as a certificate revocation list.
    pub fn decode<S: decode::Source>(source: S) -> Result<Self, S::Err> {
        Mode::Der.decode(source, Self::take_from)
    }

    /// Takes an encoded CRL from the beginning of a constructed value.
    pub fn take_from<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Self, S::Err> {
        cons.take_sequence(Self::from_constructed)
    }

    /// Parses the content of a certificate revocation list.
    pub fn from_constructed<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Self, S::Err> {
        let signed_data = SignedData::from_constructed(cons)?;

        signed_data.data().clone().decode(|cons| {
            cons.take_sequence(|cons| {
                cons.skip_u8_if(1)?; // v2 => 1
                Ok(Crl {
                    signed_data,
                    signature: SignatureAlgorithm::x509_take_from(cons)?,
                    issuer: Name::take_from(cons)?,
                    this_update: Time::take_from(cons)?,
                    next_update: Time::take_opt_from(cons)?,
                    revoked_certs: RevokedCertificates::take_from(cons)?,
                    extensions: cons.take_constructed_if(
                        Tag::CTX_0,
                        Extensions::take_from
                    )?,
                    serials: None,
                })
            })
        }).map_err(Into::into)
    }

    /// Validates the certificate revocation list.
    ///
    /// The list’s signature is validated against the provided public key.
    pub fn validate(
        &self,
        public_key: &PublicKey
    ) -> Result<(), ValidationError> {
        if self.signature != self.signed_data.signature().algorithm() {
            return Err(ValidationError)
        }
        self.signed_data.verify_signature(public_key)
    }

    /// Caches the serial numbers in the CRL.
    ///
    /// Doing this will speed up calls to `contains` later on at the price
    /// of additional memory consumption.
    pub fn cache_serials(&mut self) {
        self.serials = Some(
            self.revoked_certs.iter().map(|entry| entry.user_certificate)
                .collect()
        );
    }

    /// Returns whether the given serial number is on this revocation list.
    pub fn contains(&self, serial: &Unsigned) -> bool {
        match self.serials {
            Some(ref set) => {
                set.contains(serial)
            }
            None => self.revoked_certs.contains(serial)
        }
    }

    /// Returns whether the CRL’s nextUpdate time has passed.
    pub fn is_stale(&self) -> bool {
        self.next_update.map(|t| t < Time::now()).unwrap_or(false)
    }

    pub fn encode<'a>(&'a self) -> impl encode::Values + 'a {
        // This relies on signed_data always being in sync with the other
        // elements!
        self.signed_data.encode()
    }
}



//------------ RevokedCertificates ------------------------------------------

/// The list of revoked certificates.
///
/// A value of this type wraps the bytes of the DER encoded list. You can
/// check whether a certain serial number is part of this list via the
/// `contains` method.
#[derive(Clone, Debug)]
pub struct RevokedCertificates(Captured);

impl RevokedCertificates {
    /// Takes a revoked certificates list from the beginning of a value.
    pub fn take_from<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Self, S::Err> {
        let res = cons.take_opt_sequence(|cons| {
            cons.capture(|cons| {
                while let Some(_) = CrlEntry::take_opt_from(cons)? { }
                Ok(())
            })
        })?;
        Ok(RevokedCertificates(match res {
            Some(res) => res,
            None => Captured::empty(Mode::Der)
        }))
    }

    /// Returns whether the given serial number is contained on this list.
    ///
    /// The method walks over the list, decoding it on the fly and checking
    /// each entry.
    pub fn contains(&self, serial: &Unsigned) -> bool {
        Mode::Der.decode(self.0.as_ref(), |cons| {
            while let Some(entry) = CrlEntry::take_opt_from(cons).unwrap() {
                if entry.user_certificate == *serial {
                    return Ok(true)
                }
            }
            Ok(false)
        }).unwrap()
    }

    /// Returns an iterator over the entries in the list.
    pub fn iter(&self) -> RevokedCertificatesIter {
        RevokedCertificatesIter(self.0.clone())
    }
}


//------------ RevokedCertificatesIter ---------------------------------------

/// An iterator over the entries in the list of revoked certificates.
#[derive(Clone, Debug)]
pub struct RevokedCertificatesIter(Captured);

impl Iterator for RevokedCertificatesIter {
    type Item = CrlEntry;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.decode_partial(|cons| CrlEntry::take_opt_from(cons)).unwrap()
    }
}


//------------ CrlEntry ------------------------------------------------------

/// An entry in the revoked certificates list.
#[derive(Clone, Debug)]
pub struct CrlEntry {
    /// The serial number of the revoked certificate.
    user_certificate: Unsigned,

    /// The time of revocation.
    revocation_date: Time,
}

impl CrlEntry {
    /// Takes a single CRL entry from the beginning of a constructed value.
    pub fn take_from<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Self, S::Err> {
        cons.take_sequence(Self::from_constructed)
    }

    /// Takes an optional CRL entry from the beginning of a contructed value.
    pub fn take_opt_from<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Option<Self>, S::Err> {
        cons.take_opt_sequence(Self::from_constructed)
    }

    /// Parses the content of a CRL entry.
    pub fn from_constructed<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Self, S::Err> {
        Ok(CrlEntry {
            user_certificate: Unsigned::take_from(cons)?,
            revocation_date: Time::take_from(cons)?,
            // crlEntryExtensions are forbidden by RFC 6487.
        })
    }
}


//------------ Extensions ----------------------------------------------------

/// Extensions of a RPKI certificate revocation list.
///
/// Only two extension are allowed to be present: the authority key
/// identifier extension which contains the key identifier of the certificate
/// this CRL is associated with, and the CRL number which is the serial
/// number of this version of the CRL.
#[derive(Clone, Debug)]
pub struct Extensions {
    /// Authority Key Identifier
    ///
    /// May be omitted in CRLs included in protocol messages.
    authority_key_id: Option<AuthorityKeyIdentifier>,

    /// CRL Number
    crl_number: CrlNumber,
}

impl Extensions {
    /// Takes the CRL extension from the beginning of a constructed value.
    pub fn take_from<S: decode::Source>(
        cons: &mut decode::Constructed<S>
    ) -> Result<Self, S::Err> {
        cons.take_sequence(|cons| {
            let mut authority_key_id = None;
            let mut crl_number = None;
            while let Some(()) = cons.take_opt_sequence(|cons| {
                let id = Oid::take_from(cons)?;
                let critical = cons.take_opt_bool()?.unwrap_or(false);
                let value = OctetString::take_from(cons)?;
                Mode::Der.decode(value.to_source(), |cons| {
                    if id == oid::CE_AUTHORITY_KEY_IDENTIFIER {
                        AuthorityKeyIdentifier::take(
                            cons, critical, &mut authority_key_id
                        )
                    }
                    else if id == oid::CE_CRL_NUMBER {
                        CrlNumber::take(cons, critical, &mut crl_number)
                    }
                    else {
                        // RFC 6487 says that no other extensions are
                        // allowed. So we fail even if there is only
                        // non-critical extension.
                        xerr!(Err(decode::Malformed))
                    }
                }).map_err(Into::into)
            })? { }
            let crl_number = match crl_number {
                Some(some) => some,
                None => return Err(decode::Malformed.into())
            };
            Ok(Extensions {
                authority_key_id,
                crl_number
            })
        })
    }
}


//------------ CrlStore ------------------------------------------------------

/// A place to cache CRLs for reuse.
///
/// This type allows to store CRLs you have seen in case you may need them
/// again soon. This is useful when validating the objects issued by a CA as
/// they likely all refer to the same CRL, so keeping it around makes sense.
#[derive(Clone, Debug)]
pub struct CrlStore {
    /// The CRLs in the store.
    ///
    /// This is a simple vector because most likely we’ll only ever have one.
    crls: Vec<(uri::Rsync, Crl)>,

    /// Should we cache the serials in our CRLs?
    cache_serials: bool,
}

impl CrlStore {
    /// Creates a new CRL store.
    pub fn new() -> Self {
        CrlStore {
            crls: Vec::new(),
            cache_serials: false,
        }
    }

    /// Enables caching of serial numbers in the stored CRLs.
    pub fn enable_serial_caching(&mut self) {
        self.cache_serials = true
    }

    /// Adds an entry to the CRL store.
    ///
    /// The CRL is keyed by its rsync `uri`.
    pub fn push(&mut self, uri: uri::Rsync, mut crl: Crl) {
        if self.cache_serials {
            crl.cache_serials()
        }
        self.crls.push((uri, crl))
    }

    /// Returns a reference to a CRL if it is available in the store.
    pub fn get(&self, uri: &uri::Rsync) -> Option<&Crl> {
        for &(ref stored_uri, ref crl) in &self.crls {
            if *stored_uri == *uri {
                return Some(crl)
            }
        }
        None
    }
}

impl Default for CrlStore {
    fn default() -> Self {
        Self::new()
    }
}