GithubHelp home page GithubHelp logo

x509-parser's Introduction

Github CI

Rusticata

Overview

Rusticata is a test crate for network protocol parsers written in Rust.

It was written to show to feasibility of the implementation of safe and efficient parsers in suricata. The real parsing code is now part of suricata (starting from version 4.0), and must be configured using the --enable-rust flag.

This project is now a playground for testing parsers, features and code.

This project is based on:

Build

Run cargo build for a build in debug mode, cargo build --release for release mode.

Use cargo install to install the library, or set the LD_LIBRARY_PATH environment variable.

Testing

rusticata is mostly used to decode application layers in the pcap-analyzer project. See its documentation for examples.

License

This library is licensed under the GNU Lesser General Public License version 2.1, or (at your option) any later version.

x509-parser's People

Contributors

aggstam avatar biagiofesta avatar bkstein avatar chifflier avatar cpu avatar dependabot[bot] avatar djc avatar droundy avatar duskmoon314 avatar flavio avatar fzgregor avatar fzpp avatar g2p avatar hbina avatar jannschu avatar jbtrystram avatar jeamland avatar jeff-hiner avatar jgalenson avatar kpcyrd avatar kpp avatar nhynes avatar nicholasbishop avatar nthuemmel-scontain avatar rappet avatar reubenmiller avatar sdroege avatar sergiobenitez avatar wayofthepie avatar xonatius avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

x509-parser's Issues

new release plz

I'd like to use the feature of extracting tbsCertificate bytes implemented in #19 to verify certificates. So, please release a new version.

easier way to parse/read a PEM file?

I'm just looking at this crate, and it looks like it will do what I want, but it also looks somewhat awkward to use for a PEM file. My goal is just do decide if (and when) I need to renew a certificate through lets-encrypt, so I don't need much in the way of parsing.

Your example for pem parsing looks pretty doable, but I wonder if would be a problem to simply have a parse_x509_pem? I'd also prefer an interface returning a Result rather than an IResult, maybe even a read_x509_pem which reads a file and parses it.

If I wrote such a function, would you accept a pull request? It seems silly to put it in my crate, where no one else will use it.

Parser refuses certificates with empty subject

Found in wayofthepie/ctlogs-cli#5

When using a certificate with an empty subject, parser fails with the following error:

Could not decode DER data: Error(Der(NomError(Many1)))

Per RFC5280, it is valid to have an empty subject in some situations:

If subject
   naming information is present only in the subjectAltName extension
   (e.g., a key bound only to an email address or URI), then the subject
   name MUST be an empty sequence and the subjectAltName extension MUST
   be critical.

Can it parse Version1 x509 certs.

First of all, thanks for the amazing work.

I had one query, can it parse version 1 x509 certificates?

If not and if I want to add that support where should I start looking at in the code-base?

Can't decode PKCS#12 certificates

x509_parser::parse_x509_certificate() fails with Der(InvalidTag) when called with the contents of a .p12 file. I'm still just learning about the low-level details of certificates, but my understanding is PKCS#12 is DER-encoded, so this was surprising. My assumption right now is that this is not expecting a PKCS#12 container but just the certificate itself (and I have a suspicion that it's primarily just used to decode the data from within a PEM), and of course that it doesn't handle any sort of encryption.

First off, I would love for this to be noted in the documentation. I've been told many times that a .p12 is a DER-encoded file containing a certificate, so it's only natural to look at x509_parser::parse_x509_certificate(), see that it parses a DER-encoded certificate, and believe that it applies here.

Secondly, is there any reasonable solution here? Could x509-parser learn to handle PKCS#12 files (including the password), or is there some recommended alternative approach for handling these and extracting the certificate data from them? I would really prefer not to pull in openssl just to decode a PKCS#12 archive into a certificate, then encode that certificate into DER, just so x509-parser can then decode it again.

Missing Subject DN oid 0.9.2342.19200300.100.1.1 (uid)

The Subject field is a Distinguished Name. oid-registry defines the OIDs for expected attributes, and this crate defines additional abbreviations for use when printing them in an X509Name. Unfortunately, both this and oid-registry are missing 0.9.2342.19200300.100.1.1 (oid-info.com), which is "uid" (or, as an abbreviation for printing, "UID").

I'm filing it here as the actual issue is printing X509Names, though it should also be added to oid-registry for good measure.

Where are all the OIDs?

Version 0.9 used to contain a lot of predefined OIDs in x509_parser::objects. Now these are gone. Were they moved to some other crate or do I have to build them myself using the der_parser::oid macro?

X500UniqueIdentifier in Subject

Hi, thanks for the awesome crate! I've come across a bunch of un-parseable certificates, details here. Most of those are invalid certificates, however there are a few which I am not sure about.

The problem seems to be that they have an x500UniqueIdentifier attribute in the Subject:

Subject: x500UniqueIdentifier = #030D007276456F4263722B7855413D, C = SI, O = I D.O.O. LJUBLJANA, OU = server certificates, CN = data.idoo.si, SN = idoo.si, GN = data, L = Ljubljana, ST = SI

This is a bitstring, but parse_directory_string does not allow for that:

