Giter Club home page Giter Club logo

yasna.rs's Introduction

yasna.rs: ASN.1 library for Rust

Build Status

This is a Rust library for reading and writing ASN.1 data.

Since this library is at an early stage, the APIs are subject to change. However, BERReader and DERWriter functionalities are getting stable.

Serialization/Construction

Serialization in DER (Distinguished Encoding Rules) is supported. It can also be used for serialization in BER (Basic Encoding Rules).

fn main() {
    let der = yasna::construct_der(|writer| {
        writer.write_sequence(|writer| {
            writer.next().write_i64(10);
            writer.next().write_bool(true);
            return Ok(());
        })
    });
    println!("(10, true) = {:?}", der);
}

Currently, these datatypes are supported:

  • BOOLEAN, INTEGER, BITSTRING, OCTETSTRING, NULL, OBJECT IDENTIFIER,
  • SEQUENCE, SEQUENCE OF, SET, SET OF, CHOICE,
  • UTF8String, NumericString, PrintableString, VisibleString, IA5String, BMPString,
  • UTCTime, GeneralizedTime,
  • Explicitly/Implicitly tagged types,
  • DEFAULT/OPTIONAL in SEQUENCE/SET.

These datatypes are not supported:

  • REAL
  • TeletexString, VideotexString, GraphicString, GeneralString, UniversalString,
  • TIME, DATE, TIME-OF-DAY, DATE-TIME, DURATION.

Deserialization/Parsing

Deserialization in BER (Basic Encoding Rules) or DER (Distinguished Encoding Rules) is supported.

fn main() {
    let asn = yasna::parse_der(&[48, 6, 2, 1, 10, 1, 1, 255], |reader| {
        reader.read_sequence(|reader| {
            let i = reader.next().read_i64()?;
            let b = reader.next().read_bool()?;
            return Ok((i, b));
        })
    }).unwrap();
    println!("{:?} = [48, 6, 2, 1, 10, 1, 1, 255]", asn);
}

Currently, these datatypes are supported:

  • BOOLEAN, INTEGER, BITSTRING, OCTETSTRING, NULL, OBJECT IDENTIFIER,
  • SEQUENCE, SEQUENCE OF, SET, SET OF, CHOICE,
  • UTF8String, NumericString, PrintableString, VisibleString, IA5String, BMPString,
  • UTCTime, GeneralizedTime,
  • Explicitly/Implicitly tagged types,
  • DEFAULT/OPTIONAL in SEQUENCE.

These datatypes are not supported:

  • REAL
  • TeletexString, VideotexString, GraphicString, GeneralString, UniversalString,
  • TIME, DATE, TIME-OF-DAY, DATE-TIME, DURATION.
  • DEFAULT/OPTIONAL in SET.

Other encodings

This library is currently specialized for BER (Basic Encoding Rules) and DER (Distinguished Encoding Rules). Other encodings such as CER (Canonical Encoding Rules), PER (Packed Encoding Rules), and XER (XML Encoding Rules) are currently out of scope.

Streaming

This library is currently specialized for on-memory serialization/deserialization. There are no plans for streaming ones.

Compatibility

The minimum supported Rust version (MSRV) of yasna.rs is Rust 1.36.0. Optional feature flags that enable interoperability with third-party crates (e.g. time) follow the policy of that crate if stricter.

License

This library is distributed under MIT/Apache-2.0 dual license.

yasna.rs's People

Contributors

5225225 avatar aandyl avatar alexanderkjall avatar atouchet avatar connec avatar est31 avatar g2p avatar jack-fortanix avatar jethrogb avatar markhibberd avatar qnighy 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

yasna.rs's Issues

How to impl DEREncodable for CHOICE

How to impl DEREncodable for a CHOICE in struct:

PrefixFulfillment ::= SEQUENCE {
     subcondition       Condition
}
Condition ::= CHOICE {
     preimageSha256   [0] SimpleSha256Condition,
     prefixSha256     [1] CompoundSha256Condition,
     thresholdSha256  [2] CompoundSha256Condition,
     rsaSha256        [3] SimpleSha256Condition,
     ed25519Sha256    [4] SimpleSha256Condition
}
use yasna;
use yasna::tags::*;
let data = &[48, 5, 2, 1, 10, 5, 0];
let asn = yasna::parse_der(data, |reader| {
    reader.collect_sequence_of(|reader| {
        let tag = try!(reader.lookahead_tag());
        let choice;
        if tag == TAG_INTEGER {
            choice = Some(try!(reader.read_i64()));
        } else {
            try!(reader.read_null());
            choice = None;
        }
        return Ok(choice);
    })
}).unwrap();
assert_eq!(&asn, &[Some(10), None]);

0.2.1 release

@jethrogb do you have any further changes left to upstream? If not, I'd like to make a 0.2.1 release.

0.2 release

Due to the update of num to 0.2, and because we are using it in our public API, there have been breaking changes on our API. Therefore, the next release will be 0.2 and not 0.1.4.

This issue is a tracking issue for the 0.2 release. If everything inside this issue is done, the 0.2 release will be released.