fn parse_directory_string(i: &[u8]) -> DerResult {
    alt!(
        i,
        complete!(parse_der_utf8string)
            | complete!(parse_der_printablestring)
            | complete!(parse_der_ia5string)
            | complete!(parse_der_t61string)
            | complete!(parse_der_bmpstring)
    )
}

Example cert is:

MIIEyjCCA7KgAwIBAgIDCwWAMA0GCSqGSIb3DQEBDQUAMEIxCzAJBgNVBAYTAlNJMQ8wDQYDVQQKEwZIYWxjb20xIjAgBgNVBAMTGUhhbGNvbSBTZWN1cmUgU2VydmVyIENBIDEwHhcNMTQxMjA0MTEzMzQ4WhcNMTkxMjA0MTEzMzQ4WjCBuTEWMBQGA1UELQMNAHJ2RW9CY3IreFVBPTELMAkGA1UEBhMCU0kxGzAZBgNVBAoTEkkgRC5PLk8uIExKVUJMSkFOQTEcMBoGA1UECxMTc2VydmVyIGNlcnRpZmljYXRlczEVMBMGA1UEAxMMZGF0YS5pZG9vLnNpMRAwDgYDVQQEEwdpZG9vLnNpMQ0wCwYDVQQqEwRkYXRhMRIwEAYDVQQHEwlManVibGphbmExCzAJBgNVBAgTAlNJMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA1OBFH0sdb/dVUr4ZRx6abCapHA6wgpOICf/yuGQ7PJJMpVv7ESRF/YHv5OPb6dNhtWuPk8jUxVNobpteR7R6VYNnd8n7gkaxNSt4JoPzkI7ycufbnqMG34ArKY/ZS+61wL7LhQKuPhI70nS/YazT8vyvCdHQRNvHjXRKN2+N0RFuTaAK5rQGU+9gB580m5oUOOSoJGoqE6HQ2D+gSToUQRskUING89BzfrvvWUXYQwuNTBVFo00vYTfOh0K8EZXfBHmbXvu7JMhOKy4Ahob1Cd+OJC4VrcVQIWRTJPCxOyfeyfoBldWQHIR7Cu7n4Dwpa6+/6jXXmobktd+V+I1p6QIDAQABo4IBTzCCAUswHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBMGA1UdIwQMMAqACEx6dYbVSY2EMBcGA1UdIAQQMA4wDAYKKwYBBAGuMwUCAjB6BgNVHR8EczBxMG+gbaBrhmlsZGFwOi8vbGRhcC5oYWxjb20uc2kvY249SGFsY29tJTIwU2VjdXJlJTIwU2VydmVyJTIwQ0ElMjAxLG89SGFsY29tLGM9U0k/Y2VydGlmaWNhdGVyZXZvY2F0aW9ubGlzdDtiaW5hcnkwEQYDVR0OBAoECEC8ikKu0E4LMA4GA1UdDwEB/wQEAwIE8DBEBgkqhkiG9w0BCQ8ENzA1MA4GCCqGSIb3DQMCAgIAgDAOBggqhkiG9w0DBAICAIAwBwYFKw4DAgcwCgYIKoZIhvcNAwcwFwYDVR0RBBAwDoIMZGF0YS5pZG9vLnNpMA0GCSqGSIb3DQEBDQUAA4IBAQAwhKMGwLSsmw7CWDFC/4KSNOtCj8FMBIbdP10m/yLY4vZF7hUW6xxdQPBHg4rdhXwZfSgontSgcI0P4O/sQw5RCPYh6LSSNCpEyjS3bDK8DX0+6w0WsMv3o5Axcyg+iKK0B2ZA7++Ct6a4m9FU6aG5IVt1it6WQ5esKoDTkw6uB7qKGDo3a6c+8dz6Kz1unqNBQi8NDnZB9p5PJDN8Q7WcLhngv9qSrs4odW3hsZlEDVeqddy1I/p/MtQOKU6+xtZEmvbJSU7XAz1RrTv7xU3vWVJD2dW+YwRSb9ZZYEEzbup6jEfROpZQ4/P86CrzCTQZRIk0OeqzTsqvit2nCqrgAAA=

Re-reading the Issuer Section in RFC 5280 it mentions:

As noted above, distinguished names are composed of attributes. This
specification does not restrict the set of attribute types that may
appear in names. However, conforming implementations MUST be
prepared to receive certificates with issuer names containing the set
of attribute types defined below. This specification RECOMMENDS
support for additional attribute types.

There is some discussion about the validity of this in the python cryptography package, pyca/cryptography#3197. Just wondering what your thoughts are on this? It does seem like it should be supported. Thanks!

RSA-PSS signature params are parsed into a mess

Here is a x509 pem cert.txt signed with RSA_PSS_SHA384.

openssl x509 -text -in cert.txt -noout gives clear info of the signature algorithm:

        Signature Algorithm: rsassaPss         
         Hash Algorithm: sha384
         Mask Algorithm: mgf1 with sha384
          Salt Length: 0x30
         Trailer Field: 0xBC (default)

Unfortunately with your cratecertificate.signature_algorithm.parameters are parsed into a mess:

params = BerObject {
    header: BerObjectHeader {
        class: Universal,
        structured: 1,
        tag: Sequence,
        len: Definite(
            52,
        ),
        raw_tag: Some(
            [
                48,
            ],
        ),
    },
    content: Sequence(
        [
            BerObject {
                header: BerObjectHeader {
                    class: ContextSpecific,
                    structured: 1,
                    tag: EndOfContent,
                    len: Definite(
                        15,
                    ),
                    raw_tag: Some(
                        [
                            160,
                        ],
                    ),
                },
                content: Unknown(
                    EndOfContent,
                    [
                        48,
                        13,
                        6,
                        9,
                        96,
                        134,
                        72,
                        1,
                        101,
                        3,
                        4,
                        2,
                        2,
                        5,
                        0,
                    ],
                ),
            },
            BerObject {
                header: BerObjectHeader {
                    class: ContextSpecific,
                    structured: 1,
                    tag: Boolean,
                    len: Definite(
                        28,
                    ),
                    raw_tag: Some(
                        [
                            161,
                        ],
                    ),
                },
                content: Unknown(
                    Boolean,
                    [
                        48,
                        26,
                        6,
                        9,
                        42,
                        134,
                        72,
                        134,
                        247,
                        13,
                        1,
                        1,
                        8,
                        48,
                        13,
                        6,
                        9,
                        96,
                        134,
                        72,
                        1,
                        101,
                        3,
                        4,
                        2,
                        2,
                        5,
                        0,
                    ],
                ),
            },
            BerObject {
                header: BerObjectHeader {
                    class: ContextSpecific,
                    structured: 1,
                    tag: Integer,
                    len: Definite(
                        3,
                    ),
                    raw_tag: Some(
                        [
                            162,
                        ],
                    ),
                },
                content: Unknown(
                    Integer,
                    [
                        2,
                        1,
                        48,
                    ],
                ),
            },
        ],
    ),
}

Would you please fix it?

Should X509Name implement Eq according to 7.1?

Hi,

section 7.1 of RFC 5280 specifies how distinguished names must be checked for equality.

Should the Eq implementation reflect that? Currently PartialEq is derived automatically. Since comparing DNs is a common task, I think this would be helpful.

I will check how much work it is to implement it.

Switch storage to `Cow<'a, X>` and implement `ToOwned`?

I accidentally [1] implemented my own x509 parser as part of implementing RFC 5652 for https://crates.io/crates/cryptographic-message-syntax. When I realized the horrors I had committed, I wanted to switch to using this crate. However, I couldn't coerce it into working easily because data types in this crate have lifetimes and always hold references. e.g. &'a [u8]. There's no option to obtain an owned version of the data structures. And since Rust doesn't allow you to have self-referential data structures [easily], I couldn't get this crate to work given some of my constraints that need 'static / owned lifetimes.

Are the maintainers of this crate receptive to the idea of mass changing all references in structs to Cow<'a, X> and implementing ToOwned so all data types can be owned, cloned, and not subjected to lifetime constraints? If you are, perhaps I'll find time to do the work so I can delete the one-off x509 crate (https://crates.io/crates/x509-certificate) I begrudgingly created.

[1] This was accidental because when I was following the rabbit hole of recursively defining ASN.1 types for RFC 5652, I didn't realize I was reinventing x509 certificate parsing because this was the first time I was exposed to the internals of x509 certificates. Only after I had implemented a full RFC 5652 ASN.1 decoder did I realize many of the types were foundational and general crypto primitives. Oops!

Error parsing standard extension causes whole certificate to fail to decode

Reading through the extensions code right now, it appears that if any of the standard supported extensions fail to parse out their parsed_extension form, it bubbles the error all the way up and causes the whole certificate to fail to decode. This is extremely unfortunate, both because it's completely unnecessary, and because this means explicitly unsupported GeneralNames (x400Address and ediPartyName) will cause this even if the data is otherwise well-formed.

Instead of bubbling the error up, it should catch it and store it in place of the ParsedExtension, such that X509Extension::parsed_extension() can return a Result. This way not only will parse errors (including unsupported items) not cause the whole certificate to fail to decode, but also I can tell where the error is, and it won't even affect me if I don't need that extension.

0.8.0-beta4 fails with duplicate Authority Information Access

I started using 0.8.0-beta4 but certificates which previously parsed are now failing. It seems this is because of duplicate entries in the Authority Information Access extension. Here is an example:

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

The Authority Information Access for that cert is:

Authority Information Access:
    OCSP - URI:http://ocsp.pca.dfn.de/OCSP-Server/OCSP
    CA Issuers - URI:http://cdp1.pca.dfn.de/dfn-ca-global-g2/pub/cacert/cacert.crt
    CA Issuers - URI:http://cdp2.pca.dfn.de/dfn-ca-global-g2/pub/cacert/cacert.crt

It seems the error is coming from here:

if accessdescs.insert(oid, gn).is_some() {
// duplicate information is not allowed
return Err(Err::Failure(BerError::InvalidTag));
}

This looks like a bug, the end of Section 4.2.2.1 of RFC 5280 says:

An authorityInfoAccess extension may include multiple instances of
the id-ad-caIssuers accessMethod.

Thanks!

GeneralName returns parse error for x400Address and ediPartyName

GeneralName declares it doesn't support x400Address and ediPartyName, and as a result it returns an error on parsing. This is annoying. I would much prefer it declare variants for these two and simply have them carry an unparsed &[u8] payload instead of returning an error.

To make this more clear, it could even look something like

enum GeneralName<'a> {X400Address(UnparsedGeneralNameValue<'a>)
    EDIPartyName(UnparsedGeneralNameValue<'a>)
}