Things to be done until the 0.2 release:

  • Reducing dependencies (#7, #11)
  • Update of num to 0.2 (#12, #17)
  • Update of bit-vec to 0.5 (#20)
  • Adding all the features that rcgen needs (#15, #22)
  • Review of the remaining PRs in the queue (#9, #10)
  • Merging of #22 (#23)

Can't write explicitly tagged sets

I don't think it's possible to generate the following with yasna right now:

$ openssl asn1parse -i
-----BEGIN CERTIFICATE REQUEST-----
MIIC1zCCAb8CAQAwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDCCASIwDQYJKoZIhvcN
AQEBBQADggEPADCCAQoCggEBALZ0OBVVnPy76be52VkLNzHFKFTvBT2cwvQhlR3y
ryy9k3mzFH7tDxxFiqhvDrpaK7emYwoRuXspp/DZAElYVv7RNgnjQwuDjUoYtlcq
mDEaAP0N021myHqyDMRo1AVzta7suGZ9Zv9JY2pWA+nkYrI51KP18ixcGbga1E6W
VgZ79VBCoebC/zs61wjtCcRWV8O0XH6V8J5vuwG51D8ZYIIczg40dGidZ8R0TqM/
b7Rp747hGYB4nE4khWFn+dgyRbQuF9wOxVUbm/eG516KivXHxsXtA7hpyqm7s13C
ucER2XOUJfp/ks78l4JY09Vz9joa1fNp5dqbTjfstf7zfW8CAwEAAaBNMBAGCSqG
SIb3DQEJAjEDDAF4MBAGCSqGSIb3DQEJBzEDDAF4MCcGCSqGSIb3DQEJDjEaMBgw
CQYDVR0TBAIwADALBgNVHQ8EBAMCBeAwDQYJKoZIhvcNAQELBQADggEBAFntX9+6
UxtHFd/XrIFA8IoN7o9Q6RE6lMGy6fvQ8cpeorhnaJs92VcF95q22lLl4HrDgGYq
vsXzS9OBW1EDwp56JKo5CC0M5tBTKEdqAuhdoeyb7aliHSzpACycmgZx+M0BHcEi
XuaceP+lTd+u7N6qXvKIK+Zeayb6lPcsFG+Pna5EZ0se1pGcLPTAHAMTYOOKntZp
PYJp7G3BOpg1Q7FMiLpcCGpTV2ibfJm09gsb1AVazYWMhRixY6xA2+He3TdSt53M
S/WaUMug/FVcqJwirJt65ZuROgNZY9ATaD9bu69ALA0BCkyu7/1hoRMLKrqGEc9W
J+Dby82v7utsHFI=
-----END CERTIFICATE REQUEST-----
    0:d=0  hl=4 l= 727 cons: SEQUENCE          
    4:d=1  hl=4 l= 447 cons:  SEQUENCE          
    8:d=2  hl=2 l=   1 prim:   INTEGER           :00
   11:d=2  hl=2 l=  69 cons:   SEQUENCE          
   13:d=3  hl=2 l=  11 cons:    SET               
   15:d=4  hl=2 l=   9 cons:     SEQUENCE          
   17:d=5  hl=2 l=   3 prim:      OBJECT            :countryName
   22:d=5  hl=2 l=   2 prim:      PRINTABLESTRING   :AU
   26:d=3  hl=2 l=  19 cons:    SET               
   28:d=4  hl=2 l=  17 cons:     SEQUENCE          
   30:d=5  hl=2 l=   3 prim:      OBJECT            :stateOrProvinceName
   35:d=5  hl=2 l=  10 prim:      UTF8STRING        :Some-State
   47:d=3  hl=2 l=  33 cons:    SET               
   49:d=4  hl=2 l=  31 cons:     SEQUENCE          
   51:d=5  hl=2 l=   3 prim:      OBJECT            :organizationName
   56:d=5  hl=2 l=  24 prim:      UTF8STRING        :Internet Widgits Pty Ltd
   82:d=2  hl=4 l= 290 cons:   SEQUENCE          
   86:d=3  hl=2 l=  13 cons:    SEQUENCE          
   88:d=4  hl=2 l=   9 prim:     OBJECT            :rsaEncryption
   99:d=4  hl=2 l=   0 prim:     NULL              
  101:d=3  hl=4 l= 271 prim:    BIT STRING        
  376:d=2  hl=2 l=  77 cons:   cont [ 0 ]        
  378:d=3  hl=2 l=  16 cons:    SEQUENCE          
  380:d=4  hl=2 l=   9 prim:     OBJECT            :unstructuredName
  391:d=4  hl=2 l=   3 cons:     SET               
  393:d=5  hl=2 l=   1 prim:      UTF8STRING        :x
  396:d=3  hl=2 l=  16 cons:    SEQUENCE          
  398:d=4  hl=2 l=   9 prim:     OBJECT            :challengePassword
  409:d=4  hl=2 l=   3 cons:     SET               
  411:d=5  hl=2 l=   1 prim:      UTF8STRING        :x
  414:d=3  hl=2 l=  39 cons:    SEQUENCE          
  416:d=4  hl=2 l=   9 prim:     OBJECT            :Extension Request
  427:d=4  hl=2 l=  26 cons:     SET               
  429:d=5  hl=2 l=  24 cons:      SEQUENCE          
  431:d=6  hl=2 l=   9 cons:       SEQUENCE          
  433:d=7  hl=2 l=   3 prim:        OBJECT            :X509v3 Basic Constraints
  438:d=7  hl=2 l=   2 prim:        OCTET STRING      [HEX DUMP]:3000
  442:d=6  hl=2 l=  11 cons:       SEQUENCE          
  444:d=7  hl=2 l=   3 prim:        OBJECT            :X509v3 Key Usage
  449:d=7  hl=2 l=   4 prim:        OCTET STRING      [HEX DUMP]:030205E0
  455:d=1  hl=2 l=  13 cons:  SEQUENCE          
  457:d=2  hl=2 l=   9 prim:   OBJECT            :sha256WithRSAEncryption
  468:d=2  hl=2 l=   0 prim:   NULL              
  470:d=1  hl=4 l= 257 prim:  BIT STRING        

At offset 376 is a [0] SET OF SEQUENCE { ... }, see https://tools.ietf.org/html/rfc2986#section-4. DERWriter::write_tagged has a callback with a DERWriter, not DERWriterSeq or DERWriterSet, so I don't see how to emit the multiple sequences in the set.

Cannot read sequences tagged with tags other than sequence

When parsing an LDAP query, the following structure must be parsed:

SearchRequest ::= [APPLICATION 3] SEQUENCE {
             baseObject      LDAPDN,
             scope           ENUMERATED {
                  baseObject              (0),
                  singleLevel             (1),
                  wholeSubtree            (2),
                  ...  },
             derefAliases    ENUMERATED {
                  neverDerefAliases       (0),
                  derefInSearching        (1),
                  derefFindingBaseObj     (2),
                  derefAlways             (3) },
             sizeLimit       INTEGER (0 ..  maxInt),
             timeLimit       INTEGER (0 ..  maxInt),
             typesOnly       BOOLEAN,
             filter          Filter,
             attributes      AttributeSelection }

However, the only way to parse a sequence of multiple primitives is by calling read_sequence, which assumes a tag of SEQUENCE.

To read the Tag::application(3) one must employ read_tagged with the correct tag, which will result in a BERReader - but we really want a BERReaderSeq here.

Ownership inquiry from @est31

I've heard via another channel that @est31 is willing to contribute to this project as a maintainer.

Honestly, I've already lost interests on this project and been procrastinating its maintenance. I'm sorry for that, but therefore I'm willing to give its ownership to someone who has more interests on it.

Some confirmations:

  • Do you really consider it useful? I've not investigated its competitors, but there'll definitely be some. yasna.rs is just a byproduct of a personal experiment on implementing TLS (https://github.com/qnighy/crypt-impl-rust ).
  • You want write access to this repository on GitHub and ownership of the crates.io registration, right?

support `num-bigint-dig` crates ?

I used num-bigint-dig instead of num-bigint

In my Cargo.toml

num-bigint = { version = "0.8.1", features = ["i128", "u64_digit"], default-features = false, package = "num-bigint-dig" }

Below is my code

    let cipher = encrypt(data, public_key);
    let x = BigUint::from_bytes_be(&cipher[0..32]);
    let y = BigUint::from_bytes_be(&cipher[32..64]);
    let sm3 = &cipher[64..96];
    let secret = &cipher[96..];
    yasna::construct_der(|writer| {
        writer.write_sequence(|writer| {
            writer.next().write_biguint(&x);
            writer.next().write_biguint(&y);
            writer.next().write_bytes(&sm3);
            writer.next().write_bytes(&secret);
        });
    })

But the method write_biguint dose not support num-bigint-dig::BigUnit.

So How to make it work?

How to peek into DER and stop reading early?

In some scenarios, I want to peek into a DER, parse an early element (think of an OID), and return it. It appears that I cannot do that with BERReader without reading the whole DER.

My best try was to ignore the remainder with the following

fn extract_oid(der: &[u8]) {
    let oid = yasna::parse_der(der, |r| {
        // Let's say we have a SEQUENCE
        r.read_sequence(|r| {
            // The OID I want is embedded inside an inner SEQUENCE:
            let oid = r.next().read_sequence(|r| {
                let oid = r.next().read_oid()?;
                let _remainder = r.next().read_der(); // Ignore the remainder of that sequence
                Ok(oid)
            })?;
            let _remainder = r.next().read_der(); // Ignore the rest of the sequence
            Ok(oid)
        })
    });
    println!("extracted OID {:?}", oid);
}

Without checking the remainder, yasna fails with Err(ASN1Error { kind: Extra }), and I would like to ignore this error.

Ship license file in the crate

For distros it's generally required to have the license files shipped as part of the source so the license should be included in the yasna crate

test_ber_read_overflow fails on i386

Running the testsuite on i386 (or 32bit arm) yields the following test failure:

---- reader::tests::test_ber_read_overflow stdout ----
thread 'reader::tests::test_ber_read_overflow' panicked at 'assertion failed: `(left == right)`
  left: `Eof`,
 right: `IntegerOverflow`', src/reader/tests.rs:1665:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace


failures:
    reader::tests::test_ber_read_overflow

arm log: https://ci.debian.net/data/autopkgtest/testing/armel/r/rust-yasna/35390290/log.gz
i386 log: https://ci.debian.net/data/autopkgtest/testing/i386/r/rust-yasna/35391533/log.gz

It would be easy to gate the test to only run on 64bit, but looking at the code I feared that this might indicate a deeper problem and wanted some feedback on how to resolve it.

Don't use default features from num-*

It seems the default features of num-bigint and num-integer import lots of dependencies, such as rustc-serialize. It could be a good idea to add a default-features=false in your import of those crates.

Issue with reading INTEGER from buffer

I have found an issue with the library where it fails to read a valid integer during parsing.
I have created a contrived test example below that shows this works using the der-parser library:

use der_parser; // der-parser = "4.0.1"
use yasna; // yasna = "0.3.2"

// this one works fine for both
const INTEGER_WITH_TRAILING_ZERO: &[u8] = &[2, 3, 0, 255, 255];
// this one returns IntegerOverflow in yasna
const INTEGER_NO_TRAILING_ZERO: &[u8] = &[2, 2, 255, 255];

#[test]
fn yasna_read_integer() {
    let tests: &[(u32, &[u8])] = &[(65535, INTEGER_NO_TRAILING_ZERO), (65535, INTEGER_WITH_TRAILING_ZERO)];
    for &(evalue, data) in tests {
        let value = yasna::parse_ber(data, |reader| reader.read_u32()).unwrap();
        assert_eq!(value, evalue);
    }
}

#[test]
fn der_parser_read_integer() {
    let tests: &[(u32, &[u8])] = &[(65535, INTEGER_NO_TRAILING_ZERO), (65535, INTEGER_WITH_TRAILING_ZERO)];
    for &(evalue, data) in tests {
        let (_, value) = der_parser::ber::parse_ber_integer(data).unwrap();
        assert_eq!(value.as_u32().unwrap(), evalue);
    }
}

I don't know enough about ASN.1 notation to know why there is an explicit check here that seems to be the cause of this:

// in src/reader/mod.rs::read_u64
else if buf[0] >= 128 {
    return Err(ASN1Error::new(ASN1ErrorKind::IntegerOverflow));
} 

but as it seems that both should be supported I am raising this as a bug so it can be fixed.

no method named `read_biguint`

abort by error

error[E0599]: no method named `read_biguint` found for type `yasna::reader::BERReader<'_, '_>` in the current scope
  --> src/sm2/signature.rs:44:39
   |
44 |                 let r = reader.next().read_biguint()?;
   |                                       ^^^^^^^^^^^^

error[E0599]: no method named `read_biguint` found for type `yasna::reader::BERReader<'_, '_>` in the current scope
  --> src/sm2/signature.rs:45:39
   |
45 |                 let s = reader.next().read_biguint()?;
   |                                       ^^^^^^^^^^^^

error[E0599]: no method named `write_biguint` found for type `yasna::writer::DERWriter<'_>` in the current scope
  --> src/sm2/signature.rs:78:31
   |
78 |                 writer.next().write_biguint(&self.r);
   |                               ^^^^^^^^^^^^^

error[E0599]: no method named `write_biguint` found for type `yasna::writer::DERWriter<'_>` in the current scope
  --> src/sm2/signature.rs:79:31
   |
79 |                 writer.next().write_biguint(&self.s);
   |                               ^^^^^^^^^^^^^

error: aborting due to 4 previous errors

my code

use yasna;

pub fn der_decode(buf: &[u8]) -> Result<Signature, yasna::ASN1Error> {
        let (r, s) = yasna::parse_der(buf, |reader| {
            reader.read_sequence(|reader| {
                let r = reader.next().read_biguint()?;
                let s = reader.next().read_biguint()?;
                Ok((r, s))
            })
        })?;
        Ok(Signature { r, s })
    }
pub fn der_encode(&self) -> Vec<u8> {
        yasna::construct_der(|writer| {
            writer.write_sequence(|writer| {
                writer.next().write_biguint(&self.r);
                writer.next().write_biguint(&self.s);
            })
        })
    }

Unable to read SEQUENCE OF

Is SEQUENCE_OF supported? I see there is functionality for SET_OF. When attempting to read a SEQUENCE_OF tag, I get an EOF error.

Expose `read_length` and friends

Currently, the read_length function is not accessible, requiring a re-implementation in programs using this library that work with ASN1 over network streams.

Consider a protocol like LDAP, where a client or server will send an ASN1 object. To parse, ASN1 data must first be read, but it is type and length prefixed. Properly allocating memory and reading the correct number of bytes requires parsing this header.

If I am not mistaken, yasna does not support reading directly from stream (all parse methods require passing a slice). A consuming program is forced to implement at least enough of ASN1 to read the type and length headers. It would be a great help if these functions could be factored out and reused by clients.

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.