struct UnparsedGeneralNameValue<'a>(&'a [u8])

Or it could just have a case like Unsupported { tag: DerTag, value: &'a [u8] }, to avoid a parse error without attempting to define anything about it (though I'm not a fan of this approach, the set of choices is fixed so we may as well just define x400Address and ediPartyName even if we won't parse their values).

Note that even if #83 is fixed such that parsing an extension doesn't fail the whole parser, a single unsupported GeneralName will still cause the whole extension to fail to parse. And in general, a well-formed certificate should never produce a parse error, this crate should instead restrict unsupported types as narrowly as possible so I can read all of the supported data.

Why does X509CertificationRequest::verify_signature take a parameter

In reviewing rustls/rcgen#41 I saw that the verify_signature function on CSRs taking a parameter, with None being passed in the PR. Fearing that the API witholding some feature that upstream provides, I checked the function definition. Apparently it's only to pass an optional public key to use for verification. For certificates, such a param makes sense, as they might be signed by other entities, but CSRs are always self-signed, no? What about removing the parameter from the API before a crates.io release?

cc @djc

Accessing the CN of a certificate

hello!

There are some changes in the latest release that are related to the common name of the certificate, is there an intended way to access the CN, that's less low-level than the current approach?

Thanks!

Best way to parse X509 certificates chain?

From what I can tell, the parser only parses the last certificate block in the chain (PEM).

In my use case I would like to be able to access each certificate of the sequence.
(In order to verify that the root certificate has a known fingerprint, and that each subsequent certificate signature can be verified with the public key of its ancestor).

Is it already possible?
Would you be interested in expanding your API for that? With some guidance, I would be happy to contribute it ;)

Thanks for your time & feedback!

Consider keeping struct definitions and parsers together

Right now it seems most type definitions live in the x509 module whereas parsers live in x509_parser. It would make the code easier to follow and modify if parsers lived closer to the relevant type definitions, for example in an impl block (as in fn from_bytes(i: &[u8]) -> X509Result<Self> {}, for example).

easy way to get the time left?

I'm just looking at this crate, and it looks like it will do what I want, but it also looks somewhat awkward to use for a PEM file. My goal is just do decide if (and when) I need to renew a certificate through lets-encrypt, so I don't need much in the way of parsing.

I'm thinking a method on X509Certificate or TbsCertificate or Validity that looks like

  fn time_to_expiration(&self) -> Option<std::time::Duration> { ... }

would be very convenient to me and presumably to others. I'd be happy to submit such a patch if you can tell me where you'd like the method to be. My preference would be at the very top level, but I could imagine putting it in all three or just in Validity.

Missing some standard certificate extensions

This crate parses most standard certificate extensions defined in RFC 5280, but it seems to be missing the following:

Certificate Extensions:

It's also missing some CRL and CRL Entry extensions:

I don't know how much the CRL ones matter, though this crate does support 3 such CRL/CRL Entry extensions already (plus the ones that are identical to certificate extensions).

Ready for experimental use?

Just wanted to check if you have plans to publish this to crates.io anytime soon? I'd like to make use of it but I understand it is still an early experiment. I am able to compile it from the github source and parse an X509 certificate, but the fields of the parsed struct are not public so I can't access them.

BasicConstraints can fail to parse

The following cert (issued by the LetsEncrypt staging CA) causes parse_basicconstraints to fail with nom Err::Incomplete (needs one more byte) when a complete certificate is passed to it. This also cascades into not parsing the rest of the extensions (see #32).

gunzip and test with

cargo run --example print-cert staging-eecert.der

x509_parser::extensions::PolicyMappings confusing, discards order

The x509_parser::extensions::PolicyMappings type is rather confusing as it represents a sequence of tuples (issuerDomainPolicy, subjectDomainPolicy), but it's implemented as a HashMap<Oid<'a>, Vec<Oid<'a>>> with no documentation.

Reading the source tells me that the keys are the issuer domain policies, and the values are all of the subject domain policies that occur with the issuer domain policy, i.e. an input sequence [(a, b), (a, c)] produces the output {a: [b, c]}. But this is completely undocumented, and it also discards ordering (which matters for deterministic output when printing certificate details).

At a bare minimum, this needs documentation. But really, this should just be represented as something like Vec((Oid<'a>, Oid<'a>)). I can convert this into a HashMap myself if I need to.

BTW this is the second such ticket about a sequence being converted to a HashMap and discarding order. I'm not sure offhand if any other such conversions exist, but if so, I would really prefer to just get ordered sequences across the board, so I can print certificate details in a deterministic and predictable manner (and so I can generate new modified certificates while minimizing unnecessary changes such as ordering).

Infinite loop when trying to parse non-pem file as pem certificate

Hi,

I'm using your library to parse PEM certificates and check the expiration date and it works great!

However I had to revert to version 0.5.2 because 0.6.2 can enter an infinite loop if you attempt to parse pem data from a non-pem file.

Info that may be important: I'm running on Linux x64 (Ubuntu based).

To recreate the issue, you could just make a new binary project with cargo new, add the dep to Cargo.toml:

[dependencies]
x509-parser = "0.6.2"

Create a text file at the root of the project and call it "some_file.txt", add some text in it.

Now use that code for main.rs:

extern crate x509_parser;
use std::io::Cursor;
use std::io::Read;
use std::fs::File;
use x509_parser::pem::Pem;

fn main() {
  let mut f = File::open("some_file.txt").unwrap();
  let mut bytes = Vec::new();
  f.read_to_end(&mut bytes).unwrap();
  let reader = Cursor::new(bytes);
  let (pem, _bytes_read) = Pem::read(reader).unwrap();
  let x509 = pem.parse_x509().unwrap();
  // Do something with x509...
  // ..
}

Try running the program. On my machine it shoots to 100% CPU immediately.

If I specify version 0.5.1 the issue is gone.

Hope this helps and thanks for the code,
Dk

X509v3 extension ordering discarded

This crate parses the X509v3 extensions as a sequence, then converts them to a HashMap which throws away the extension order.

If I'm trying to look up extensions by name, exposing them as a HashMap makes a lot of sense. But in my case I care about ordering, because I'm trying to print certificate information and I want it to be deterministic and to match other tooling. This also means if I'm ingesting a certificate and using it to build a new modified version, the modified version will reorder the extensions.

Switch verify feature from ring to openssl?

Followup of #69

Ring is a nice library, but it is not sure it is the best for our purpose: x509-parser needs to be able to verify all kind of cryptographic signatures (even old/deprecated/not ideal but correct algorithms). ring aims at providing a secure by default API, but our criteria is compatibility with previous implementations, so openssl may be a better choice.

Panic on overflow in addition

Found using cargo-fuzz.

extern crate x509_parser;

fn main() {
    let data = b"0\x88\xff\xff\xff\xff\xff\xff\xff\xff00\x0f\x02\x000\x00\x00\x00\x00\x00\x0000\x0f\x00\xff\x0a\xbb\xff";
    let _ = x509_parser::x509_parser(data);
}
thread '<unnamed>' panicked at 'attempt to add with overflow', <do_parse macros>:33
stack backtrace:
   0:     0x55f4b01b12f3 - std::sys::imp::backtrace::tracing::imp::unwind_backtrace::hf9ed9ccfd9f14c2b
                               at /checkout/src/libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
   1:     0x55f4b01adc44 - std::sys_common::backtrace::_print::hd8a1b72dcf3955ef
                               at /checkout/src/libstd/sys_common/backtrace.rs:71
   2:     0x55f4b01b22c7 - std::panicking::default_hook::{{closure}}::h5ff605bba7612658
                               at /checkout/src/libstd/sys_common/backtrace.rs:60
                               at /checkout/src/libstd/panicking.rs:355
   3:     0x55f4b01b1e4b - std::panicking::default_hook::h9bc4f6dfee57d6bd
                               at /checkout/src/libstd/panicking.rs:371
   4:     0x55f4b01b272b - std::panicking::rust_panic_with_hook::hdc01585dc2bf7122
                               at /checkout/src/libstd/panicking.rs:549
   5:     0x55f4b01b2604 - std::panicking::begin_panic::hf84f4975d9f9b642
                               at /checkout/src/libstd/panicking.rs:511
   6:     0x55f4b01b2539 - std::panicking::begin_panic_fmt::hcc3f360b2ba80419
                               at /checkout/src/libstd/panicking.rs:495
   7:     0x55f4b01b24c7 - rust_begin_unwind
                               at /checkout/src/libstd/panicking.rs:471
   8:     0x55f4b01b9acd - core::panicking::panic_fmt::h795d9a9608ddc2bb
                               at /checkout/src/libcore/panicking.rs:69
   9:     0x55f4b01b9a04 - core::panicking::panic::hcab3e0dfa81beee9
                               at /checkout/src/libcore/panicking.rs:49
  10:     0x55f4b0122c04 - x509_parser::x509::x509_parser::ha5319985231d7696
                               at /home/neo/dev/work/x509-parser/src/x509.rs:142
  11:     0x55f4aff8a3d5 - rust_fuzzer_test_input
                               at /home/neo/dev/work/x509-parser/fuzz/fuzzers/fuzzer_script_1.rs:7
  12:     0x55f4aff8e00a - libfuzzer_sys::test_input_wrap::{{closure}}::h01afe675cf6a0c88
                               at /home/neo/.cargo/git/checkouts/libfuzzer-sys-e07fde05820d7bc6/36a3928/src/lib.rs:13
  13:     0x55f4aff8c0cf - std::panicking::try::do_call::hfeac5113da58e53b
                               at /checkout/src/libstd/panicking.rs:454
  14:     0x55f4b01b841b - <unknown>
                               at /checkout/src/libpanic_abort/lib.rs:40
==24442== ERROR: libFuzzer: deadly signal
    #0 0x55f4b0092cb9 in __sanitizer_print_stack_trace /checkout/src/compiler-rt/lib/asan/asan_stack.cc:38
    #1 0x55f4aff9f401 in fuzzer::Fuzzer::CrashCallback() /home/neo/.cargo/git/checkouts/libfuzzer-sys-e07fde05820d7bc6/36a3928/llvm/lib/Fuzzer/FuzzerLoop.cpp:280
    #2 0x55f4aff9f34b in fuzzer::Fuzzer::StaticCrashSignalCallback() /home/neo/.cargo/git/checkouts/libfuzzer-sys-e07fde05820d7bc6/36a3928/llvm/lib/Fuzzer/FuzzerLoop.cpp:264
    #3 0x55f4affbcb3d in fuzzer::CrashHandler(int, siginfo_t*, void*) /home/neo/.cargo/git/checkouts/libfuzzer-sys-e07fde05820d7bc6/36a3928/llvm/lib/Fuzzer/FuzzerUtilPosix.cpp:37
    #4 0x7f8218a69fdf  (/usr/lib/libpthread.so.0+0x11fdf)
    #5 0x7f82184cba0f in __GI_raise (/usr/lib/libc.so.6+0x33a0f)
    #6 0x7f82184cd139 in __GI_abort (/usr/lib/libc.so.6+0x35139)
    #7 0x55f4b01b8428 in panic_abort::__rust_start_panic::abort /checkout/src/libpanic_abort/lib.rs:61
    #8 0x55f4b01b8428 in __rust_start_panic /checkout/src/libpanic_abort/lib.rs:56

NOTE: libFuzzer has rudimentary signal handlers.
      Combine libFuzzer with AddressSanitizer or similar for better crash reports.
SUMMARY: libFuzzer: deadly signal
MS: 4 ChangeBinInt-CopyPart-CrossOver-CMP- DE: "\xff\xff\xff\xff\xff\xff\xff\xff"-; base unit: 0c49320faa5c47824170ed0eb79fe6b7367bd96f
0x30,0x88,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x30,0x30,0xf,0x2,0x0,0x30,0x0,0x0,0x0,0x0,0x0,0x0,0x30,0x30,0xf,0x0,0xff,0xa,0xbb,0xff,
0\x88\xff\xff\xff\xff\xff\xff\xff\xff00\x0f\x02\x000\x00\x00\x00\x00\x00\x0000\x0f\x00\xff\x0a\xbb\xff
artifact_prefix='artifacts/'; Test unit written to artifacts/crash-867582c6bd5fa9304fe4213e5cb48765aca88a12
Base64: MIj//////////zAwDwIAMAAAAAAAADAwDwD/Crv/

Expose parsing functions, or provide more types that include them

I'm trying to add parsing of more extensions myself. Right now I'm adding Subject Directory Attributes since this crate seems to have just forgotten about that entirely, though I need to add custom extensions too. Subject Directory Attributes is defined as

SubjectDirectoryAttributes ::= SEQUENCE SIZE (1..MAX) OF Attribute

Attribute               ::= SEQUENCE {
      type             AttributeType,
      values    SET OF AttributeValue }
            -- at least one value is required

This crate exposes AttributeTypeAndValue, which parses a sequence of a single type and value, but it doesn't have Attribute, and it doesn't expose its internal parse_attribute_value() (or the interesting part of that, which is the fallback alternative parser parse_malformed_string()).

This is annoying as I don't want to have to replicate things like parse_malformed_string(), but I want to match the behavior of this crate's existing parsers.

Ideally, every type definition in Appendix A of RFC 5280 would have some publicly-exposed parser in this crate, so I could use it to parse extensions that this crate doesn't itself support.

Naming confusion: pem_to_der returns a Pem

The lines in question: https://docs.rs/x509-parser/0.8.2/src/x509_parser/pem.rs.html#70-77

Hi, thanks for writing this package. I am not very familiar with x509 and I had some confusion trying to figure out the API. As far as I understand a PEM and a DER are mutually exclusive encodings, and I was confused that a function called x_to_y actually returns something of type x. I guess they're different at the byte level but share the same trait impl at the code level, but this is quite confusing to think about. I'm not familiar enough with this domain to suggest a better name, but I wanted to throw this out for future consideration.

Can't parse pem certificate if the section "Bag Attributes" is provided.

The function pem_to_der can't parse pem file if the section "Bag attributes" is provided.

The pem certificate can look like this:

Bag Attributes
localKeyID: B9 FD 3E 10 61 18 CD D3 27 A3 8D F4 97 C6 2D 1C 4A 7C A0 6C
subject=/C=AU/ST=Some-State/O=/CN=
issuer=/C=AU/ST=Some-State/O=/CN=
-----BEGIN CERTIFICATE-----
<base_64>
-----END CERTIFICATE-----

Parsing of apparently valid certificate fails

Hi there,

I know this might be a duplicate of the other isses raised here, but I'm no expert at x509 so I'll have to let someone else judge:

when calling parse_x509_der(), the parser panicks:
thread 'main' panicked at 'no x509 data: Error(Der(NomError(MapRes)))', src\libcore\result.rs:1165:5

const cert: &str = "\
MIICnTCCAYUCBgFurVvnkjANBgkqhkiG9w0BAQsFADASMRAwDgYDVQQDDAdtYXJxdWVlMB4XDTE5
MTEyNzE0NTMzMVoXDTI5MTEyNzE0NTUxMVowEjEQMA4GA1UEAwwHbWFycXVlZTCCASIwDQYJKoZI
hvcNAQEBBQADggEPADCCAQoCggEBAIThIxtIo0SyaT9jVm+lAFvkMjmHg+YfD7/AjblETSOt3jcX
RufXZb6l5wX+p4qDUTP0GkB3JmXvTzTgMwCsnLrSGdjucxMKC6VPWWJtXFXHzVbrLSN6tsKrAhCb
yKzCoZfC/ld+wRYa3gshPVfBlhJolIihe8H12UEEhXk+5UYEwWnfma1E6Rzp+6RFbkv8XSR5gnX3
PTTCygy16AYGWYA744JE6VucwsDDOgJF5fRNUzaSpmfGXFNsHBXWT7eK6O02BuLLRZsXy4ijn0Tb
QpjsC/6uTaNNlUBiWgSCdt+upvhnOj5k5i66yvNArpkmvaGgtfJRhjLXkgJ68JN4eKMCAwEAATAN
BgkqhkiG9w0BAQsFAAOCAQEAMgh0pFD1Yk1GPKUO3fQZsK2ErpJRaHXLtQLmCOrVaAZbu4WfqWMX
oRyf0xyTtJGXLEaWM0UpNvwwtKCeZeMXT4C7nwDNilLkZUWVZsfs3Fcfo5GoboA78K0AAMPb94Ck
r1jXoKD+JoKYLkO08w/32kFQVri2YoCoLPF2RX58m270yQCJ1+wywGSnrJHRX8zU9JZvuS3O2chb
Zf6X4IGKqNU3ISlwl7cj4x2cazCYXjO21Sqh40e8NTt57iNbXeSvK6NjJcRz6PZM7J7D4y4xSO/j
ZE+FZlZIk4f1kPzBk08dAmTmTUuSJYPm9YQWyH4HN5RqTT9Whs5kMsS7XKaHig==";
fn main() {
    
    let cert_data = base64::decode(&String::from(cert).replace("\n", "")).unwrap();

    x509_parser::parse_x509_der(cert_data.as_ref()).expect("no x509 data");

}

Howerver, openssl claims that certificate is valid (this is avert base64-decoding it and writing it to a file):

$ openssl x509 -in cert.der -inform der -noout -text
Certificate:
    Data:
        Version: 1 (0x0)
        Serial Number:
            01:6e:ad:5b:e7:92
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: CN=marquee
        Validity
            Not Before: Nov 27 14:53:31 2019 GMT
            Not After : Nov 27 14:55:11 2029 GMT
        Subject: CN=marquee
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                Public-Key: (2048 bit)
                Modulus:
                    00:84:e1:23:1b:48:a3:44:b2:69:3f:63:56:6f:a5:
                    00:5b:e4:32:39:87:83:e6:1f:0f:bf:c0:8d:b9:44:
                    4d:23:ad:de:37:17:46:e7:d7:65:be:a5:e7:05:fe:
                    a7:8a:83:51:33:f4:1a:40:77:26:65:ef:4f:34:e0:
                    33:00:ac:9c:ba:d2:19:d8:ee:73:13:0a:0b:a5:4f:
                    59:62:6d:5c:55:c7:cd:56:eb:2d:23:7a:b6:c2:ab:
                    02:10:9b:c8:ac:c2:a1:97:c2:fe:57:7e:c1:16:1a:
                    de:0b:21:3d:57:c1:96:12:68:94:88:a1:7b:c1:f5:
                    d9:41:04:85:79:3e:e5:46:04:c1:69:df:99:ad:44:
                    e9:1c:e9:fb:a4:45:6e:4b:fc:5d:24:79:82:75:f7:
                    3d:34:c2:ca:0c:b5:e8:06:06:59:80:3b:e3:82:44:
                    e9:5b:9c:c2:c0:c3:3a:02:45:e5:f4:4d:53:36:92:
                    a6:67:c6:5c:53:6c:1c:15:d6:4f:b7:8a:e8:ed:36:
                    06:e2:cb:45:9b:17:cb:88:a3:9f:44:db:42:98:ec:
                    0b:fe:ae:4d:a3:4d:95:40:62:5a:04:82:76:df:ae:
                    a6:f8:67:3a:3e:64:e6:2e:ba:ca:f3:40:ae:99:26:
                    bd:a1:a0:b5:f2:51:86:32:d7:92:02:7a:f0:93:78:
                    78:a3
                Exponent: 65537 (0x10001)
    Signature Algorithm: sha256WithRSAEncryption
         32:08:74:a4:50:f5:62:4d:46:3c:a5:0e:dd:f4:19:b0:ad:84:
         ae:92:51:68:75:cb:b5:02:e6:08:ea:d5:68:06:5b:bb:85:9f:
         a9:63:17:a1:1c:9f:d3:1c:93:b4:91:97:2c:46:96:33:45:29:
         36:fc:30:b4:a0:9e:65:e3:17:4f:80:bb:9f:00:cd:8a:52:e4:
         65:45:95:66:c7:ec:dc:57:1f:a3:91:a8:6e:80:3b:f0:ad:00:
         00:c3:db:f7:80:a4:af:58:d7:a0:a0:fe:26:82:98:2e:43:b4:
         f3:0f:f7:da:41:50:56:b8:b6:62:80:a8:2c:f1:76:45:7e:7c:
         9b:6e:f4:c9:00:89:d7:ec:32:c0:64:a7:ac:91:d1:5f:cc:d4:
         f4:96:6f:b9:2d:ce:d9:c8:5b:65:fe:97:e0:81:8a:a8:d5:37:
         21:29:70:97:b7:23:e3:1d:9c:6b:30:98:5e:33:b6:d5:2a:a1:
         e3:47:bc:35:3b:79:ee:23:5b:5d:e4:af:2b:a3:63:25:c4:73:
         e8:f6:4c:ec:9e:c3:e3:2e:31:48:ef:e3:64:4f:85:66:56:48:
         93:87:f5:90:fc:c1:93:4f:1d:02:64:e6:4d:4b:92:25:83:e6:
         f5:84:16:c8:7e:07:37:94:6a:4d:3f:56:86:ce:64:32:c4:bb:
         5c:a6:87:8a

Am I doing something wrong? Or is this one of the not yet implemented extensions?

The certificate originates from keycloak, an OpenID Connect identity provider (www.keycloak.org).

Cheers,
Uwe

Verification of Ed25519 certificates

Hi! We would like to use X509Certificate::verify_signature to verify the signature of a certificate created with OpenSSL that was (self-)signed with an Ed25519 key.

Currently this fails with an X509Error::SignatureUnsupportedAlgorithm because the respective OID (1.3.101.112; see also RFC 8410) is neither supported by the x509-parser nor by the oid-registry.

It seems the underlying ring library, however, does support Ed25519 via ring::signature::ED25519, which also implements the required ring::signature::VerificationAlgorithm, so it seems this is a matter of extending the oid-registry and wiring all the existing pieces together.

Would you be interested in supporting (the more and more popular) Ed25519, and if yes, what would be a sensible path forward? For example, is the best/fastest way to submit respective PRs to oid-registry and then to x509-parser? Thanks!

Missing oid2description() function

This crate uses an OidRegistry internally, and provides an oid2sn() function, but does not provide any way to get at the oid description. This is unfortunate, because I don't want to have to maintain my own separate OidRegistry with all of the same entries as that seems like a waste of memory (and it also means I'd get out of sync if this crate ever adds any new oids to its own registry). This crate should either provide an oid2description() function, or perhaps just provide a function that returns a reference to its internal OidRegistry that I can use to look up stuff directly. The latter approach would also let me do things like iterate over all known oids.

Most types are missing Clone

A bunch of types are missing Clone even though they could derive it. Skimming through the docs, practically every type is missing it. The only ones I'm seeing that have it are tuple structs containing an integer, such as pub struct ReasonCode(pub u8). But everything else I've looked at so far could support it too, and I suspect that every single type in this crate could potentially derive Clone.

Right now what's particularly frustrating to me is the lack of Clone on GeneralName (which requires it on X509Name, which requires it on RelativeDistinguishedName, which requires it on AttributeTypeAndValue, which could trivially derive it).

Use crate `oid-registry`

Crate oid-registry has been uploaded to crates.io, and should be the correct way of managing the huge list of possible OIDs.

This crate will depend on oid-registry in the next release. This is the tracking issue for migration.

Feature request: Add function to parse from bytes

I've found that parsing a certificate is difficult because you need to start off with a DerObject, but that in turn requires the parse_der method from the der-parser crate, which requires a static lifetime for its input, which makes it unusable in many circumstances.

There's probably a way to use the more specific functions in der-parser (e.g., parse_der_set) that don't require static lifetimes, but that would require knowing a lot more about the x509 spec. It'd be nice if x509-parser could provide a parse_from_bytes method or something like that encapsulated all of that logic.

Feature request: Expose bytes of encoded tbsCertificate

Hi,

for checking the signature the encoded representation of cbsCertificate is needed. I would suggest to add api to expose this representation.

I think this can be implemented with a nom combinator similar to recognize. If you interested in this, I can create a pull request.

Authority Key Identifier isn't parsed

Many of the common x509 extensions are parsed, but Authority Key Identifier isn't among them.

Is there any reason this extension isn't supported? Would this be an improvement that you'd accept a PR for?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.