pax_global_header00006660000000000000000000000064147657640120014526gustar00rootroot0000000000000052 comment=560739207523f50e6260f38b52dd70bbe185f762 rusticata-asn1-rs-07e764c/000077500000000000000000000000001476576401200153505ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/.github/000077500000000000000000000000001476576401200167105ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/.github/dependabot.yml000066400000000000000000000003121476576401200215340ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "daily" - package-ecosystem: github-actions directory: "/" schedule: interval: weekly rusticata-asn1-rs-07e764c/.github/workflows/000077500000000000000000000000001476576401200207455ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/.github/workflows/rust.yml000066400000000000000000000060431476576401200224700ustar00rootroot00000000000000name: Continuous integration on: push: pull_request: merge_group: schedule: - cron: '0 18 * * *' env: check_ext_rust_version: nightly-2024-06-30 # ^ sync with https://github.com/awslabs/cargo-check-external-types/blob/main/rust-toolchain.toml jobs: check: name: Check runs-on: ubuntu-latest strategy: matrix: rust: - stable - 1.63.0 - nightly steps: - uses: actions/checkout@v4 - name: Install ${{ matrix.rust }} toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - name: Cargo update run: cargo update - name: Cargo update (fix for MSRV) run: cargo update -p time --precise 0.3.20 if: matrix.rust == '1.63.0' - run: RUSTFLAGS="-D warnings" cargo check test_features: name: Test suite (with features) runs-on: ubuntu-latest strategy: matrix: features: - --features=default - --all-features - --features=bigint,serialize,debug - --features=bigint,serialize,trace - --no-default-features steps: - uses: actions/checkout@v4 - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - run: cargo test ${{ matrix.features }} no_std: name: no-std runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install stable toolchain uses: dtolnay/rust-toolchain@stable - run: RUSTFLAGS="-D warnings" cargo check --no-default-features fmt: name: Rustfmt runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install stable rustfmt uses: dtolnay/rust-toolchain@stable with: components: rustfmt - run: cargo fmt --all -- --check clippy: name: Clippy runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install nightly clippy uses: dtolnay/rust-toolchain@nightly with: components: clippy - run: cargo clippy -- -D warnings doc: name: Build documentation runs-on: ubuntu-latest env: RUSTDOCFLAGS: --cfg docsrs steps: - uses: actions/checkout@v4 - name: Install nightly rust uses: dtolnay/rust-toolchain@nightly - run: cargo doc --workspace --no-deps --all-features semver: name: Check semver compatibility runs-on: ubuntu-latest steps: - name: Checkout sources uses: actions/checkout@v4 - name: Check semver uses: obi1kenobi/cargo-semver-checks-action@v2 check-external-types: name: Validate external types appearing in public API runs-on: ubuntu-latest continue-on-error: true steps: - name: Checkout sources uses: actions/checkout@v4 - name: Install rust toolchain uses: dtolnay/rust-toolchain@master with: toolchain: ${{ env.check_ext_rust_version }} - run: cargo install --locked cargo-check-external-types - run: cargo check-external-types rusticata-asn1-rs-07e764c/.github/workflows/security-audit.yml000066400000000000000000000004451476576401200244460ustar00rootroot00000000000000name: Security audit on: schedule: - cron: "0 8 * * *" push: paths: - "**/Cargo.*" jobs: security_audit: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: rustsec/audit-check@v2 with: token: ${{ secrets.GITHUB_TOKEN }} rusticata-asn1-rs-07e764c/.gitignore000066400000000000000000000000211476576401200173310ustar00rootroot00000000000000**/target /.idea rusticata-asn1-rs-07e764c/CHANGELOG.md000066400000000000000000000114261476576401200171650ustar00rootroot00000000000000# Change Log ## [Unreleased][unreleased] ### Changed/Fixed ### Added ### Thanks ## 0.7.1 ### Changed/Fixed - Update lock file - Update features trace and debug to depend on std (#70) - Fix doctests with `--no-default-features` - Make some tests conditional if `std` is not enabled - Fix misc clippy (nightly) warnings ## 0.7.0 ### Changed/Fixed - Update `thiserror` to 2.0 and derive `Error` for error types (even with `no_std`) ### Added - Add `ToStatic` custom derive attribute - Add `ToDerSequence` custom derive attribute (#44) ## 0.6.2 ### Changed/Fixed Important: - Fix a potential panic when using derived parsers, when using custom errors (see #40) This affects only auto-derived parsers specifying a custom error, and parsing incomplete data. Fixed: - Fix wrong encoding of large tags (#43) - Fix wrong encoding of optional TaggedImplicit object (#42) General: - Add licences to sub-crates (#38) - Refactor CI (#36, #41) - Updates license field to valid SPDX format (#34) ### Thanks - Daniel McCarney, Łukasz Wojniłowicz, Isaiah Becker-Mayer, Philip Ye ## 0.6.1 ### Changed/Fixed - Provide implementations for `Option::from_der` ## 0.6.0 ### Changed/Fixed General: - Set MSRV to 1.67.0 - Add PartialEq to SequenceOf and SetOf - Implement traits for SequenceOf and SetOf to improve usability - Fix receiver lifetimes in `Any` methods - Implement `BmpString::try_from` for &Any (so it does not need to consume input) (#26) - oid: change macro to expect dot-separated literals (#28) - Fix wrong tag in encoding of SET OF (#30) - Option: require T::Tagged, and check tag before constraints (#27) - Add missing constructed bit when serializing [2] IMPLICIT) (#18) - Add methods to convert Any to Real - Add tag for CHARACTER STRING (29) - Fix method `Any::as_generalstring` (wrong return type) - Add method `Any::as_bmpstring` - Fix clippy warnings (1.76.0) Dependencies updates: - pem to 3.0 - hex-literal to 0.4 - syn to 2.0 - examples: drop circular dev-dependency caused by oid-registry ### Thanks - Sergio Benitez, Andrey Chesnokov ## 0.5.2 ### Changed/Fixed - Fix decoding of integers: check if value will wrap if integer is signed - Fix encoding of integers (add 0x00 prefix when required, and remove extra 0xff for negative integers) - Fix a small math error in GeneralizedTime - Introduce trait GetObjectContent, use `from_ber` when skipping BER content (closes #14) ### Thanks - Nadja Reitzenstein, Christian Speich ## 0.5.1 Minor fixes: - Fix constraints too strict on `TaggedValue::FromDer`, do not auto-derive - Update oid-registry - Fix `Any::as_relative_oid` to take a reference (and not consume input) derive: - Add special case handler for alias to Any - Add support for DEFAULT attribute ## 0.5.0 This release adds some new methods and custom derive attributes. It also adds a lot of tests to improve code coverage. asn1-rs: - Add helper types for Application/Private tagged values - Any: add methods `from_ber_and_then` (and `_der`) - TaggedParser: add documentation for `from_ber_and_then` (and `_der`) - Oid: add method `starts_with` - Fix documentation of application and private tagged helpers - Fix clippy warnings derive: - Add custom derive BerAlias and DerAlias coverage: - Add many tests to improve coverage ## 0.4.2 Bugfix release: - Remove explicit output lifetime in traits - Fix wrong encoding `BmpString` when using `ToDer` - Fix parsing of some EmbeddedPdv subtypes - Fix encoded length for Enumerated - Add missing `DerAutoDerive` impl for bool - Add missing `DerAutoDerive` impl for f32/f64 - Remove redundant check, `Any::from_der` checks than length is definite - Length: fix potential bug when adding Length + Indefinite - Fix inverted logic in `Header::assert_definite()` ## 0.4.1 Minor fix: - add missing file in distribution (fix docs.rs build) ## 0.4.0 asn1-rs: - Add generic error parameter in traits and in types - This was added for all types except a few (like `Vec` or `BTreeSet`) due to Rust compiler limitations - Add `DerAutoDerive` trait to control manual/automatic implementation of `FromDer` - This allow controlling automatic trait implementation, and providing manual implementations of both `FromDer` and `CheckDerConstraints` - UtcTime: Introduce utc_adjusted_date() to map 2 chars years date to 20/21 centuries date (#9) derive: - Add attributes to simplify deriving EXPLICIT, IMPLICIT and OPTIONAL - Add support for different tag classes (like APPLICATION or PRIVATE) - Add support for custom errors and mapping errors - Add support for deriving BER/DER SET - DerDerive: derive both CheckDerConstraints and FromDer documentation: - Add doc modules for recipes and for custom derive attributes - Add note on trailing bytes being ignored in sequence - Improve documentation for notation with braces in TaggedValue - Improve documentation rusticata-asn1-rs-07e764c/Cargo.lock000066400000000000000000000410741476576401200172630ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "asn1-rs" version = "0.7.1" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", "bitvec", "colored", "cookie-factory", "displaydoc", "hex-literal", "nom", "num-bigint", "num-traits", "pem", "rusticata-macros", "thiserror", "time", "trybuild", ] [[package]] name = "asn1-rs-derive" version = "0.6.0" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "asn1-rs-impl" version = "0.2.0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "autocfg" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitvec" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" dependencies = [ "funty", "radium", "tap", "wyz", ] [[package]] name = "colored" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fde0e0ec90c9dfb3b4b1a0891a7dcd0e2bffde2f7efed5fe7c9bb00e5bfb915e" dependencies = [ "windows-sys", ] [[package]] name = "cookie-factory" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" dependencies = [ "futures", ] [[package]] name = "deranged" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "funty" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", "futures-executor", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", "futures-util", ] [[package]] name = "futures-io" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-macro" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "futures-sink" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "pin-utils", "slab", ] [[package]] name = "glob" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" [[package]] name = "hashbrown" version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "hex-literal" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "indexmap" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", ] [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "memchr" version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-conv" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "pem" version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38af38e8470ac9dee3ce1bae1af9c1671fffc44ddfd8bd1d0a3445bf349a8ef3" dependencies = [ "base64", "serde", ] [[package]] name = "pin-project-lite" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "powerfmt" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "proc-macro2" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "radium" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" [[package]] name = "rusticata-macros" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "faf0c4a6ece9950b9abdb62b1cfcf2a68b3b67a10ba445b3bb85be2a293d0632" dependencies = [ "nom", ] [[package]] name = "ryu" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", "ryu", "serde", ] [[package]] name = "serde_spanned" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] [[package]] name = "slab" version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" dependencies = [ "autocfg", ] [[package]] name = "syn" version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "tap" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "target-triple" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" [[package]] name = "termcolor" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "thiserror" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" version = "2.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "time" version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa", "num-conv", "powerfmt", "serde", "time-core", "time-macros", ] [[package]] name = "time-core" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", ] [[package]] name = "toml" version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148" dependencies = [ "serde", "serde_spanned", "toml_datetime", "toml_edit", ] [[package]] name = "toml_datetime" version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] [[package]] name = "toml_edit" version = "0.22.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", "winnow", ] [[package]] name = "trybuild" version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ae08be68c056db96f0e6c6dd820727cca756ced9e1f4cc7fdd20e2a55e23898" dependencies = [ "glob", "serde", "serde_derive", "serde_json", "target-triple", "termcolor", "toml", ] [[package]] name = "unicode-ident" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "winapi-util" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ "windows-sys", ] [[package]] name = "windows-sys" version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e97b544156e9bebe1a0ffbc03484fc1ffe3100cbce3ffb17eac35f7cdd7ab36" dependencies = [ "memchr", ] [[package]] name = "wyz" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" dependencies = [ "tap", ] rusticata-asn1-rs-07e764c/Cargo.toml000066400000000000000000000034721476576401200173060ustar00rootroot00000000000000[package] description = "Parser/encoder for ASN.1 BER/DER data" license = "MIT OR Apache-2.0" keywords = ["BER","DER","ASN1","parser","nom"] homepage = "https://github.com/rusticata/asn1-rs" repository = "https://github.com/rusticata/asn1-rs.git" name = "asn1-rs" version = "0.7.1" authors = ["Pierre Chifflier "] categories = ["parser-implementations"] readme = "README.md" edition = "2018" rust-version = "1.63" include = [ "CHANGELOG.md", "LICENSE-*", "README.md", ".gitignore", "Cargo.toml", "doc/*.md", "examples/*.rs", "src/*.rs", "src/asn1_types/*.rs", "src/asn1_types/real/*.rs", "src/asn1_types/sequence/*.rs", "src/asn1_types/set/*.rs", "src/asn1_types/strings/*.rs", "src/asn1_types/tagged/*.rs", "src/ber/*.rs", "src/doc/*.rs", "tests/*.rs", ] [package.metadata.docs.rs] all-features = true rustdoc-args = ["--cfg", "docsrs"] [features] default = ["std"] bigint = ["num-bigint"] bits = ["bitvec"] datetime = ["time"] debug = ["std", "colored"] serialize = ["cookie-factory"] std = [] trace = ["debug"] [dependencies] asn1-rs-derive = { version="0.6", path="./derive" } asn1-rs-impl = { version="0.2", path="./impl" } bitvec = { version="1.0", optional=true } colored = { version="3.0", optional=true } cookie-factory = { version="0.3.0", optional=true } displaydoc = "0.2.2" nom = { version="7.0", default-features=false, features=["std"] } num-bigint = { version = "0.4", optional = true } num-traits = "0.2.14" rusticata-macros = "4.0" thiserror = "2.0.0" time = { version="0.3", features=["macros", "parsing", "formatting"], optional=true } [dev-dependencies] colored = "3.0" hex-literal = "0.4" pem = "3.0" trybuild = "1.0" [package.metadata.cargo_check_external_types] allowed_external_types = [ "nom", "nom::*", "asn1_rs_derive", "asn1_rs_derive::*" ] rusticata-asn1-rs-07e764c/LICENSE-APACHE000066400000000000000000000251371476576401200173040ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. rusticata-asn1-rs-07e764c/LICENSE-MIT000066400000000000000000000020441476576401200170040ustar00rootroot00000000000000Copyright (c) 2017 Pierre Chifflier Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. rusticata-asn1-rs-07e764c/README.md000066400000000000000000000147771476576401200166470ustar00rootroot00000000000000 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT) [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE) [![docs.rs](https://docs.rs/asn1-rs/badge.svg)](https://docs.rs/asn1-rs) [![crates.io](https://img.shields.io/crates/v/asn1-rs.svg)](https://crates.io/crates/asn1-rs) [![Download numbers](https://img.shields.io/crates/d/asn1-rs.svg)](https://crates.io/crates/asn1-rs) [![Github CI](https://github.com/rusticata/asn1-rs/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/asn1-rs/actions) [![Minimum rustc version](https://img.shields.io/badge/rustc-1.63.0+-lightgray.svg)](#rust-version-requirements) # BER/DER Parsers/Encoders A set of parsers/encoders for Basic Encoding Rules (BER [[X.690]]) and Distinguished Encoding Rules(DER [[X.690]]) formats, implemented with the [nom] parser combinator framework. It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken to ensure security and safety of this crate, including design (recursion limit, defensive programming), tests, and fuzzing. It also aims to be panic-free. This crate is a rewrite of [der-parser](https://crates.io/crates/der-parser) to propose a more data-oriented API, and add generalized support for serialization. Many ideas were borrowed from the [crypto/utils/der](https://github.com/RustCrypto/utils/tree/master/der) crate (like the `Any`/`TryFrom`/`FromDer` mechanism), adapted and merged into a generalized BER/DER crate. Credits (and many thanks) go to Tony Arcieri for writing the original crate. # BER/DER parsers BER stands for Basic Encoding Rules, and is defined in [[X.690]]. It defines a set of rules to encode and decode ASN.1 [[X.680]] objects in binary. [[X.690]] also defines Distinguished Encoding Rules (DER), which is BER with added rules to ensure canonical and unequivocal binary representation of objects. The choice of which one to use is usually guided by the speficication of the data format based on BER or DER: for example, X.509 uses DER as encoding representation. The main traits for parsing are the [`FromBer`] and [`FromDer`] traits. These traits provide methods to parse binary input, and return either the remaining (unparsed) bytes and the parsed object, or an error. The parsers follow the interface from [nom], and the [`ParseResult`] object is a specialized version of `nom::IResult`. This means that most `nom` combinators (`map`, `many0`, etc.) can be used in combination to objects and methods from this crate. Reading the nom documentation may help understanding how to write and combine parsers and use the output. **Minimum Supported Rust Version**: 1.63.0 # Recipes See [doc::recipes] and [doc::derive] for more examples and recipes. See [doc::debug] for advice and tools to debug parsers. ## Examples Parse 2 BER integers: ```rust use asn1_rs::{Integer, FromBer}; let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00, ]; let (rem, obj1) = Integer::from_ber(&bytes).expect("parsing failed"); let (rem, obj2) = Integer::from_ber(&bytes).expect("parsing failed"); assert_eq!(obj1, Integer::from_u32(65537)); ``` In the above example, the generic [`Integer`] type is used. This type can contain integers of any size, but do not provide a simple API to manipulate the numbers. In most cases, the integer either has a limit, or is expected to fit into a primitive type. To get a simple value, just use the `from_ber`/`from_der` methods on the primitive types: ```rust use asn1_rs::FromBer; let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00, ]; let (rem, obj1) = u32::from_ber(&bytes).expect("parsing failed"); let (rem, obj2) = u32::from_ber(&rem).expect("parsing failed"); assert_eq!(obj1, 65537); assert_eq!(obj2, 65536); ``` If the parsing succeeds, but the integer cannot fit into the expected type, the method will return an `IntegerTooLarge` error. # BER/DER encoders BER/DER encoding is symmetrical to decoding, using the traits `ToBer` and [`ToDer`] traits. These traits provide methods to write encoded content to objects with the `io::Write` trait, or return an allocated `Vec` with the encoded data. If the serialization fails, an error is returned. ## Examples Writing 2 BER integers: ```rust use asn1_rs::{Integer, ToDer}; let mut writer = Vec::new(); let obj1 = Integer::from_u32(65537); let obj2 = Integer::from_u32(65536); let _ = obj1.write_der(&mut writer).expect("serialization failed"); let _ = obj2.write_der(&mut writer).expect("serialization failed"); let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00, ]; assert_eq!(&writer, bytes); ``` Similarly to `FromBer`/`FromDer`, serialization methods are also implemented for primitive types: ```rust use asn1_rs::ToDer; let mut writer = Vec::new(); let _ = 65537.write_der(&mut writer).expect("serialization failed"); let _ = 65536.write_der(&mut writer).expect("serialization failed"); let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, 0x02, 0x03, 0x01, 0x00, 0x00, ]; assert_eq!(&writer, bytes); ``` If the parsing succeeds, but the integer cannot fit into the expected type, the method will return an `IntegerTooLarge` error. ## Changes See `CHANGELOG.md`. # References - [[X.680]] Abstract Syntax Notation One (ASN.1): Specification of basic notation. - [[X.690]] ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER). [X.680]: http://www.itu.int/rec/T-REC-X.680/en "Abstract Syntax Notation One (ASN.1): Specification of basic notation." [X.690]: https://www.itu.int/rec/T-REC-X.690/en "ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER)." [nom]: https://github.com/Geal/nom "Nom parser combinator framework" ## Changes See `CHANGELOG.md`, and `UPGRADING.md` for instructions for upgrading major versions. ## License Licensed under either of * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. rusticata-asn1-rs-07e764c/derive/000077500000000000000000000000001476576401200166265ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/derive/Cargo.lock000066400000000000000000000025511476576401200205360ustar00rootroot00000000000000# This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "asn1-rs-derive" version = "0.6.0" dependencies = [ "proc-macro2", "quote", "syn", "synstructure", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.96" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "synstructure" version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "unicode-ident" version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83" rusticata-asn1-rs-07e764c/derive/Cargo.toml000066400000000000000000000007121476576401200205560ustar00rootroot00000000000000[package] name = "asn1-rs-derive" version = "0.6.0" authors = ["Pierre Chifflier "] description = "Derive macros for the `asn1-rs` crate" license = "MIT OR Apache-2.0" homepage = "https://github.com/rusticata/asn1-rs" repository = "https://github.com/rusticata/asn1-rs.git" edition = "2018" [lib] proc-macro = true [dependencies] proc-macro2 = "1.0" quote = "1.0" syn = { version = "2.0", features = ["full"] } synstructure = "0.13" rusticata-asn1-rs-07e764c/derive/LICENSE-APACHE000077700000000000000000000000001476576401200227042../LICENSE-APACHEustar00rootroot00000000000000rusticata-asn1-rs-07e764c/derive/LICENSE-MIT000077700000000000000000000000001476576401200221242../LICENSE-MITustar00rootroot00000000000000rusticata-asn1-rs-07e764c/derive/src/000077500000000000000000000000001476576401200174155ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/derive/src/alias.rs000066400000000000000000000033121476576401200210530ustar00rootroot00000000000000use crate::container::*; use proc_macro2::Span; use quote::quote; use syn::{Data, Ident}; pub fn derive_ber_alias(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Alias), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); let impl_tryfrom = container.gen_tryfrom(); let impl_tagged = container.gen_tagged(); let ts = s.gen_impl(quote! { extern crate asn1_rs; #impl_tryfrom #impl_tagged }); if debug_derive { eprintln!("{}", ts); } ts } pub fn derive_der_alias(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Alias), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); let impl_tryfrom = container.gen_tryfrom(); let impl_tagged = container.gen_tagged(); let impl_checkconstraints = container.gen_checkconstraints(); let impl_fromder = container.gen_fromder(); let ts = s.gen_impl(quote! { extern crate asn1_rs; #impl_tryfrom #impl_tagged #impl_checkconstraints #impl_fromder }); if debug_derive { eprintln!("{}", ts); } ts } rusticata-asn1-rs-07e764c/derive/src/container.rs000066400000000000000000000452511476576401200217540ustar00rootroot00000000000000use proc_macro2::{Literal, Span, TokenStream}; use quote::{quote, ToTokens}; use syn::{ parse::ParseStream, parse_quote, spanned::Spanned, Attribute, DataStruct, DeriveInput, Field, Fields, Ident, Lifetime, LitInt, Meta, Type, WherePredicate, }; #[derive(Copy, Clone, Debug, PartialEq)] pub enum ContainerType { Alias, Sequence, Set, } impl ToTokens for ContainerType { fn to_tokens(&self, tokens: &mut TokenStream) { let s = match self { ContainerType::Alias => quote! {}, ContainerType::Sequence => quote! { asn1_rs::Tag::Sequence }, ContainerType::Set => quote! { asn1_rs::Tag::Set }, }; s.to_tokens(tokens) } } #[derive(Clone, Copy, Debug, PartialEq)] enum Asn1Type { Ber, Der, } #[derive(Copy, Clone, Debug, PartialEq)] pub enum Asn1TagKind { Explicit, Implicit, } impl ToTokens for Asn1TagKind { fn to_tokens(&self, tokens: &mut TokenStream) { let s = match self { Asn1TagKind::Explicit => quote! { asn1_rs::Explicit }, Asn1TagKind::Implicit => quote! { asn1_rs::Implicit }, }; s.to_tokens(tokens) } } #[derive(Copy, Clone, Debug, PartialEq)] pub enum Asn1TagClass { Universal, Application, ContextSpecific, Private, } impl ToTokens for Asn1TagClass { fn to_tokens(&self, tokens: &mut TokenStream) { let s = match self { Asn1TagClass::Application => quote! { asn1_rs::Class::APPLICATION }, Asn1TagClass::ContextSpecific => quote! { asn1_rs::Class::CONTEXT_SPECIFIC }, Asn1TagClass::Private => quote! { asn1_rs::Class::PRIVATE }, Asn1TagClass::Universal => quote! { asn1_rs::Class::UNIVERSAL }, }; s.to_tokens(tokens) } } pub struct Container { pub container_type: ContainerType, pub fields: Vec, pub where_predicates: Vec, pub error: Option, is_any: bool, } impl Container { pub fn from_datastruct( ds: &DataStruct, ast: &DeriveInput, container_type: ContainerType, ) -> Self { let mut is_any = false; match (container_type, &ds.fields) { (ContainerType::Alias, Fields::Unnamed(f)) => { if f.unnamed.len() != 1 { panic!("Alias: only tuple fields with one element are supported"); } match &f.unnamed[0].ty { Type::Path(type_path) if type_path .clone() .into_token_stream() .to_string() .starts_with("Any") => { is_any = true; } _ => (), } } (ContainerType::Alias, _) => panic!("BER/DER alias must be used with tuple strucs"), (_, Fields::Unnamed(_)) => panic!("BER/DER sequence cannot be used on tuple structs"), _ => (), } let fields = ds.fields.iter().map(FieldInfo::from).collect(); // get lifetimes from generics let lfts: Vec<_> = ast.generics.lifetimes().collect(); let mut where_predicates = Vec::new(); if !lfts.is_empty() { // input slice must outlive all lifetimes from Self let lft = Lifetime::new("'ber", Span::call_site()); let wh: WherePredicate = parse_quote! { #lft: #(#lfts)+* }; where_predicates.push(wh); }; // get custom attributes on container let error = ast .attrs .iter() .find(|attr| { attr.meta .path() .is_ident(&Ident::new("error", Span::call_site())) }) .cloned(); Container { container_type, fields, where_predicates, error, is_any, } } pub fn gen_tryfrom(&self) -> TokenStream { let field_names = &self.fields.iter().map(|f| &f.name).collect::>(); let parse_content = derive_ber_sequence_content(&self.fields, Asn1Type::Ber, self.error.is_some()); let lifetime = Lifetime::new("'ber", Span::call_site()); let wh = &self.where_predicates; let error = if let Some(attr) = &self.error { get_attribute_meta(attr).expect("Invalid error attribute format") } else { quote! { asn1_rs::Error } }; let fn_content = if self.container_type == ContainerType::Alias { // special case: is this an alias for Any if self.is_any { quote! { Ok(Self(any)) } } else { quote! { let res = TryFrom::try_from(any)?; Ok(Self(res)) } } } else { quote! { use asn1_rs::nom::*; any.tag().assert_eq(Self::TAG)?; // no need to parse sequence, we already have content let i = any.data; // #parse_content // let _ = i; // XXX check if empty? Ok(Self{#(#field_names),*}) } }; // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing // the `where` statement if there are none. quote! { use asn1_rs::{Any, FromBer}; use core::convert::TryFrom; gen impl<#lifetime> TryFrom> for @Self where #(#wh)+* { type Error = #error; fn try_from(any: Any<#lifetime>) -> asn1_rs::Result { #fn_content } } } } pub fn gen_tagged(&self) -> TokenStream { let tag = if self.container_type == ContainerType::Alias { // special case: is this an alias for Any if self.is_any { return quote! {}; } // find type of sub-item let ty = &self.fields[0].type_; quote! { <#ty as asn1_rs::Tagged>::TAG } } else { let container_type = self.container_type; quote! { #container_type } }; quote! { gen impl<'ber> asn1_rs::Tagged for @Self { const TAG: asn1_rs::Tag = #tag; } } } pub fn gen_checkconstraints(&self) -> TokenStream { let lifetime = Lifetime::new("'ber", Span::call_site()); let wh = &self.where_predicates; // let parse_content = derive_ber_sequence_content(&field_names, Asn1Type::Der); let fn_content = if self.container_type == ContainerType::Alias { // special case: is this an alias for Any if self.is_any { return quote! {}; } let ty = &self.fields[0].type_; quote! { any.tag().assert_eq(Self::TAG)?; <#ty>::check_constraints(any) } } else { let check_fields: Vec<_> = self .fields .iter() .map(|field| { let ty = &field.type_; quote! { let (rem, any) = Any::from_der(rem)?; <#ty as CheckDerConstraints>::check_constraints(&any)?; } }) .collect(); quote! { any.tag().assert_eq(Self::TAG)?; let rem = &any.data; #(#check_fields)* Ok(()) } }; // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing // the `where` statement if there are none. quote! { use asn1_rs::{CheckDerConstraints, Tagged}; gen impl<#lifetime> CheckDerConstraints for @Self where #(#wh)+* { fn check_constraints(any: &Any) -> asn1_rs::Result<()> { #fn_content } } } } pub fn gen_fromder(&self) -> TokenStream { let lifetime = Lifetime::new("'ber", Span::call_site()); let wh = &self.where_predicates; let field_names = &self.fields.iter().map(|f| &f.name).collect::>(); let parse_content = derive_ber_sequence_content(&self.fields, Asn1Type::Der, self.error.is_some()); let error = if let Some(attr) = &self.error { get_attribute_meta(attr).expect("Invalid error attribute format") } else { quote! { asn1_rs::Error } }; let fn_content = if self.container_type == ContainerType::Alias { // special case: is this an alias for Any if self.is_any { quote! { let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?; Ok((rem,Self(any))) } } else { quote! { let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?; any.header.assert_tag(Self::TAG).map_err(|e| asn1_rs::nom::Err::Error(e.into()))?; let res = TryFrom::try_from(any)?; Ok((rem,Self(res))) } } } else { quote! { let (rem, any) = asn1_rs::Any::from_der(bytes).map_err(asn1_rs::nom::Err::convert)?; any.header.assert_tag(Self::TAG).map_err(|e| asn1_rs::nom::Err::Error(e.into()))?; let i = any.data; // #parse_content // // let _ = i; // XXX check if empty? Ok((rem,Self{#(#field_names),*})) } }; // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing // the `where` statement if there are none. quote! { use asn1_rs::FromDer; gen impl<#lifetime> asn1_rs::FromDer<#lifetime, #error> for @Self where #(#wh)+* { fn from_der(bytes: &#lifetime [u8]) -> asn1_rs::ParseResult<#lifetime, Self, #error> { #fn_content } } } } pub fn gen_to_der_len(&self) -> TokenStream { let field_names = &self.fields.iter().map(|f| &f.name).collect::>(); let add_len_instructions = field_names.iter().fold(Vec::new(), |mut instrs, field| { instrs.push(quote! {total_len += self.#field.to_der_len()?;}); instrs }); quote! { fn to_der_len(&self) -> asn1_rs::Result { let mut total_len = 0; #(#add_len_instructions)* // now add header length computation if total_len < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + total_len) } else { // 1 (class+tag) + n (length) + len let n = asn1_rs::Length::Definite(total_len).to_der_len()?; Ok(1 + n + total_len) } } } } pub fn gen_write_der_header(&self) -> TokenStream { quote! { fn write_der_header(&self, writer: &mut dyn std::io::Write) -> asn1_rs::SerializeResult { let mut empty = std::io::empty(); let num_bytes = self.write_der_content(&mut empty)?; let header = asn1_rs::Header::new( asn1_rs::Class::Universal, true, asn1_rs::Sequence::TAG, asn1_rs::Length::Definite(num_bytes), ); header.write_der_header(writer).map_err(Into::into) } } } pub fn gen_write_der_content(&self) -> TokenStream { let field_names = &self.fields.iter().map(|f| &f.name).collect::>(); let write_instructions = field_names.iter().fold(Vec::new(), |mut instrs, field| { instrs.push(quote! {num_bytes += self.#field.write_der_header(writer)?;}); instrs.push(quote! {num_bytes += self.#field.write_der_content(writer)?;}); instrs }); quote! { fn write_der_content(&self, writer: &mut dyn std::io::Write) -> asn1_rs::SerializeResult { let mut num_bytes = 0; #(#write_instructions)* Ok(num_bytes) } } } } #[derive(Debug)] pub struct FieldInfo { pub name: Ident, pub type_: Type, pub default: Option, pub optional: bool, pub tag: Option<(Asn1TagKind, Asn1TagClass, u16)>, pub map_err: Option, } impl From<&Field> for FieldInfo { fn from(field: &Field) -> Self { // parse attributes and keep supported ones let mut optional = false; let mut tag = None; let mut map_err = None; let mut default = None; let name = field .ident .as_ref() .map_or_else(|| Ident::new("_", Span::call_site()), |s| s.clone()); for attr in &field.attrs { let ident = match attr.meta.path().get_ident() { Some(ident) => ident.to_string(), None => continue, }; match ident.as_str() { "map_err" => { let expr: syn::Expr = attr.parse_args().expect("could not parse map_err"); map_err = Some(quote! { #expr }); } "default" => { let expr: syn::Expr = attr.parse_args().expect("could not parse default"); default = Some(quote! { #expr }); optional = true; } "optional" => optional = true, "tag_explicit" => { if tag.is_some() { panic!("tag cannot be set twice!"); } let (class, value) = attr.parse_args_with(parse_tag_args).unwrap(); tag = Some((Asn1TagKind::Explicit, class, value)); } "tag_implicit" => { if tag.is_some() { panic!("tag cannot be set twice!"); } let (class, value) = attr.parse_args_with(parse_tag_args).unwrap(); tag = Some((Asn1TagKind::Implicit, class, value)); } // ignore unknown attributes _ => (), } } FieldInfo { name, type_: field.ty.clone(), default, optional, tag, map_err, } } } fn parse_tag_args(stream: ParseStream) -> Result<(Asn1TagClass, u16), syn::Error> { let tag_class: Option = stream.parse()?; let tag_class = if let Some(ident) = tag_class { let s = ident.to_string().to_uppercase(); match s.as_str() { "UNIVERSAL" => Asn1TagClass::Universal, "CONTEXT-SPECIFIC" => Asn1TagClass::ContextSpecific, "APPLICATION" => Asn1TagClass::Application, "PRIVATE" => Asn1TagClass::Private, _ => { return Err(syn::Error::new(stream.span(), "Invalid tag class")); } } } else { Asn1TagClass::ContextSpecific }; let lit: LitInt = stream.parse()?; let value = lit.base10_parse::()?; Ok((tag_class, value)) } fn derive_ber_sequence_content( fields: &[FieldInfo], asn1_type: Asn1Type, custom_errors: bool, ) -> TokenStream { let field_parsers: Vec<_> = fields .iter() .map(|f| get_field_parser(f, asn1_type, custom_errors)) .collect(); quote! { #(#field_parsers)* } } fn get_field_parser(f: &FieldInfo, asn1_type: Asn1Type, custom_errors: bool) -> TokenStream { let from = match asn1_type { Asn1Type::Ber => quote! {FromBer::from_ber}, Asn1Type::Der => quote! {FromDer::from_der}, }; let name = &f.name; let default = f .default .as_ref() // use a type hint, otherwise compiler will not know what type provides .unwrap_or .map(|x| quote! {let #name: Option<_> = #name; let #name = #name.unwrap_or(#x);}); let map_err = if let Some(tt) = f.map_err.as_ref() { if asn1_type == Asn1Type::Ber { Some(quote! { .map_err(|err| err.map(#tt)) .map_err(asn1_rs::from_nom_error::<_, Self::Error>) }) } else { // Some(quote! { .map_err(|err| nom::Err::convert(#tt)) }) Some(quote! { .map_err(|err| err.map(#tt)) }) } } else { // add mapping functions only if custom errors are used if custom_errors { if asn1_type == Asn1Type::Ber { Some(quote! { .map_err(asn1_rs::from_nom_error::<_, Self::Error>) }) } else { Some(quote! { .map_err(nom::Err::convert) }) } } else { None } }; if let Some((tag_kind, class, n)) = f.tag { let tag = Literal::u16_unsuffixed(n); // test if tagged + optional if f.optional { return quote! { let (i, #name) = { if i.is_empty() { (i, None) } else { let (_, header): (_, asn1_rs::Header) = #from(i)#map_err?; if header.tag().0 == #tag { let (i, t): (_, asn1_rs::TaggedValue::<_, _, #tag_kind, {#class}, #tag>) = #from(i)#map_err?; (i, Some(t.into_inner())) } else { (i, None) } } }; #default }; } else { // tagged, but not OPTIONAL return quote! { let (i, #name) = { let (i, t): (_, asn1_rs::TaggedValue::<_, _, #tag_kind, {#class}, #tag>) = #from(i)#map_err?; (i, t.into_inner()) }; #default }; } } else { // neither tagged nor optional quote! { let (i, #name) = #from(i)#map_err?; #default } } } fn get_attribute_meta(attr: &Attribute) -> Result { if let Meta::List(meta) = &attr.meta { let content = &meta.tokens; Ok(quote! { #content }) } else { Err(syn::Error::new( attr.span(), "Invalid error attribute format", )) } } rusticata-asn1-rs-07e764c/derive/src/lib.rs000066400000000000000000000026061476576401200205350ustar00rootroot00000000000000mod alias; mod container; mod sequence; mod set; mod tostatic; use alias::*; use sequence::*; use set::*; use tostatic::derive_tostatic; synstructure::decl_derive!([BerAlias, attributes( debug_derive, default, optional, tag_explicit, tag_implicit, error, map_err )] => derive_ber_alias); synstructure::decl_derive!([DerAlias, attributes( debug_derive, default, optional, tag_explicit, tag_implicit, error, map_err )] => derive_der_alias); synstructure::decl_derive!([BerSequence, attributes( debug_derive, default, optional, tag_explicit, tag_implicit, error, map_err )] => derive_ber_sequence); synstructure::decl_derive!([DerSequence, attributes( debug_derive, default, optional, tag_explicit, tag_implicit, error, map_err )] => derive_der_sequence); synstructure::decl_derive!([BerSet, attributes( debug_derive, default, optional, tag_explicit, tag_implicit, error, map_err )] => derive_ber_set); synstructure::decl_derive!([DerSet, attributes( debug_derive, default, optional, tag_explicit, tag_implicit, error, map_err )] => derive_der_set); synstructure::decl_derive!([ToStatic, attributes( debug_derive )] => derive_tostatic); synstructure::decl_derive!([ToDerSequence, attributes( debug_derive, )] => derive_toder_sequence); rusticata-asn1-rs-07e764c/derive/src/sequence.rs000066400000000000000000000063031476576401200215750ustar00rootroot00000000000000use crate::container::*; use proc_macro2::Span; use quote::quote; use syn::{Data, Ident, WherePredicate}; pub fn derive_ber_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); let impl_tryfrom = container.gen_tryfrom(); let impl_tagged = container.gen_tagged(); let ts = s.gen_impl(quote! { extern crate asn1_rs; #impl_tryfrom #impl_tagged }); if debug_derive { eprintln!("{}", ts); } ts } pub fn derive_der_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); let impl_tryfrom = container.gen_tryfrom(); let impl_tagged = container.gen_tagged(); let impl_checkconstraints = container.gen_checkconstraints(); let impl_fromder = container.gen_fromder(); let ts = s.gen_impl(quote! { extern crate asn1_rs; #impl_tryfrom #impl_tagged #impl_checkconstraints #impl_fromder }); if debug_derive { eprintln!("{}", ts); } ts } pub fn derive_toder_sequence(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Sequence), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); //let lifetime = Lifetime::new("'ber", Span::call_site()); let wh = &container.where_predicates; // we must filter out the 'ber lifetime (added for parsers, but not used here) let wh = wh.iter().filter(|predicate| match predicate { WherePredicate::Lifetime(lft) => lft.lifetime.ident != "ber", _ => true, }); let impl_to_der_len = container.gen_to_der_len(); let impl_write_der_header = container.gen_write_der_header(); let impl_write_der_content = container.gen_write_der_content(); // note: `gen impl` in synstructure takes care of appending extra where clauses if any, and removing // the `where` statement if there are none. let ts = s.gen_impl(quote! { extern crate asn1_rs; #[cfg(feature = "std")] gen impl asn1_rs::ToDer for @Self where #(#wh)+* { #impl_to_der_len #impl_write_der_header #impl_write_der_content } }); if debug_derive { eprintln!("{}", ts); } ts } rusticata-asn1-rs-07e764c/derive/src/set.rs000066400000000000000000000033021476576401200205540ustar00rootroot00000000000000use crate::container::*; use proc_macro2::Span; use quote::quote; use syn::{Data, Ident}; pub fn derive_ber_set(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Set), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); let impl_tryfrom = container.gen_tryfrom(); let impl_tagged = container.gen_tagged(); let ts = s.gen_impl(quote! { extern crate asn1_rs; #impl_tryfrom #impl_tagged }); if debug_derive { eprintln!("{}", ts); } ts } pub fn derive_der_set(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let container = match &ast.data { Data::Struct(ds) => Container::from_datastruct(ds, ast, ContainerType::Set), _ => panic!("Unsupported type, cannot derive"), }; let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); let impl_tryfrom = container.gen_tryfrom(); let impl_tagged = container.gen_tagged(); let impl_checkconstraints = container.gen_checkconstraints(); let impl_fromder = container.gen_fromder(); let ts = s.gen_impl(quote! { extern crate asn1_rs; #impl_tryfrom #impl_tagged #impl_checkconstraints #impl_fromder }); if debug_derive { eprintln!("{}", ts); } ts } rusticata-asn1-rs-07e764c/derive/src/tostatic.rs000066400000000000000000000036571476576401200216300ustar00rootroot00000000000000use proc_macro2::Span; use quote::quote; use syn::{Ident, Lifetime}; pub fn derive_tostatic(s: synstructure::Structure) -> proc_macro2::TokenStream { let ast = s.ast(); let debug_derive = ast.attrs.iter().any(|attr| { attr.meta .path() .is_ident(&Ident::new("debug_derive", Span::call_site())) }); // if deriving a struct, there will be only one variant // for enums, this will iterate on each variant let body = s.each_variant(|vi| { // bindings can be empty for unit variants let instrs = vi .bindings() .iter() .enumerate() .fold(quote! {}, |acc, (idx, bi)| { let ident = Ident::new(&format!("_{idx}"), Span::call_site()); quote! { #acc let #ident = #bi.to_static(); } }); // use construct() to handle possible cases (unit/named/unnamed) let c = vi.construct(|_f, i| { let ident = Ident::new(&format!("_{i}"), Span::call_site()); quote! { #ident } }); quote! { #instrs #c } }); let struct_ident = &ast.ident; // check if struct has lifetimes let static_token = match ast.generics.lifetimes().count() { 0 => None, 1 => { let lt = Lifetime::new("'static", Span::call_site()); Some(quote! {<#lt>}) } _ => { let lt_static = Lifetime::new("'static", Span::call_site()); let lts = ast.generics.lifetimes().map(|_| lt_static.clone()); Some(quote! {<#(#lts),*>}) } }; let ts = s.gen_impl(quote! { gen impl asn1_rs::ToStatic for @Self { type Owned = #struct_ident #static_token; fn to_static(&self) -> Self::Owned { match *self { #body } } } }); if debug_derive { eprintln!("TS: {ts}"); } ts } rusticata-asn1-rs-07e764c/doc/000077500000000000000000000000001476576401200161155ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/doc/DEBUG.md000066400000000000000000000037071476576401200172740ustar00rootroot00000000000000# Debugging To debug parsing errors, the `debug` feature can be used to print any error on stderr: ```shell $ ./fail_debug asn1_rs::asn1_types::sequence::Sequence ↯ Parsing failed at location: 00000000 30 81 04 00 00 0�... asn1_rs::asn1_types::sequence::Sequence ↯ T::from_der failed: Parsing requires 2 bytes/chars ``` In the above example, the parser tries to read a `Sequence` but input is incomplete (missing at least 2 bytes). The `debug` feature will print errors. To add the full trace of all parsing functions, use the `trace` feature: ```shell $ ./fail_trace u32 ⤷ T::from_der u32 ⤷ input (len=3, type=asn1_rs::asn1_types::any::Any) u32 ⤶ Parsed 3 bytes, 0 remaining u32 ⤷ Conversion to uint u32 ↯ T::from_der failed: Parsing Error: UnexpectedTag { expected: Some(Tag(2)), actual: Tag(1) } bool ↯ T::from_der failed: Parsing Error: DerConstraintFailed(InvalidBoolean) ``` In this example, the parser tries to read an `u32`. It is first read as `Any` with sucess, however the conversion to `u32` fails because of a wrong tag. See below for details on how to interpret output. Note that the `trace` feature is very verbose, and can generate a huge amount of logs on large inputs. ## Interpretating output When interpreting the trace output, knowing how `asn1-rs` works is useful. For most types, the following operations are done when parsing type `T`: - first, the object is parsed as `Any`: this is a very quick step, header is parsed, and object length is tested - next, DER constraint are tested for type `T` (if parsing DER) - finally, object is converted using `T::try_from(any)`. Other type-depdendant checks are done during this step. ## Examples and When writing a crate, the feature can be activated without changing the `Cargo.toml` file. For example, if you want to run `example/print-cert` with trace enabled: ```shell $ cargo run --features=asn1-rs/trace --example=print-cert -- ./assets/certificate.der ```rusticata-asn1-rs-07e764c/doc/DERIVE.md000066400000000000000000000223361476576401200174230ustar00rootroot00000000000000# BER/DER Custom Derive Attributes ## BER/DER Sequence parsers ### `BER` To derive a BER `SEQUENCE` parser, add the [`BerSequence`] derive attribute to an existing struct. Parsers will be derived automatically for all fields, which must implement the [`FromBer`] trait. For ex: ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, BerSequence)] pub struct S { a: u32, b: u16, c: u16, } # let parser = |input| -> Result<(), Error> { let (rest, result) = S::from_ber(input)?; # Ok(()) }; ``` After parsing b, any bytes that were leftover and not used to fill val will be returned in `rest`. When parsing a `SEQUENCE` into a struct, any trailing elements of the `SEQUENCE` that do not have matching fields in val will not be included in `rest`, as these are considered valid elements of the `SEQUENCE` and not trailing data. ### `DER` To derive a `DER` parser, use the [`DerSequence`] custom attribute. *Note: the `DerSequence` attributes derive both `BER` and `DER` parsers.* ## Tagged values ### `EXPLICIT` There are several ways of parsing tagged values: either using types like [`TaggedExplicit`], or using custom annotations. Using `TaggedExplicit` works as usual. The only drawback is that the type is visible in the structure, so accessing the value must be done using `.as_ref()` or `.into_inner()`: ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence)] pub struct S2 { a: u16, } // test with EXPLICIT Vec #[derive(Debug, PartialEq, DerSequence)] pub struct S { // a INTEGER a: u32, // b INTEGER b: u16, // c [0] EXPLICIT SEQUENCE OF S2 c: TaggedExplicit, Error, 0>, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; // Get a reference on c (type is &Vec) let ref_c = result.c.as_ref(); # Ok(()) }; ``` *Note: tags are context-specific by default. To specify other kind of tags (like `APPLICATION`) use [`TaggedValue`].* ### `tag_explicit` To "hide" the tag from the parser, the `tag_explicit` attribute is provided. This attribute must specify the tag value (as an integer), and will automatically wrap reading the value with the specified tag. ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence)] pub struct S { // a [0] EXPLICIT INTEGER #[tag_explicit(0)] a: u16, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; # Ok(()) }; ``` This method handles transparently the encapsulation and the read of the tagged value. *Note: tags are context-specific by default. To specify other kind of tags (like `APPLICATION`) add the tag class before the value in the `tag_explicit` attribute.* For ex: `tag_explicit(APPLICATION 0)` or `tag_explicit(PRIVATE 2)`. ### Tagged optional values The `optional` custom attribute can be used in addition of `tag_explicit` to specify that the value is `OPTIONAL`. The type of the annotated field member must be resolvable to `Option`. ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence)] pub struct S { // a [0] EXPLICIT INTEGER OPTIONAL #[tag_explicit(0)] #[optional] a: Option, // b INTEGER b: u16, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; # Ok(()) }; ``` ### `IMPLICIT` Tagged `IMPLICIT` values are handled similarly as for `EXPLICIT`, and can be parsed either using the [`TaggedImplicit`] type, or using the `tag_implicit` custom attribute. For ex: ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence)] pub struct S { // a [0] IMPLICIT INTEGER OPTIONAL #[tag_implicit(0)] #[optional] a: Option, // b INTEGER b: u16, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; # Ok(()) }; ``` ## `OPTIONAL` values (not tagged) The `optional` custom attribute can be specified to indicate the value is `OPTIONAL`. ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence)] pub struct S { // a INTEGER a: u16, // b INTEGER OPTIONAL #[optional] b: Option, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; # Ok(()) }; ``` **Important**: there are several limitations to this attribute. In particular, the parser is eager: when an `OPTIONAL` value of some type is followed by another value (not `OPTIONAL`) of the same type, this can create problem. If only one value is present, the parser will affect it to the first field, and then raise an error because the second is absent. Note that this does not concern tagged optional values (unless they have the same tag). ## `DEFAULT` The `default` custom attribute can be specified to indicate the value has a `DEFAULT` attribute. The value can also be marked as `OPTIONAL`, but this can be omitted. Since the value can always be obtained, the type should not be `Option`, but should use `T` directly. ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence)] #[debug_derive] pub struct S { // a INTEGER a: u16, // b INTEGER DEFAULT 0 #[default(0_u16)] b: u16, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; # Ok(()) }; ``` Limitations are the same as for `OPTIONAL` attribute. ## Debugging To help debugging the generated code, the `#[debug_derive]` attribute has been added. When this attribute is specified, the generated code will be printed to `stderr` during compilation. Example: ```rust use asn1_rs::*; #[derive(BerSequence)] #[debug_derive] struct S { a: u32, } ``` ## BER/DER Set parsers Parsing BER/DER `SET` objects is very similar to `SEQUENCE`. Use the [`BerSet`] and [`DerSet`] custom derive attributes on the structure, and everything else is exactly the same as for sequences (see above for documentation). Example: ```rust # use asn1_rs::*; use std::collections::BTreeSet; // `Ord` is needed because we will parse as a `BTreeSet` later #[derive(Debug, DerSet, PartialEq, Eq, PartialOrd, Ord)] pub struct S2 { a: u16, } // test with EXPLICIT Vec #[derive(Debug, PartialEq, DerSet)] pub struct S { // a INTEGER a: u32, // b INTEGER b: u16, // c [0] EXPLICIT SET OF S2 c: TaggedExplicit, Error, 0>, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_ber(input)?; // Get a reference on c (type is &BTreeSet) let ref_c = result.c.as_ref(); # Ok(()) }; ``` # Advanced ## Custom errors Derived parsers can use the `error` attribute to specify the error type of the parser. The custom error type must implement `From`, so the derived parsers will transparently convert errors using the [`Into`] trait. Example: ```rust # use asn1_rs::*; # #[derive(Debug, PartialEq)] pub enum MyError { NotYetImplemented, } impl From for MyError { fn from(_: asn1_rs::Error) -> Self { MyError::NotYetImplemented } } #[derive(DerSequence)] #[error(MyError)] pub struct T2 { pub a: u32, } ``` ## Mapping errors Sometimes, it is necessary to map the returned error to another type, for example when a subparser returns a different error type than the parser's, and the [`Into`] trait cannot be implemented. This is often used in combination with the `error` attribute, but can also be used alone. The `map_err` attribute can be used to specify a function or closure to map errors. The function signature is `fn (e1: E1) -> E2`. Example: ```rust # use asn1_rs::*; # #[derive(Debug, PartialEq)] pub enum MyError { NotYetImplemented, } impl From for MyError { fn from(_: asn1_rs::Error) -> Self { MyError::NotYetImplemented } } #[derive(DerSequence)] #[error(MyError)] pub struct T2 { pub a: u32, } // subparser returns an error of type MyError, // which is mapped to `Error` #[derive(DerSequence)] pub struct T4 { #[map_err(|_| Error::BerTypeError)] pub a: T2, } ``` *Note*: when deriving BER and DER parsers, errors paths are different (`TryFrom` returns the error type, while [`FromDer`] returns a [`ParseResult`]). Some code will be inserted by the `map_err` attribute to handle this transparently and keep the same function signature. # Serialization ## BER/DER Sequence serialization To serialize a struct to a DER `SEQUENCE`, add the [`ToDerSequence`] derive attribute to an existing struct. Serialization will be derived automatically for all fields, which must implement the [`ToDer`] trait. Some parser traits may be required, so also deriving parsers using [`DerSequence`] may be required. *Note*: serialization to BER is currently not available. In most cases, DER serialization should be enough. For ex: ```rust # use asn1_rs::*; #[derive(Debug, PartialEq, DerSequence, ToDerSequence)] pub struct S { a: u32, b: u16, c: u16, } let s = S { a: 1, b: 2, c: 3 }; let output = s.to_der_vec().expect("serialization failed"); let (_rest, result) = S::from_ber(&output).expect("parsing failed"); assert_eq!(s, result); ``` [`FromBer`]: crate::FromBer [`FromDer`]: crate::FromDer [`ToDer`]: crate::ToDer [`BerSequence`]: crate::BerSequence [`DerSequence`]: crate::DerSequence [`BerSet`]: crate::BerSet [`DerSet`]: crate::DerSet [`ToDerSequence`]: crate::ToDerSequence [`ParseResult`]: crate::ParseResult [`TaggedExplicit`]: crate::TaggedExplicit [`TaggedImplicit`]: crate::TaggedImplicit [`TaggedValue`]: crate::TaggedValue rusticata-asn1-rs-07e764c/doc/RECIPES.md000066400000000000000000000151421476576401200175340ustar00rootroot00000000000000# Documentation: BER/DER parsing recipes ## Builtin types Most builtin types can be parsed by calling the `from_der` or `from_der` functions (see `FromBer` and `FromDer` traits for documentation). For ex: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = ::from_der(input)?; # Ok(()) }; ``` Note: this crates makes extensive use of types annotation and turbofish operator, for example `::from_der()` or `TaggedExplicit::::from_der()`. See table B-3 in for reference on syntax. ## `SEQUENCE` and `SET` The `SEQUENCE` and `SET` types are handled very similarly, so recipes will be given for `SEQUENCE`, but can be adapted to `SET` by replacing words. ### Parsing `SEQUENCE` Usually, the sequence envelope does not need to be stored, so it just needs to be parsed to get the sequence content and parse it. The methods [`from_ber_and_then`](crate::Sequence::from_ber_and_then()) and [`from_der_and_then`](crate::Sequence::from_der_and_then()) provide helpers for that: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = Sequence::from_ber_and_then(input, |i| { // first item is INTEGER let (rem, a) = u32::from_der(input)?; // second item is OCTET STRING let (rem, b) = <&[u8]>::from_der(input)?; Ok((rem, (a, b))) })?; // result has type (u32, &[u8]) assert_eq!(result.0, 0); assert_eq!(result.1, b"\x00\x01"); # Ok(()) }; ``` ### Automatically deriving sequence parsers The [`BerSequence`](crate::BerSequence) and [`DerSequence`](crate::DerSequence) custom derive provide attributes to automatically derive a parser for a sequence. For ex: ```rust # use asn1_rs::*; #[derive(DerSequence)] pub struct S { a: u32, b: u16, c: u16, } # let parser = |input| -> Result<(), Error> { let (rem, result) = S::from_der(input)?; # Ok(()) }; ``` This will work for any field type that implements [`FromBer`](crate::FromBer) or [`FromDer`](crate::FromDer), respectively. See [`derive`](mod@derive) documentation for more examples and documentation. ### Parsing `SEQUENCE OF` `SEQUENCE OF T` can be parsed using either type `SequenceOf` or `Vec`: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = SequenceOf::::from_der(input)?; # Ok(()) }; ``` or ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = >::from_der(input)?; # Ok(()) }; ``` `SET OF T` can be parsed using either `SetOf`, `BTreeSet` or `HashSet`. ## `EXPLICIT` tagged values ### Parsing `EXPLICIT`, expecting a known tag If you expect only a specific tag, use `TaggedExplicit`. For ex, to parse a `[3] EXPLICIT INTEGER`: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = TaggedExplicit::::from_der(input)?; // result has type TaggedValue. Use `.as_ref()` or `.into_inner()` // to access content let tag = result.tag(); let class = result.class(); assert_eq!(result.as_ref(), &0); # Ok(()) }; ``` ### Specifying the class `TaggedExplicit` does not check the class, and accepts any class. It expects you to check the class after reading the value. To specify the class in the parser, use `TaggedValue`: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { // Note: the strange notation (using braces) is required by the compiler to use // a constant instead of the numeric value. let (rem, result) = TaggedValue::::from_der(input)?; # Ok(()) }; ``` Note that `TaggedExplicit` is a type alias to `TaggedValue`, so the objects are the same. ### Accepting any `EXPLICIT` tag To parse a value, accepting any class or tag, use `TaggedParser`. ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = TaggedParser::::from_der(input)?; // result has type TaggedParser. Use `.as_ref()` or `.into_inner()` // to access content let tag = result.tag(); let class = result.class(); assert_eq!(result.as_ref(), &0); # Ok(()) }; ``` ### Optional tagged values To parse optional tagged values, `Option>` can be used: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = Option::>::from_der(input)?; # Ok(()) }; ``` The type `OptTaggedExplicit` is also provided as an alias: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = OptTaggedExplicit::::from_der(input)?; # Ok(()) }; ``` ## `IMPLICIT` tagged values ### Parsing `IMPLICIT`, expecting a known tag If you expect only a specific tag, use `TaggedImplicit`. For ex, to parse a `[3] EXPLICIT INTEGER`: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = TaggedExplicit::::from_der(input)?; // result has type TaggedValue. Use `.as_ref()` or `.into_inner()` // to access content let tag = result.tag(); let class = result.class(); assert_eq!(result.as_ref(), &0); # Ok(()) }; ``` ### Specifying the class `TaggedImplicit` does not check the class, and accepts any class. It expects you to check the class after reading the value. To specify the class in the parser, use `TaggedValue`: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { // Note: the strange notation (using braces) is required by the compiler to use // a constant instead of the numeric value. let (rem, result) = TaggedValue::::from_der(input)?; # Ok(()) }; ``` Note that `TaggedImplicit` is a type alias to `TaggedValue`, so the objects are the same. ### Accepting any `IMPLICIT` tag To parse a value, accepting any class or tag, use `TaggedParser`. ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = TaggedParser::::from_der(input)?; // result has type TaggedParser. Use `.as_ref()` or `.into_inner()` // to access content let tag = result.tag(); let class = result.class(); assert_eq!(result.as_ref(), &0); # Ok(()) }; ``` ### Optional tagged values To parse optional tagged values, `Option>` can be used: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = Option::>::from_der(input)?; # Ok(()) }; ``` The type `OptTaggedImplicit` is also provided as an alias: ```rust # use asn1_rs::*; # let parser = |input| -> Result<(), Error> { let (rem, result) = OptTaggedImplicit::::from_der(input)?; # Ok(()) }; ``` rusticata-asn1-rs-07e764c/examples/000077500000000000000000000000001476576401200171665ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/examples/dump-der.rs000066400000000000000000000174101476576401200212540ustar00rootroot00000000000000use asn1_rs::{Any, Class, FromDer, Length, Result, Tag}; use colored::*; use nom::HexDisplay; // use oid_registry::{format_oid, Oid as DerOid, OidRegistry}; use std::cmp::min; use std::error::Error; use std::marker::PhantomData; use std::{env, fs}; struct Context<'a> { // oid_registry: OidRegistry<'a>, hex_max: usize, t: PhantomData<&'a ()>, } impl Default for Context<'_> { fn default() -> Self { // let oid_registry = OidRegistry::default().with_all_crypto().with_x509(); Context { // oid_registry, hex_max: 64, t: PhantomData, } } } #[macro_export] macro_rules! indent_println { ( $depth: expr, $fmt:expr ) => { println!(concat!("{:indent$}",$fmt), "", indent = 2*$depth) }; ( $depth: expr, $fmt:expr, $( $x:expr ),* ) => { println!(concat!("{:indent$}",$fmt), "", $($x),*, indent = 2*$depth) }; } #[allow(dead_code)] pub fn print_hex_dump(bytes: &[u8], max_len: usize) { let m = min(bytes.len(), max_len); print!("{}", &bytes[..m].to_hex(16)); if bytes.len() > max_len { println!("... "); } } fn main() -> std::result::Result<(), Box> { let ctx = Context::default(); for filename in env::args().skip(1) { eprintln!("File: {}", filename); let content = fs::read(&filename)?; // check for PEM file if filename.ends_with(".pem") || content.starts_with(b"----") { let pems = pem::parse_many(&content).expect("Parsing PEM failed"); if pems.is_empty() { eprintln!("{}", "No PEM section decoded".bright_red()); continue; } for (idx, pem) in pems.iter().enumerate() { eprintln!("Pem entry {} [{}]", idx, pem.tag().bright_blue()); print_der(pem.contents(), 1, &ctx); } } else { print_der(&content, 1, &ctx); } } Ok(()) } fn print_der(i: &[u8], depth: usize, ctx: &Context) { match Any::from_der(i) { Ok((rem, any)) => { print_der_any(any, depth, ctx); if !rem.is_empty() { let warning = format!("WARNING: {} extra bytes after object", rem.len()); indent_println!(depth, "{}", warning.bright_red()); print_hex_dump(rem, ctx.hex_max); } } Err(e) => { eprintln!("Error while parsing at depth {}: {:?}", depth, e); } } } fn print_der_result_any(r: Result, depth: usize, ctx: &Context) { match r { Ok(any) => print_der_any(any, depth, ctx), Err(e) => { eprintln!("Error while parsing at depth {}: {:?}", depth, e); } } } fn print_der_any(any: Any, depth: usize, ctx: &Context) { let class = match any.header.class() { Class::Universal => "UNIVERSAL".to_string().white(), c => c.to_string().cyan(), }; let hdr = format!( "[c:{} t:{}({}) l:{}]", class, any.header.tag().0, any.header.tag().to_string().white(), str_of_length(any.header.length()) ); indent_println!(depth, "{}", hdr); match any.header.class() { Class::Universal => (), Class::ContextSpecific | Class::Application => { // attempt to decode inner object (if EXPLICIT) match Any::from_der(any.data) { Ok((rem2, inner)) => { indent_println!( depth + 1, "{} (rem.len={})", format!("EXPLICIT [{}]", any.header.tag().0).green(), // any.header.tag.0, rem2.len() ); print_der_any(inner, depth + 2, ctx); } Err(_) => { // assume tagged IMPLICIT indent_println!( depth + 1, "{}", "could not decode (IMPLICIT tagging?)".bright_red() ); } } return; } _ => { indent_println!( depth + 1, "tagged: [{}] {}", any.header.tag().0, "*NOT SUPPORTED*".red() ); return; } } match any.header.tag() { Tag::BitString => { let b = any.bitstring().unwrap(); indent_println!(depth + 1, "BITSTRING"); print_hex_dump(b.as_ref(), ctx.hex_max); } Tag::Boolean => { let b = any.bool().unwrap(); indent_println!(depth + 1, "BOOLEAN: {}", b.to_string().green()); } Tag::EmbeddedPdv => { let e = any.embedded_pdv().unwrap(); indent_println!(depth + 1, "EMBEDDED PDV: {:?}", e); print_hex_dump(e.data_value, ctx.hex_max); } Tag::Enumerated => { let i = any.enumerated().unwrap(); indent_println!(depth + 1, "ENUMERATED: {}", i.0); } Tag::GeneralizedTime => { let s = any.generalizedtime().unwrap(); indent_println!(depth + 1, "GeneralizedTime: {}", s); } Tag::GeneralString => { let s = any.generalstring().unwrap(); indent_println!(depth + 1, "GeneralString: {}", s.as_ref()); } Tag::Ia5String => { let s = any.ia5string().unwrap(); indent_println!(depth + 1, "IA5String: {}", s.as_ref()); } Tag::Integer => { let i = any.integer().unwrap(); match i.as_i128() { Ok(i) => { indent_println!(depth + 1, "{}", i); } Err(_) => { print_hex_dump(i.as_ref(), ctx.hex_max); } } } Tag::Null => (), Tag::OctetString => { let b = any.octetstring().unwrap(); indent_println!(depth + 1, "OCTETSTRING"); print_hex_dump(b.as_ref(), ctx.hex_max); } Tag::Oid => { let oid = any.oid().unwrap(); // let der_oid = DerOid::new(oid.as_bytes().into()); // let s = format_oid(&der_oid, &ctx.oid_registry).cyan(); let s = oid.to_string().cyan(); indent_println!(depth + 1, "OID: {}", s); } Tag::PrintableString => { let s = any.printablestring().unwrap(); indent_println!(depth + 1, "PrintableString: {}", s.as_ref()); } Tag::RelativeOid => { let oid = any.oid().unwrap(); // let der_oid = DerOid::new(oid.as_bytes().into()); // let s = format_oid(&der_oid, &ctx.oid_registry).cyan(); let s = oid.to_string().cyan(); indent_println!(depth + 1, "RELATIVE-OID: {}", s); } Tag::Set => { let seq = any.set().unwrap(); for item in seq.der_iter::() { print_der_result_any(item, depth + 1, ctx); } } Tag::Sequence => { let seq = any.sequence().unwrap(); for item in seq.der_iter::() { print_der_result_any(item, depth + 1, ctx); } } Tag::UtcTime => { let s = any.utctime().unwrap(); indent_println!(depth + 1, "UtcTime: {}", s); } Tag::Utf8String => { let s = any.utf8string().unwrap(); indent_println!(depth + 1, "UTF-8: {}", s.as_ref()); } _ => unimplemented!("unsupported tag {}", any.header.tag()), } } fn str_of_length(l: Length) -> String { match l { Length::Definite(l) => l.to_string(), Length::Indefinite => "Indefinite".to_string(), } } rusticata-asn1-rs-07e764c/impl/000077500000000000000000000000001476576401200163115ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/impl/Cargo.toml000066400000000000000000000006241476576401200202430ustar00rootroot00000000000000[package] name = "asn1-rs-impl" version = "0.2.0" authors = ["Pierre Chifflier "] description = "Implementation details for the `asn1-rs` crate" license = "MIT OR Apache-2.0" homepage = "https://github.com/rusticata/asn1-rs" repository = "https://github.com/rusticata/asn1-rs.git" edition = "2018" [lib] proc-macro = true [dependencies] proc-macro2 = "1" quote = "1" syn = "2.0" rusticata-asn1-rs-07e764c/impl/LICENSE-APACHE000077700000000000000000000000001476576401200223672../LICENSE-APACHEustar00rootroot00000000000000rusticata-asn1-rs-07e764c/impl/LICENSE-MIT000077700000000000000000000000001476576401200216072../LICENSE-MITustar00rootroot00000000000000rusticata-asn1-rs-07e764c/impl/src/000077500000000000000000000000001476576401200171005ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/impl/src/lib.rs000066400000000000000000000074511476576401200202230ustar00rootroot00000000000000extern crate proc_macro; use proc_macro::{Span, TokenStream}; use syn::{parse_macro_input, Error, LitInt}; #[proc_macro] pub fn encode_oid(input: TokenStream) -> TokenStream { let token_stream = input.to_string(); let (s, relative) = if token_stream.starts_with("rel ") { (&token_stream[4..], true) } else { (token_stream.as_ref(), false) }; let items: Result, _> = s.split('.').map(|x| x.trim().parse::()).collect(); let mut items: &[_] = match items.as_ref() { Ok(v) => v.as_ref(), Err(_) => return create_error("Could not parse OID"), }; let mut v = Vec::new(); if !relative { if items.len() < 2 { if items.len() == 1 && items[0] == 0 { return "[0]".parse().unwrap(); } return create_error("Need at least two components for non-relative oid"); } if items[0] > 2 || items[1] > 39 { return create_error("First components are too big"); } let first_byte = (items[0] * 40 + items[1]) as u8; v.push(first_byte); items = &items[2..]; } for &int in items { let enc = encode_base128(int); v.extend_from_slice(&enc); } // "fn answer() -> u32 { 42 }".parse().unwrap() let mut s = String::with_capacity(2 + 6 * v.len()); s.push('['); for byte in v.iter() { s.insert_str(s.len(), &format!("0x{:02x}, ", byte)); } s.push(']'); s.parse().unwrap() } #[inline] fn create_error(msg: &str) -> TokenStream { let s = format!("Invalid OID({})", msg); Error::new(Span::call_site().into(), &s) .to_compile_error() .into() } // encode int as base128 fn encode_base128(int: u128) -> Vec { let mut val = int; let mut base128 = Vec::new(); let lo = val & 0x7f; base128.push(lo as u8); val >>= 7; loop { if val == 0 { base128.reverse(); return base128; } let lo = val & 0x7f; base128.push(lo as u8 | 0x80); val >>= 7; } } #[proc_macro] pub fn encode_int(input: TokenStream) -> TokenStream { let lit = parse_macro_input!(input as LitInt); match impl_encode_int(lit) { Ok(ts) => ts, Err(e) => e.to_compile_error().into(), } // let token_stream = input.to_string(); // let items: Result, _> = token_stream // .split('.') // .map(|x| x.trim().parse::()) // .collect(); // let err = Error::new(Span::call_site().into(), "invalid OID"); // if let Ok(items) = items { // let mut v = Vec::new(); // if items.len() < 2 || items[0] > 2 || items[1] > 39 { // return err.to_compile_error().into(); // } // let first_byte = (items[0] * 40 + items[1]) as u8; // v.push(first_byte); // for &int in &items[2..] { // let enc = encode_base128(int); // v.extend_from_slice(&enc); // } // // "fn answer() -> u32 { 42 }".parse().unwrap() // let mut s = String::with_capacity(2 + 6 * v.len()); // s.push('['); // for byte in v.iter() { // s.insert_str(s.len(), &format!("0x{:02x}, ", byte)); // } // s.push(']'); // s.parse().unwrap() // } else { // eprintln!("could not parse OID '{}'", token_stream); // err.to_compile_error().into() // } } fn impl_encode_int(lit: LitInt) -> Result { let value = lit.base10_parse::()?; let bytes = value.to_be_bytes(); let v: Vec<_> = bytes.iter().skip_while(|&c| *c == 0).collect(); let mut s = String::with_capacity(2 + 6 * v.len()); s.push('['); for byte in v.iter() { s.insert_str(s.len(), &format!("0x{:02x}, ", byte)); } s.push(']'); Ok(s.parse().unwrap()) } rusticata-asn1-rs-07e764c/impl/tests/000077500000000000000000000000001476576401200174535ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/impl/tests/test_oid.rs000066400000000000000000000005461476576401200216400ustar00rootroot00000000000000use asn1_rs_impl::{encode_int, encode_oid}; #[test] fn test_encode_oid() { // example from http://luca.ntop.org/Teaching/Appunti/asn1.html let oid = encode_oid! {1.2.840.113549}; assert_eq!(oid, [0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d]); } #[test] fn test_encode_int() { // let int = encode_int!(1234); assert_eq!(int, [0x04, 0xd2]); } rusticata-asn1-rs-07e764c/src/000077500000000000000000000000001476576401200161375ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/000077500000000000000000000000001476576401200202255ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/any.rs000066400000000000000000000404421476576401200213660ustar00rootroot00000000000000use crate::ber::*; use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; use core::convert::{TryFrom, TryInto}; use self::debug::trace; /// The `Any` object is not strictly an ASN.1 type, but holds a generic description of any object /// that could be encoded. /// /// It contains a header, and either a reference to or owned data for the object content. /// /// Note: this type is only provided in **borrowed** version (*i.e.* it cannot own the inner data). #[derive(Clone, Debug, PartialEq, Eq)] pub struct Any<'a> { /// The object header pub header: Header<'a>, /// The object contents pub data: &'a [u8], } impl<'a> Any<'a> { /// Create a new `Any` from BER/DER header and content #[inline] pub const fn new(header: Header<'a>, data: &'a [u8]) -> Self { Any { header, data } } /// Create a new `Any` from a tag, and BER/DER content #[inline] pub const fn from_tag_and_data(tag: Tag, data: &'a [u8]) -> Self { let constructed = matches!(tag, Tag::Sequence | Tag::Set); Any { header: Header { tag, constructed, class: Class::Universal, length: Length::Definite(data.len()), raw_tag: None, }, data, } } /// Return the `Class` of this object #[inline] pub const fn class(&self) -> Class { self.header.class } /// Update the class of the current object #[inline] pub fn with_class(self, class: Class) -> Self { Any { header: self.header.with_class(class), ..self } } /// Return the `Tag` of this object #[inline] pub const fn tag(&self) -> Tag { self.header.tag } /// Update the tag of the current object #[inline] pub fn with_tag(self, tag: Tag) -> Self { Any { header: self.header.with_tag(tag), data: self.data, } } /// Get the bytes representation of the *content* #[inline] pub fn as_bytes(&self) -> &'a [u8] { self.data } #[inline] pub fn parse_ber(&self) -> ParseResult<'a, T> where T: FromBer<'a>, { T::from_ber(self.data) } /// Parse a BER value and apply the provided parsing function to content /// /// After parsing, the sequence object and header are discarded. pub fn from_ber_and_then( class: Class, tag: u32, bytes: &'a [u8], op: F, ) -> ParseResult<'a, T, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>, E: From, { let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; any.tag() .assert_eq(Tag(tag)) .map_err(|e| Err::Error(e.into()))?; any.class() .assert_eq(class) .map_err(|e| Err::Error(e.into()))?; let (_, res) = op(any.data)?; Ok((rem, res)) } /// Parse a DER value and apply the provided parsing function to content /// /// After parsing, the sequence object and header are discarded. pub fn from_der_and_then( class: Class, tag: u32, bytes: &'a [u8], op: F, ) -> ParseResult<'a, T, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>, E: From, { let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; any.tag() .assert_eq(Tag(tag)) .map_err(|e| Err::Error(e.into()))?; any.class() .assert_eq(class) .map_err(|e| Err::Error(e.into()))?; let (_, res) = op(any.data)?; Ok((rem, res)) } #[inline] pub fn parse_der(&self) -> ParseResult<'a, T> where T: FromDer<'a>, { T::from_der(self.data) } /// Get the content following a BER header #[inline] pub fn parse_ber_content<'i>(i: &'i [u8], header: &'_ Header) -> ParseResult<'i, &'i [u8]> { header.parse_ber_content(i) } /// Get the content following a DER header #[inline] pub fn parse_der_content<'i>(i: &'i [u8], header: &'_ Header) -> ParseResult<'i, &'i [u8]> { header.assert_definite()?; DerParser::get_object_content(i, header, 8) } } macro_rules! impl_any_into { (IMPL $sname:expr, $fn_name:ident => $ty:ty, $asn1:expr) => { #[doc = "Attempt to convert object to `"] #[doc = $sname] #[doc = "` (ASN.1 type: `"] #[doc = $asn1] #[doc = "`)."] pub fn $fn_name(self) -> Result<$ty> { self.try_into() } }; ($fn_name:ident => $ty:ty, $asn1:expr) => { impl_any_into! { IMPL stringify!($ty), $fn_name => $ty, $asn1 } }; } macro_rules! impl_any_as { (IMPL $sname:expr, $fn_name:ident => $ty:ty, $asn1:expr) => { #[doc = "Attempt to create ASN.1 type `"] #[doc = $asn1] #[doc = "` from this object."] #[inline] pub fn $fn_name(&self) -> Result<$ty> { TryFrom::try_from(self) } }; ($fn_name:ident => $ty:ty, $asn1:expr) => { impl_any_as! { IMPL stringify!($ty), $fn_name => $ty, $asn1 } }; } impl<'a> Any<'a> { impl_any_into!(bitstring => BitString<'a>, "BIT STRING"); impl_any_into!(bmpstring => BmpString<'a>, "BMPString"); impl_any_into!(bool => bool, "BOOLEAN"); impl_any_into!(boolean => Boolean, "BOOLEAN"); impl_any_into!(embedded_pdv => EmbeddedPdv<'a>, "EMBEDDED PDV"); impl_any_into!(enumerated => Enumerated, "ENUMERATED"); impl_any_into!(generalizedtime => GeneralizedTime, "GeneralizedTime"); impl_any_into!(generalstring => GeneralString<'a>, "GeneralString"); impl_any_into!(graphicstring => GraphicString<'a>, "GraphicString"); impl_any_into!(i8 => i8, "INTEGER"); impl_any_into!(i16 => i16, "INTEGER"); impl_any_into!(i32 => i32, "INTEGER"); impl_any_into!(i64 => i64, "INTEGER"); impl_any_into!(i128 => i128, "INTEGER"); impl_any_into!(ia5string => Ia5String<'a>, "IA5String"); impl_any_into!(integer => Integer<'a>, "INTEGER"); impl_any_into!(null => Null, "NULL"); impl_any_into!(numericstring => NumericString<'a>, "NumericString"); impl_any_into!(objectdescriptor => ObjectDescriptor<'a>, "ObjectDescriptor"); impl_any_into!(octetstring => OctetString<'a>, "OCTET STRING"); impl_any_into!(oid => Oid<'a>, "OBJECT IDENTIFIER"); impl_any_into!(real => Real, "REAL"); /// Attempt to convert object to `Oid` (ASN.1 type: `RELATIVE-OID`). pub fn relative_oid(self) -> Result> { self.header.assert_tag(Tag::RelativeOid)?; let asn1 = Cow::Borrowed(self.data); Ok(Oid::new_relative(asn1)) } impl_any_into!(printablestring => PrintableString<'a>, "PrintableString"); // XXX REAL impl_any_into!(sequence => Sequence<'a>, "SEQUENCE"); impl_any_into!(set => Set<'a>, "SET"); impl_any_into!(str => &'a str, "UTF8String"); impl_any_into!(string => String, "UTF8String"); impl_any_into!(teletexstring => TeletexString<'a>, "TeletexString"); impl_any_into!(u8 => u8, "INTEGER"); impl_any_into!(u16 => u16, "INTEGER"); impl_any_into!(u32 => u32, "INTEGER"); impl_any_into!(u64 => u64, "INTEGER"); impl_any_into!(u128 => u128, "INTEGER"); impl_any_into!(universalstring => UniversalString<'a>, "UniversalString"); impl_any_into!(utctime => UtcTime, "UTCTime"); impl_any_into!(utf8string => Utf8String<'a>, "UTF8String"); impl_any_into!(videotexstring => VideotexString<'a>, "VideotexString"); impl_any_into!(visiblestring => VisibleString<'a>, "VisibleString"); impl_any_as!(as_bitstring => BitString, "BITSTRING"); impl_any_as!(as_bmpstring => BmpString, "BMPString"); impl_any_as!(as_bool => bool, "BOOLEAN"); impl_any_as!(as_boolean => Boolean, "BOOLEAN"); impl_any_as!(as_embedded_pdv => EmbeddedPdv, "EMBEDDED PDV"); impl_any_as!(as_endofcontent => EndOfContent, "END OF CONTENT (not a real ASN.1 type)"); impl_any_as!(as_enumerated => Enumerated, "ENUMERATED"); impl_any_as!(as_generalizedtime => GeneralizedTime, "GeneralizedTime"); impl_any_as!(as_generalstring => GeneralString, "GeneralString"); impl_any_as!(as_graphicstring => GraphicString, "GraphicString"); impl_any_as!(as_i8 => i8, "INTEGER"); impl_any_as!(as_i16 => i16, "INTEGER"); impl_any_as!(as_i32 => i32, "INTEGER"); impl_any_as!(as_i64 => i64, "INTEGER"); impl_any_as!(as_i128 => i128, "INTEGER"); impl_any_as!(as_ia5string => Ia5String, "IA5String"); impl_any_as!(as_integer => Integer, "INTEGER"); impl_any_as!(as_null => Null, "NULL"); impl_any_as!(as_numericstring => NumericString, "NumericString"); impl_any_as!(as_objectdescriptor => ObjectDescriptor, "OBJECT IDENTIFIER"); impl_any_as!(as_octetstring => OctetString, "OCTET STRING"); impl_any_as!(as_oid => Oid, "OBJECT IDENTIFIER"); impl_any_as!(as_real => Real, "REAL"); /// Attempt to create ASN.1 type `RELATIVE-OID` from this object. pub fn as_relative_oid(&self) -> Result> { self.header.assert_tag(Tag::RelativeOid)?; let asn1 = Cow::Borrowed(self.data); Ok(Oid::new_relative(asn1)) } impl_any_as!(as_printablestring => PrintableString, "PrintableString"); impl_any_as!(as_sequence => Sequence, "SEQUENCE"); impl_any_as!(as_set => Set, "SET"); impl_any_as!(as_str => &str, "UTF8String"); impl_any_as!(as_string => String, "UTF8String"); impl_any_as!(as_teletexstring => TeletexString, "TeletexString"); impl_any_as!(as_u8 => u8, "INTEGER"); impl_any_as!(as_u16 => u16, "INTEGER"); impl_any_as!(as_u32 => u32, "INTEGER"); impl_any_as!(as_u64 => u64, "INTEGER"); impl_any_as!(as_u128 => u128, "INTEGER"); impl_any_as!(as_universalstring => UniversalString, "UniversalString"); impl_any_as!(as_utctime => UtcTime, "UTCTime"); impl_any_as!(as_utf8string => Utf8String, "UTF8String"); impl_any_as!(as_videotexstring => VideotexString, "VideotexString"); impl_any_as!(as_visiblestring => VisibleString, "VisibleString"); /// Attempt to create an `Option` from this object. pub fn as_optional<'b, T>(&'b self) -> Result> where T: TryFrom<&'b Any<'a>, Error = Error>, 'a: 'b, { match TryFrom::try_from(self) { Ok(t) => Ok(Some(t)), Err(Error::UnexpectedTag { .. }) => Ok(None), Err(e) => Err(e), } } /// Attempt to create a tagged value (EXPLICIT) from this object. pub fn as_tagged_explicit( &self, ) -> Result, E> where T: FromBer<'a, E>, E: From, { TryFrom::try_from(self) } /// Attempt to create a tagged value (IMPLICIT) from this object. pub fn as_tagged_implicit( &self, ) -> Result, E> where T: TryFrom, Error = E>, T: Tagged, E: From, { TryFrom::try_from(self) } /// Attempt to get value as `str`, for all known string types /// /// This function does not allocate data, so it supports all string types except /// `UniversalString`. pub fn as_any_str(&self) -> Result { match self.tag() { Tag::GeneralString | Tag::GraphicString | Tag::Ia5String | Tag::NumericString | Tag::PrintableString | Tag::T61String | Tag::Utf8String | Tag::VideotexString | Tag::VisibleString => { let res = core::str::from_utf8(self.data)?; Ok(res.to_string()) } Tag::UniversalString => { let us = UniversalString::try_from(self)?; Ok(us.string()) } _ => todo!(), } } /// Attempt to get value as `String`, for all known string types /// /// This function allocates data pub fn as_any_string(&self) -> Result<&str> { match self.tag() { Tag::GeneralString | Tag::GraphicString | Tag::Ia5String | Tag::NumericString | Tag::PrintableString | Tag::T61String //| Tag::UniversalString // UCS-4, cannot be converted | Tag::Utf8String | Tag::VideotexString | Tag::VisibleString => { let res = core::str::from_utf8(self.data)?; Ok(res) } _ => todo!(), } } } pub(crate) fn parse_ber_any(input: &[u8]) -> ParseResult { let (i, header) = Header::from_ber(input)?; let (i, data) = BerParser::get_object_content(i, &header, MAX_RECURSION)?; Ok((i, Any { header, data })) } pub(crate) fn parse_der_any(input: &[u8]) -> ParseResult { let (i, header) = Header::from_der(input)?; // X.690 section 10.1: The definite form of length encoding shall be used header.length.assert_definite()?; let (i, data) = DerParser::get_object_content(i, &header, MAX_RECURSION)?; Ok((i, Any { header, data })) } impl<'a> FromBer<'a> for Any<'a> { #[inline] fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> { trace("Any", parse_ber_any, bytes) } } impl<'a> FromDer<'a> for Any<'a> { #[inline] fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { trace("Any", parse_der_any, bytes) } } impl CheckDerConstraints for Any<'_> { fn check_constraints(any: &Any) -> Result<()> { any.header.length().assert_definite()?; // if len < 128, must use short form (10.1: minimum number of octets) Ok(()) } } impl DerAutoDerive for Any<'_> {} impl DynTagged for Any<'_> { fn tag(&self) -> Tag { self.tag() } } // impl<'a> ToStatic for Any<'a> { // type Owned = Any<'static>; // fn to_static(&self) -> Self::Owned { // Any { // header: self.header.to_static(), // data: Cow::Owned(self.data.to_vec()), // } // } // } #[cfg(feature = "std")] impl ToDer for Any<'_> { fn to_der_len(&self) -> Result { let hdr_len = self.header.to_der_len()?; Ok(hdr_len + self.data.len()) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // create fake header to have correct length let header = Header::new( self.header.class, self.header.constructed, self.header.tag, Length::Definite(self.data.len()), ); let sz = header.write_der_header(writer)?; Ok(sz) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(self.data).map_err(Into::into) } /// Similar to using `to_der`, but uses header without computing length value fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let sz = self.header.write_der_header(writer)?; let sz = sz + writer.write(self.data)?; Ok(sz) } } #[cfg(test)] mod tests { use crate::*; use hex_literal::hex; #[test] fn methods_any() { let header = Header::new_simple(Tag::Integer); let any = Any::new(header, &[]) .with_class(Class::ContextSpecific) .with_tag(Tag(0)); assert_eq!(any.as_bytes(), &[]); let input = &hex! {"80 03 02 01 01"}; let (_, any) = Any::from_ber(input).expect("parsing failed"); let (_, r) = any.parse_ber::().expect("parse_ber failed"); assert_eq!(r.as_u32(), Ok(1)); let (_, r) = any.parse_der::().expect("parse_der failed"); assert_eq!(r.as_u32(), Ok(1)); let header = &any.header; let (_, content) = Any::parse_ber_content(&input[2..], header).unwrap(); assert_eq!(content.len(), 3); let (_, content) = Any::parse_der_content(&input[2..], header).unwrap(); assert_eq!(content.len(), 3); let (_, any) = Any::from_der(&input[2..]).unwrap(); Any::check_constraints(&any).unwrap(); assert_eq!(::tag(&any), any.tag()); let int = any.integer().unwrap(); assert_eq!(int.as_u16(), Ok(1)); } } rusticata-asn1-rs-07e764c/src/asn1_types/bitstring.rs000066400000000000000000000105551476576401200226060ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; #[cfg(feature = "bits")] use bitvec::{order::Msb0, slice::BitSlice}; use core::convert::TryFrom; /// ASN.1 `BITSTRING` type #[derive(Clone, Debug, Eq, PartialEq)] pub struct BitString<'a> { pub unused_bits: u8, pub data: Cow<'a, [u8]>, } impl<'a> BitString<'a> { // Length must be >= 1 (first byte is number of ignored bits) pub const fn new(unused_bits: u8, s: &'a [u8]) -> Self { BitString { unused_bits, data: Cow::Borrowed(s), } } /// Test if bit `bitnum` is set pub fn is_set(&self, bitnum: usize) -> bool { let byte_pos = bitnum / 8; if byte_pos >= self.data.len() { return false; } let b = 7 - (bitnum % 8); (self.data[byte_pos] & (1 << b)) != 0 } /// Constructs a shared `&BitSlice` reference over the object data. #[cfg(feature = "bits")] pub fn as_bitslice(&self) -> Option<&BitSlice> { BitSlice::<_, Msb0>::try_from_slice(&self.data).ok() } } impl AsRef<[u8]> for BitString<'_> { fn as_ref(&self) -> &[u8] { &self.data } } impl<'a> TryFrom> for BitString<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result> { TryFrom::try_from(&any) } } // non-consuming version impl<'a, 'b> TryFrom<&'b Any<'a>> for BitString<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; if any.data.is_empty() { return Err(Error::InvalidLength); } let s = any.data; let (unused_bits, data) = (s[0], Cow::Borrowed(&s[1..])); Ok(BitString { unused_bits, data }) } } impl CheckDerConstraints for BitString<'_> { fn check_constraints(any: &Any) -> Result<()> { // X.690 section 10.2 any.header.assert_primitive()?; // Check that padding bits are all 0 (X.690 section 11.2.1) match any.data.len() { 0 => Err(Error::InvalidLength), 1 => { // X.690 section 11.2.2 Note 2 if any.data[0] == 0 { Ok(()) } else { Err(Error::InvalidLength) } } len => { let unused_bits = any.data[0]; let last_byte = any.data[len - 1]; if last_byte.trailing_zeros() < unused_bits as u32 { return Err(Error::DerConstraintFailed(DerConstraint::UnusedBitsNotZero)); } Ok(()) } } } } impl DerAutoDerive for BitString<'_> {} impl Tagged for BitString<'_> { const TAG: Tag = Tag::BitString; } #[cfg(feature = "std")] impl ToDer for BitString<'_> { fn to_der_len(&self) -> Result { let sz = self.data.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + 1 (unused bits) + len Ok(3 + sz) } else { // 1 (class+tag) + n (length) + 1 (unused bits) + len let n = Length::Definite(sz + 1).to_der_len()?; Ok(2 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(1 + self.data.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let sz = writer.write(&[self.unused_bits])?; let sz = sz + writer.write(&self.data)?; Ok(sz) } } #[cfg(test)] mod tests { use super::BitString; #[test] fn test_bitstring_is_set() { let obj = BitString::new(0, &[0x0f, 0x00, 0x40]); assert!(!obj.is_set(0)); assert!(obj.is_set(7)); assert!(!obj.is_set(9)); assert!(obj.is_set(17)); } #[cfg(feature = "bits")] #[test] fn test_bitstring_to_bitvec() { let obj = BitString::new(0, &[0x0f, 0x00, 0x40]); let bv = obj.as_bitslice().expect("could not get bitslice"); assert_eq!(bv.get(0).as_deref(), Some(&false)); assert_eq!(bv.get(7).as_deref(), Some(&true)); assert_eq!(bv.get(9).as_deref(), Some(&false)); assert_eq!(bv.get(17).as_deref(), Some(&true)); } } rusticata-asn1-rs-07e764c/src/asn1_types/boolean.rs000066400000000000000000000076001476576401200222150ustar00rootroot00000000000000use crate::*; use core::convert::TryFrom; /// ASN.1 `BOOLEAN` type /// /// BER objects consider any non-zero value as `true`, and `0` as `false`. /// /// DER objects must use value `0x0` (`false`) or `0xff` (`true`). #[derive(Debug, PartialEq, Eq)] pub struct Boolean { pub value: u8, } impl Boolean { /// `BOOLEAN` object for value `false` pub const FALSE: Boolean = Boolean::new(0); /// `BOOLEAN` object for value `true` pub const TRUE: Boolean = Boolean::new(0xff); /// Create a new `Boolean` from the provided logical value. #[inline] pub const fn new(value: u8) -> Self { Boolean { value } } /// Return the `bool` value from this object. #[inline] pub const fn bool(&self) -> bool { self.value != 0 } } impl<'a> TryFrom> for Boolean { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } // non-consuming version impl<'a, 'b> TryFrom<&'b Any<'a>> for Boolean { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; // X.690 section 8.2.1: // The encoding of a boolean value shall be primitive. The contents octets shall consist of a single octet if any.header.length != Length::Definite(1) { return Err(Error::InvalidLength); } let value = any.data[0]; Ok(Boolean { value }) } } impl CheckDerConstraints for Boolean { fn check_constraints(any: &Any) -> Result<()> { let c = any.data[0]; // X.690 section 11.1 if !(c == 0 || c == 0xff) { return Err(Error::DerConstraintFailed(DerConstraint::InvalidBoolean)); } Ok(()) } } impl DerAutoDerive for Boolean {} impl Tagged for Boolean { const TAG: Tag = Tag::Boolean; } #[cfg(feature = "std")] impl ToDer for Boolean { fn to_der_len(&self) -> Result { // 3 = 1 (tag) + 1 (length) + 1 (value) Ok(3) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let b = if self.value != 0 { 0xff } else { 0x00 }; writer.write(&[b]).map_err(Into::into) } /// Similar to using `to_der`, but uses header without computing length value fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let sz = writer.write(&[Self::TAG.0 as u8, 0x01, self.value])?; Ok(sz) } } impl<'a> TryFrom> for bool { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for bool { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; let b = Boolean::try_from(any)?; Ok(b.bool()) } } impl CheckDerConstraints for bool { fn check_constraints(any: &Any) -> Result<()> { let c = any.data[0]; // X.690 section 11.1 if !(c == 0 || c == 0xff) { return Err(Error::DerConstraintFailed(DerConstraint::InvalidBoolean)); } Ok(()) } } impl DerAutoDerive for bool {} impl Tagged for bool { const TAG: Tag = Tag::Boolean; } #[cfg(feature = "std")] impl ToDer for bool { fn to_der_len(&self) -> Result { // 3 = 1 (tag) + 1 (length) + 1 (value) Ok(3) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let b = if *self { 0xff } else { 0x00 }; writer.write(&[b]).map_err(Into::into) } } rusticata-asn1-rs-07e764c/src/asn1_types/choice.rs000066400000000000000000000007611476576401200220310ustar00rootroot00000000000000use crate::{FromBer, FromDer, Tag, Tagged}; pub trait Choice { /// Is the provided [`Tag`] decodable as a variant of this `CHOICE`? fn can_decode(tag: Tag) -> bool; } /// This blanket impl allows any [`Tagged`] type to function as a [`Choice`] /// with a single alternative. impl Choice for T where T: Tagged, { fn can_decode(tag: Tag) -> bool { T::TAG == tag } } pub trait BerChoice<'a>: Choice + FromBer<'a> {} pub trait DerChoice<'a>: Choice + FromDer<'a> {} rusticata-asn1-rs-07e764c/src/asn1_types/embedded_pdv.rs000066400000000000000000000102671476576401200232030ustar00rootroot00000000000000use crate::*; use core::convert::TryFrom; #[derive(Debug, PartialEq, Eq)] pub struct EmbeddedPdv<'a> { pub identification: PdvIdentification<'a>, pub data_value_descriptor: Option>, pub data_value: &'a [u8], } #[derive(Debug, PartialEq, Eq)] pub enum PdvIdentification<'a> { Syntaxes { s_abstract: Oid<'a>, s_transfer: Oid<'a>, }, Syntax(Oid<'a>), PresentationContextId(Integer<'a>), ContextNegotiation { presentation_context_id: Integer<'a>, presentation_syntax: Oid<'a>, }, TransferSyntax(Oid<'a>), Fixed, } impl<'a> TryFrom> for EmbeddedPdv<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for EmbeddedPdv<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { let data = any.data; // AUTOMATIC TAGS means all values will be tagged (IMPLICIT) // [0] -> identification let (rem, seq0) = TaggedParser::::parse_ber(Class::ContextSpecific, Tag(0), data)?; let inner = seq0.inner; let identification = match inner.tag() { Tag(0) => { // syntaxes SEQUENCE { // abstract OBJECT IDENTIFIER, // transfer OBJECT IDENTIFIER // }, // AUTOMATIC tags -> implicit! Hopefully, Oid does not check tag value! let (rem, s_abstract) = Oid::from_ber(inner.data)?; let (_, s_transfer) = Oid::from_ber(rem)?; PdvIdentification::Syntaxes { s_abstract, s_transfer, } } Tag(1) => { // syntax OBJECT IDENTIFIER let oid = Oid::new(inner.data.into()); PdvIdentification::Syntax(oid) } Tag(2) => { // presentation-context-id INTEGER let i = Integer::new(inner.data); PdvIdentification::PresentationContextId(i) } Tag(3) => { // context-negotiation SEQUENCE { // presentation-context-id INTEGER, // transfer-syntax OBJECT IDENTIFIER // }, // AUTOMATIC tags -> implicit! let (rem, any) = Any::from_ber(inner.data)?; let presentation_context_id = Integer::new(any.data); let (_, presentation_syntax) = Oid::from_ber(rem)?; PdvIdentification::ContextNegotiation { presentation_context_id, presentation_syntax, } } Tag(4) => { // transfer-syntax OBJECT IDENTIFIER let oid = Oid::new(inner.data.into()); PdvIdentification::TransferSyntax(oid) } Tag(5) => { // fixed NULL PdvIdentification::Fixed } _ => { return Err(inner .tag() .invalid_value("Invalid identification tag in EMBEDDED PDV")) } }; // [1] -> data-value-descriptor ObjectDescriptor OPTIONAL // *BUT* WITH COMPONENTS data-value-descriptor ABSENT // XXX this should be parse_ber? // let (rem, data_value_descriptor) = // TaggedOptional::from(1).parse_der(rem, |_, inner| ObjectDescriptor::from_ber(inner))?; let (rem, data_value_descriptor) = (rem, None); // [2] -> data-value OCTET STRING let (_, data_value) = TaggedParser::::parse_ber(Class::ContextSpecific, Tag(2), rem)?; let data_value = data_value.inner; let obj = EmbeddedPdv { identification, data_value_descriptor, data_value, }; Ok(obj) } } impl CheckDerConstraints for EmbeddedPdv<'_> { fn check_constraints(any: &Any) -> Result<()> { any.header.length().assert_definite()?; any.header.assert_constructed()?; Ok(()) } } impl DerAutoDerive for EmbeddedPdv<'_> {} rusticata-asn1-rs-07e764c/src/asn1_types/end_of_content.rs000066400000000000000000000030411476576401200235550ustar00rootroot00000000000000use crate::{Any, Error, Result, Tag, Tagged}; use core::convert::TryFrom; /// End-of-contents octets /// /// `EndOfContent` is not a BER type, but represents a marked to indicate the end of contents /// of an object, when the length is `Indefinite` (see X.690 section 8.1.5). /// /// This type cannot exist in DER, and so provides no `FromDer`/`ToDer` implementation. #[derive(Debug)] pub struct EndOfContent {} impl Default for EndOfContent { fn default() -> Self { Self::new() } } impl EndOfContent { pub const fn new() -> Self { EndOfContent {} } } impl<'a> TryFrom> for EndOfContent { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for EndOfContent { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; if !any.header.length.is_null() { return Err(Error::InvalidLength); } Ok(EndOfContent {}) } } impl Tagged for EndOfContent { const TAG: Tag = Tag::EndOfContent; } // impl ToDer for EndOfContent { // fn to_der_len(&self) -> Result { // Ok(2) // } // fn write_der_header(&self, writer: &mut dyn std::io::Write) -> crate::SerializeResult { // writer.write(&[Self::TAG.0 as u8, 0x00]).map_err(Into::into) // } // fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> crate::SerializeResult { // Ok(0) // } // } rusticata-asn1-rs-07e764c/src/asn1_types/enumerated.rs000066400000000000000000000034541476576401200227320ustar00rootroot00000000000000use crate::ber::bytes_to_u64; use crate::*; use core::convert::TryFrom; /// ASN.1 `ENUMERATED` type /// /// # Limitations /// /// Supported values are limited to 0 .. 2^32 #[derive(Debug, PartialEq, Eq)] pub struct Enumerated(pub u32); impl Enumerated { pub const fn new(value: u32) -> Self { Enumerated(value) } } impl<'a> TryFrom> for Enumerated { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Enumerated { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; let res_u64 = bytes_to_u64(any.data)?; if res_u64 > (::MAX as u64) { return Err(Error::IntegerTooLarge); } let value = res_u64 as u32; Ok(Enumerated(value)) } } impl CheckDerConstraints for Enumerated { fn check_constraints(any: &Any) -> Result<()> { any.header.length.assert_definite()?; Ok(()) } } impl DerAutoDerive for Enumerated {} impl Tagged for Enumerated { const TAG: Tag = Tag::Enumerated; } #[cfg(feature = "std")] impl ToDer for Enumerated { fn to_der_len(&self) -> Result { Integer::from(self.0).to_der_len() } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let i = Integer::from(self.0); let len = i.data.len(); let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(self.0); int.write_der_content(writer) } } rusticata-asn1-rs-07e764c/src/asn1_types/generalizedtime.rs000066400000000000000000000261521476576401200237510ustar00rootroot00000000000000use crate::*; use alloc::format; #[cfg(not(feature = "std"))] use alloc::string::String; use core::convert::TryFrom; use core::fmt; #[cfg(feature = "datetime")] use time::OffsetDateTime; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct GeneralizedTime(pub ASN1DateTime); impl GeneralizedTime { pub const fn new(datetime: ASN1DateTime) -> Self { GeneralizedTime(datetime) } pub fn from_bytes(bytes: &[u8]) -> Result { // X.680 section 42 defines a GeneralizedTime as a VisibleString restricted to: // // a) a string representing the calendar date, as specified in ISO 8601, with a four-digit representation of the // year, a two-digit representation of the month and a two-digit representation of the day, without use of // separators, followed by a string representing the time of day, as specified in ISO 8601, without separators // other than decimal comma or decimal period (as provided for in ISO 8601), and with no terminating Z (as // provided for in ISO 8601); or // b) the characters in a) above followed by an upper-case letter Z ; or // c) he characters in a) above followed by a string representing a local time differential, as specified in // ISO 8601, without separators. let (year, month, day, hour, minute, rem) = match bytes { [year1, year2, year3, year4, mon1, mon2, day1, day2, hour1, hour2, min1, min2, rem @ ..] => { let year_hi = decode_decimal(Self::TAG, *year1, *year2)?; let year_lo = decode_decimal(Self::TAG, *year3, *year4)?; let year = (year_hi as u32) * 100 + (year_lo as u32); let month = decode_decimal(Self::TAG, *mon1, *mon2)?; let day = decode_decimal(Self::TAG, *day1, *day2)?; let hour = decode_decimal(Self::TAG, *hour1, *hour2)?; let minute = decode_decimal(Self::TAG, *min1, *min2)?; (year, month, day, hour, minute, rem) } _ => return Err(Self::TAG.invalid_value("malformed time string (not yymmddhhmm)")), }; if rem.is_empty() { return Err(Self::TAG.invalid_value("malformed time string")); } // check for seconds let (second, rem) = match rem { [sec1, sec2, rem @ ..] => { let second = decode_decimal(Self::TAG, *sec1, *sec2)?; (second, rem) } _ => (0, rem), }; if month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59 { // eprintln!("GeneralizedTime: time checks failed"); // eprintln!(" month:{}", month); // eprintln!(" day:{}", day); // eprintln!(" hour:{}", hour); // eprintln!(" minute:{}", minute); // eprintln!(" second:{}", second); return Err(Self::TAG.invalid_value("time components with invalid values")); } if rem.is_empty() { // case a): no fractional seconds part, and no terminating Z return Ok(GeneralizedTime(ASN1DateTime::new( year, month, day, hour, minute, second, None, ASN1TimeZone::Undefined, ))); } // check for fractional seconds let (millisecond, rem) = match rem { [b'.' | b',', rem @ ..] => { let mut fsecond = 0; let mut rem = rem; let mut digits = 0; for idx in 0..=4 { if rem.is_empty() { if idx == 0 { // dot or comma, but no following digit return Err(Self::TAG.invalid_value( "malformed time string (dot or comma but no digits)", )); } digits = idx; break; } if idx == 4 { return Err( Self::TAG.invalid_value("malformed time string (invalid milliseconds)") ); } match rem[0] { b'0'..=b'9' => { // cannot overflow, max 4 digits will be read fsecond = fsecond * 10 + (rem[0] - b'0') as u16; } b'Z' | b'+' | b'-' => { digits = idx; break; } _ => { return Err(Self::TAG.invalid_value( "malformed time string (invalid milliseconds/timezone)", )) } } rem = &rem[1..]; } // fix fractional seconds depending on the number of digits // for ex, date "xxxx.3" means 3000 milliseconds, not 3 let fsecond = match digits { 1 => fsecond * 100, 2 => fsecond * 10, _ => fsecond, }; (Some(fsecond), rem) } _ => (None, rem), }; // check timezone if rem.is_empty() { // case a): fractional seconds part, and no terminating Z return Ok(GeneralizedTime(ASN1DateTime::new( year, month, day, hour, minute, second, millisecond, ASN1TimeZone::Undefined, ))); } let tz = match rem { [b'Z'] => ASN1TimeZone::Z, [b'+', h1, h2, m1, m2] => { let hh = decode_decimal(Self::TAG, *h1, *h2)?; let mm = decode_decimal(Self::TAG, *m1, *m2)?; ASN1TimeZone::Offset(hh as i8, mm as i8) } [b'-', h1, h2, m1, m2] => { let hh = decode_decimal(Self::TAG, *h1, *h2)?; let mm = decode_decimal(Self::TAG, *m1, *m2)?; ASN1TimeZone::Offset(-(hh as i8), mm as i8) } _ => return Err(Self::TAG.invalid_value("malformed time string: no time zone")), }; Ok(GeneralizedTime(ASN1DateTime::new( year, month, day, hour, minute, second, millisecond, tz, ))) } /// Return a ISO 8601 combined date and time with time zone. #[cfg(feature = "datetime")] #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] pub fn utc_datetime(&self) -> Result { self.0.to_datetime() } } impl<'a> TryFrom> for GeneralizedTime { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for GeneralizedTime { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; #[allow(clippy::trivially_copy_pass_by_ref)] fn is_visible(b: &u8) -> bool { 0x20 <= *b && *b <= 0x7f } if !any.data.iter().all(is_visible) { return Err(Error::StringInvalidCharset); } GeneralizedTime::from_bytes(any.data) } } impl fmt::Display for GeneralizedTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let dt = &self.0; let fsec = match self.0.millisecond { Some(v) => format!(".{}", v), None => String::new(), }; match dt.tz { ASN1TimeZone::Undefined => write!( f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec ), ASN1TimeZone::Z => write!( f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}Z", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec ), ASN1TimeZone::Offset(hh, mm) => { let (s, hh) = if hh > 0 { ('+', hh) } else { ('-', -hh) }; write!( f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}{}{:02}{:02}", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, fsec, s, hh, mm ) } } } } impl CheckDerConstraints for GeneralizedTime { fn check_constraints(any: &Any) -> Result<()> { // X.690 section 11.7.1: The encoding shall terminate with a "Z" if any.data.last() != Some(&b'Z') { return Err(Error::DerConstraintFailed(DerConstraint::MissingTimeZone)); } // X.690 section 11.7.2: The seconds element shall always be present. // XXX // X.690 section 11.7.4: The decimal point element, if present, shall be the point option "." if any.data.contains(&b',') { return Err(Error::DerConstraintFailed(DerConstraint::MissingSeconds)); } Ok(()) } } impl DerAutoDerive for GeneralizedTime {} impl Tagged for GeneralizedTime { const TAG: Tag = Tag::GeneralizedTime; } #[cfg(feature = "std")] impl ToDer for GeneralizedTime { fn to_der_len(&self) -> Result { // data: // - 8 bytes for YYYYMMDD // - 6 for hhmmss in DER (X.690 section 11.7.2) // - (variable) the fractional part, without trailing zeros, with a point "." // - 1 for the character Z in DER (X.690 section 11.7.1) // data length: 15 + fractional part // // thus, length will always be on 1 byte (short length) and // class+structure+tag also on 1 // // total: = 1 (class+constructed+tag) + 1 (length) + 15 + fractional let num_digits = match self.0.millisecond { None => 0, Some(v) => 1 + v.to_string().len(), }; Ok(2 + 15 + num_digits) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // see above for length value let num_digits = match self.0.millisecond { None => 0, Some(v) => 1 + v.to_string().len() as u8, }; writer .write(&[Self::TAG.0 as u8, 15 + num_digits]) .map_err(Into::into) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let fractional = match self.0.millisecond { None => "".to_string(), Some(v) => format!(".{}", v), }; let num_digits = fractional.len(); write!( writer, "{:04}{:02}{:02}{:02}{:02}{:02}{}Z", self.0.year, self.0.month, self.0.day, self.0.hour, self.0.minute, self.0.second, fractional, )?; // write_fmt returns (), see above for length value Ok(15 + num_digits) } } rusticata-asn1-rs-07e764c/src/asn1_types/integer.rs000066400000000000000000000556111476576401200222400ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; use alloc::vec; use core::convert::{TryFrom, TryInto}; #[cfg(feature = "bigint")] #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] pub use num_bigint::{BigInt, BigUint, Sign}; /// Decode an unsigned integer into a big endian byte slice with all leading /// zeroes removed (if positive) and extra 0xff remove (if negative) fn trim_slice<'a>(any: &'a Any<'_>) -> Result<&'a [u8]> { let bytes = any.data; if bytes.is_empty() || (bytes[0] != 0x00 && bytes[0] != 0xff) { return Ok(bytes); } match bytes.iter().position(|&b| b != 0) { // first byte is not 0 Some(0) => (), // all bytes are 0 None => return Ok(&bytes[bytes.len() - 1..]), Some(first) => return Ok(&bytes[first..]), } // same for negative integers : skip byte 0->n if byte 0->n = 0xff AND byte n+1 >= 0x80 match bytes.windows(2).position(|s| match s { &[a, b] => !(a == 0xff && b >= 0x80), _ => true, }) { // first byte is not 0xff Some(0) => (), // all bytes are 0xff None => return Ok(&bytes[bytes.len() - 1..]), Some(first) => return Ok(&bytes[first..]), } Ok(bytes) } /// Decode an unsigned integer into a byte array of the requested size /// containing a big endian integer. fn decode_array_uint(any: &Any<'_>) -> Result<[u8; N]> { if is_highest_bit_set(any.data) { return Err(Error::IntegerNegative); } let input = trim_slice(any)?; if input.len() > N { return Err(Error::IntegerTooLarge); } // Input has leading zeroes removed, so we need to add them back let mut output = [0u8; N]; assert!(input.len() <= N); output[N.saturating_sub(input.len())..].copy_from_slice(input); Ok(output) } /// Decode an unsigned integer of the specified size. /// /// Returns a byte array of the requested size containing a big endian integer. fn decode_array_int(any: &Any<'_>) -> Result<[u8; N]> { if any.data.len() > N { return Err(Error::IntegerTooLarge); } // any.tag().assert_eq(Tag::Integer)?; let mut output = [0xFFu8; N]; let offset = N.saturating_sub(any.as_bytes().len()); output[offset..].copy_from_slice(any.as_bytes()); Ok(output) } /// Is the highest bit of the first byte in the slice 1? (if present) #[inline] fn is_highest_bit_set(bytes: &[u8]) -> bool { bytes .first() .map(|byte| byte & 0b10000000 != 0) .unwrap_or(false) } macro_rules! impl_int { ($uint:ty => $int:ty) => { impl<'a> TryFrom> for $int { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for $int { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { $crate::debug::trace_generic( core::any::type_name::<$int>(), "Conversion to int", |any| { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; let uint = if is_highest_bit_set(any.as_bytes()) { <$uint>::from_be_bytes(decode_array_int(&any)?) } else { // read as uint, but check if the value will fit in a signed integer let u = <$uint>::from_be_bytes(decode_array_uint(&any)?); if u > <$int>::MAX as $uint { return Err(Error::IntegerTooLarge); } u }; Ok(uint as $int) }, any, ) } } impl CheckDerConstraints for $int { fn check_constraints(any: &Any) -> Result<()> { check_der_int_constraints(any) } } impl DerAutoDerive for $int {} impl Tagged for $int { const TAG: Tag = Tag::Integer; } #[cfg(feature = "std")] impl ToDer for $int { fn to_der_len(&self) -> Result { let int = Integer::from(*self); int.to_der_len() } fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(*self); int.write_der(writer) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(*self); int.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(*self); int.write_der_content(writer) } } }; } macro_rules! impl_uint { ($ty:ty) => { impl<'a> TryFrom> for $ty { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for $ty { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { $crate::debug::trace_generic( core::any::type_name::<$ty>(), "Conversion to uint", |any| { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; let result = Self::from_be_bytes(decode_array_uint(any)?); Ok(result) }, any, ) } } impl CheckDerConstraints for $ty { fn check_constraints(any: &Any) -> Result<()> { check_der_int_constraints(any) } } impl DerAutoDerive for $ty {} impl Tagged for $ty { const TAG: Tag = Tag::Integer; } #[cfg(feature = "std")] impl ToDer for $ty { fn to_der_len(&self) -> Result { let int = Integer::from(*self); int.to_der_len() } fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(*self); int.write_der(writer) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(*self); int.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let int = Integer::from(*self); int.write_der_content(writer) } } }; } impl_uint!(u8); impl_uint!(u16); impl_uint!(u32); impl_uint!(u64); impl_uint!(u128); impl_int!(u8 => i8); impl_int!(u16 => i16); impl_int!(u32 => i32); impl_int!(u64 => i64); impl_int!(u128 => i128); /// ASN.1 `INTEGER` type /// /// Generic representation for integer types. /// BER/DER integers can be of any size, so it is not possible to store them as simple integers (they /// are stored as raw bytes). /// /// The internal representation can be obtained using `.as_ref()`. /// /// # Note /// /// Methods from/to BER and DER encodings are also implemented for primitive types /// (`u8`, `u16` to `u128`, and `i8` to `i128`). /// In most cases, it is easier to use these types directly. /// /// # Examples /// /// Creating an `Integer` /// /// ``` /// use asn1_rs::Integer; /// /// // unsigned /// let i = Integer::from(4); /// assert_eq!(i.as_ref(), &[4]); /// // signed /// let j = Integer::from(-2); /// assert_eq!(j.as_ref(), &[0xfe]); /// ``` /// /// Converting an `Integer` to a primitive type (using the `TryInto` trait) /// /// ``` /// use asn1_rs::{Error, Integer}; /// use std::convert::TryInto; /// /// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]); /// // converts to an u32 /// let n: u32 = i.try_into().unwrap(); /// /// // Same, but converting to an u16: will fail, value cannot fit into an u16 /// let i = Integer::new(&[0x12, 0x34, 0x56, 0x78]); /// assert_eq!(i.try_into() as Result, Err(Error::IntegerTooLarge)); /// ``` /// /// Encoding an `Integer` to DER /// #[cfg_attr(feature = "std", doc = r#"```"#)] #[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] /// use asn1_rs::{Integer, ToDer}; /// /// let i = Integer::from(4); /// let v = i.to_der_vec().unwrap(); /// assert_eq!(&v, &[2, 1, 4]); /// /// // same, with primitive types /// let v = 4.to_der_vec().unwrap(); /// assert_eq!(&v, &[2, 1, 4]); /// ``` #[derive(Debug, Eq, PartialEq)] pub struct Integer<'a> { pub(crate) data: Cow<'a, [u8]>, } impl<'a> Integer<'a> { /// Creates a new `Integer` containing the given value (borrowed). #[inline] pub const fn new(s: &'a [u8]) -> Self { Integer { data: Cow::Borrowed(s), } } /// Creates a borrowed `Any` for this object #[inline] pub fn any(&'a self) -> Any<'a> { Any::from_tag_and_data(Self::TAG, &self.data) } /// Returns a `BigInt` built from this `Integer` value. #[cfg(feature = "bigint")] #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] pub fn as_bigint(&self) -> BigInt { BigInt::from_signed_bytes_be(&self.data) } /// Returns a `BigUint` built from this `Integer` value. #[cfg(feature = "bigint")] #[cfg_attr(docsrs, doc(cfg(feature = "bigint")))] pub fn as_biguint(&self) -> Result { if is_highest_bit_set(&self.data) { Err(Error::IntegerNegative) } else { Ok(BigUint::from_bytes_be(&self.data)) } } /// Build an `Integer` from a constant array of bytes representation of an integer. pub fn from_const_array(b: [u8; N]) -> Self { // if high bit set -> add leading 0 to ensure unsigned if is_highest_bit_set(&b) { let mut bytes = vec![0]; bytes.extend_from_slice(&b); Integer { data: Cow::Owned(bytes), } } // otherwise -> remove 0 unless next has high bit set else { let mut idx = 0; while idx < b.len() - 1 { if b[idx] == 0 && b[idx + 1] < 0x80 { idx += 1; continue; } break; } Integer { data: Cow::Owned(b[idx..].to_vec()), } } } fn from_const_array_negative(b: [u8; N]) -> Self { let mut idx = 0; // Skip leading FF unless next has high bit clear while idx < b.len() - 1 { if b[idx] == 0xFF && b[idx + 1] >= 0x80 { idx += 1; continue; } break; } if idx == b.len() { Integer { data: Cow::Borrowed(&[0]), } } else { Integer { data: Cow::Owned(b[idx..].to_vec()), } } } } macro_rules! impl_from_to { ($ty:ty, $sty:expr, $from:ident, $to:ident) => { impl From<$ty> for Integer<'_> { fn from(i: $ty) -> Self { Self::$from(i) } } impl TryFrom> for $ty { type Error = Error; fn try_from(value: Integer<'_>) -> Result { value.$to() } } impl Integer<'_> { #[doc = "Attempts to convert an `Integer` to a `"] #[doc = $sty] #[doc = "`."] #[doc = ""] #[doc = "This function returns an `IntegerTooLarge` error if the integer will not fit into the output type."] pub fn $to(&self) -> Result<$ty> { self.any().try_into() } } }; (IMPL SIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => { impl_from_to!($ty, $sty, $from, $to); impl Integer<'_> { #[doc = "Converts a `"] #[doc = $sty] #[doc = "` to an `Integer`"] #[doc = ""] #[doc = "Note: this function allocates data."] pub fn $from(i: $ty) -> Self { let b = i.to_be_bytes(); if i >= 0 { Self::from_const_array(b) } else { Self::from_const_array_negative(b) } } } }; (IMPL UNSIGNED $ty:ty, $sty:expr, $from:ident, $to:ident) => { impl_from_to!($ty, $sty, $from, $to); impl Integer<'_> { #[doc = "Converts a `"] #[doc = $sty] #[doc = "` to an `Integer`"] #[doc = ""] #[doc = "Note: this function allocates data."] pub fn $from(i: $ty) -> Self { Self::from_const_array(i.to_be_bytes()) } } }; (SIGNED $ty:ty, $from:ident, $to:ident) => { impl_from_to!(IMPL SIGNED $ty, stringify!($ty), $from, $to); }; (UNSIGNED $ty:ty, $from:ident, $to:ident) => { impl_from_to!(IMPL UNSIGNED $ty, stringify!($ty), $from, $to); }; } impl_from_to!(SIGNED i8, from_i8, as_i8); impl_from_to!(SIGNED i16, from_i16, as_i16); impl_from_to!(SIGNED i32, from_i32, as_i32); impl_from_to!(SIGNED i64, from_i64, as_i64); impl_from_to!(SIGNED i128, from_i128, as_i128); impl_from_to!(UNSIGNED u8, from_u8, as_u8); impl_from_to!(UNSIGNED u16, from_u16, as_u16); impl_from_to!(UNSIGNED u32, from_u32, as_u32); impl_from_to!(UNSIGNED u64, from_u64, as_u64); impl_from_to!(UNSIGNED u128, from_u128, as_u128); impl AsRef<[u8]> for Integer<'_> { fn as_ref(&self) -> &[u8] { &self.data } } impl<'a> TryFrom> for Integer<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result> { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Integer<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; Ok(Integer { data: Cow::Borrowed(any.data), }) } } impl CheckDerConstraints for Integer<'_> { fn check_constraints(any: &Any) -> Result<()> { check_der_int_constraints(any) } } fn check_der_int_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; any.header.length.assert_definite()?; match any.as_bytes() { [] => Err(Error::DerConstraintFailed(DerConstraint::IntegerEmpty)), [0] => Ok(()), // leading zeroes [0, byte, ..] if *byte < 0x80 => Err(Error::DerConstraintFailed( DerConstraint::IntegerLeadingZeroes, )), // negative integer with non-minimal encoding [0xff, byte, ..] if *byte >= 0x80 => { Err(Error::DerConstraintFailed(DerConstraint::IntegerLeadingFF)) } _ => Ok(()), } } impl DerAutoDerive for Integer<'_> {} impl Tagged for Integer<'_> { const TAG: Tag = Tag::Integer; } #[cfg(feature = "std")] impl ToDer for Integer<'_> { fn to_der_len(&self) -> Result { let sz = self.data.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // hmm, a very long integer. anyway: // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.data.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&self.data).map_err(Into::into) } } /// Helper macro to declare integers at compile-time /// /// [`Integer`] stores the encoded representation of the integer, so declaring /// an integer requires to either use a runtime function or provide the encoded value. /// This macro simplifies this task by encoding the value. /// It can be used the following ways: /// /// - `int!(1234)`: Create a const expression for the corresponding `Integer<'static>` /// - `int!(raw 1234)`: Return the DER encoded form as a byte array (hex-encoded, big-endian /// representation from the integer, with leading zeroes removed). /// /// # Examples /// /// ```rust /// use asn1_rs::{int, Integer}; /// /// const INT0: Integer = int!(1234); /// ``` #[macro_export] macro_rules! int { (raw $item:expr) => { $crate::exports::asn1_rs_impl::encode_int!($item) }; (rel $item:expr) => { $crate::exports::asn1_rs_impl::encode_int!(rel $item) }; ($item:expr) => { $crate::Integer::new( &$crate::int!(raw $item), ) }; } #[cfg(all(test, feature = "std"))] mod tests { use crate::{Any, FromDer, Header, Tag, ToDer}; use std::convert::TryInto; // Vectors from Section 5.7 of: // https://luca.ntop.org/Teaching/Appunti/asn1.html pub(crate) const I0_BYTES: &[u8] = &[0x02, 0x01, 0x00]; pub(crate) const I127_BYTES: &[u8] = &[0x02, 0x01, 0x7F]; pub(crate) const I128_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0x80]; pub(crate) const I256_BYTES: &[u8] = &[0x02, 0x02, 0x01, 0x00]; pub(crate) const INEG128_BYTES: &[u8] = &[0x02, 0x01, 0x80]; pub(crate) const INEG129_BYTES: &[u8] = &[0x02, 0x02, 0xFF, 0x7F]; // Additional vectors pub(crate) const I255_BYTES: &[u8] = &[0x02, 0x02, 0x00, 0xFF]; pub(crate) const I32767_BYTES: &[u8] = &[0x02, 0x02, 0x7F, 0xFF]; pub(crate) const I65535_BYTES: &[u8] = &[0x02, 0x03, 0x00, 0xFF, 0xFF]; pub(crate) const INEG32768_BYTES: &[u8] = &[0x02, 0x02, 0x80, 0x00]; #[test] fn decode_i8() { assert_eq!(0, i8::from_der(I0_BYTES).unwrap().1); assert_eq!(127, i8::from_der(I127_BYTES).unwrap().1); assert_eq!(-128, i8::from_der(INEG128_BYTES).unwrap().1); } #[test] fn encode_i8() { assert_eq!(0i8.to_der_vec().unwrap(), I0_BYTES); assert_eq!(127i8.to_der_vec().unwrap(), I127_BYTES); assert_eq!((-128i8).to_der_vec().unwrap(), INEG128_BYTES); } #[test] fn decode_i16() { assert_eq!(0, i16::from_der(I0_BYTES).unwrap().1); assert_eq!(127, i16::from_der(I127_BYTES).unwrap().1); assert_eq!(128, i16::from_der(I128_BYTES).unwrap().1); assert_eq!(255, i16::from_der(I255_BYTES).unwrap().1); assert_eq!(256, i16::from_der(I256_BYTES).unwrap().1); assert_eq!(32767, i16::from_der(I32767_BYTES).unwrap().1); assert_eq!(-128, i16::from_der(INEG128_BYTES).unwrap().1); assert_eq!(-129, i16::from_der(INEG129_BYTES).unwrap().1); assert_eq!(-32768, i16::from_der(INEG32768_BYTES).unwrap().1); } #[test] fn encode_i16() { assert_eq!(0i16.to_der_vec().unwrap(), I0_BYTES); assert_eq!(127i16.to_der_vec().unwrap(), I127_BYTES); assert_eq!(128i16.to_der_vec().unwrap(), I128_BYTES); assert_eq!(255i16.to_der_vec().unwrap(), I255_BYTES); assert_eq!(256i16.to_der_vec().unwrap(), I256_BYTES); assert_eq!(32767i16.to_der_vec().unwrap(), I32767_BYTES); assert_eq!((-128i16).to_der_vec().unwrap(), INEG128_BYTES); assert_eq!((-129i16).to_der_vec().unwrap(), INEG129_BYTES); assert_eq!((-32768i16).to_der_vec().unwrap(), INEG32768_BYTES); } #[test] fn decode_u8() { assert_eq!(0, u8::from_der(I0_BYTES).unwrap().1); assert_eq!(127, u8::from_der(I127_BYTES).unwrap().1); assert_eq!(255, u8::from_der(I255_BYTES).unwrap().1); } #[test] fn encode_u8() { assert_eq!(0u8.to_der_vec().unwrap(), I0_BYTES); assert_eq!(127u8.to_der_vec().unwrap(), I127_BYTES); assert_eq!(255u8.to_der_vec().unwrap(), I255_BYTES); } #[test] fn decode_u16() { assert_eq!(0, u16::from_der(I0_BYTES).unwrap().1); assert_eq!(127, u16::from_der(I127_BYTES).unwrap().1); assert_eq!(255, u16::from_der(I255_BYTES).unwrap().1); assert_eq!(256, u16::from_der(I256_BYTES).unwrap().1); assert_eq!(32767, u16::from_der(I32767_BYTES).unwrap().1); assert_eq!(65535, u16::from_der(I65535_BYTES).unwrap().1); } #[test] fn encode_u16() { assert_eq!(0u16.to_der_vec().unwrap(), I0_BYTES); assert_eq!(127u16.to_der_vec().unwrap(), I127_BYTES); assert_eq!(255u16.to_der_vec().unwrap(), I255_BYTES); assert_eq!(256u16.to_der_vec().unwrap(), I256_BYTES); assert_eq!(32767u16.to_der_vec().unwrap(), I32767_BYTES); assert_eq!(65535u16.to_der_vec().unwrap(), I65535_BYTES); } /// Integers must be encoded with a minimum number of octets #[test] fn reject_non_canonical() { assert!(i8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); assert!(i16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); assert!(u8::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); assert!(u16::from_der(&[0x02, 0x02, 0x00, 0x00]).is_err()); } #[test] fn declare_int() { let int = super::int!(1234); assert_eq!(int.try_into(), Ok(1234)); } #[test] fn trim_slice() { use super::trim_slice; let h = Header::new_simple(Tag(0)); // no zero nor ff - nothing to remove let input: &[u8] = &[0x7f, 0xff, 0x00, 0x02]; assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); // // 0x00 // // empty - nothing to remove let input: &[u8] = &[]; assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); // one zero - nothing to remove let input: &[u8] = &[0]; assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); // all zeroes - keep only one let input: &[u8] = &[0, 0, 0]; assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); // some zeroes - keep only the non-zero part let input: &[u8] = &[0, 0, 1]; assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); // // 0xff // // one ff - nothing to remove let input: &[u8] = &[0xff]; assert_eq!(Ok(input), trim_slice(&Any::new(h.clone(), input))); // all ff - keep only one let input: &[u8] = &[0xff, 0xff, 0xff]; assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); // some ff - keep only the non-zero part let input: &[u8] = &[0xff, 0xff, 1]; assert_eq!(Ok(&input[1..]), trim_slice(&Any::new(h.clone(), input))); // some ff and a MSB 1 - keep only the non-zero part let input: &[u8] = &[0xff, 0xff, 0x80, 1]; assert_eq!(Ok(&input[2..]), trim_slice(&Any::new(h.clone(), input))); } } rusticata-asn1-rs-07e764c/src/asn1_types/mod.rs000066400000000000000000000010531476576401200213510ustar00rootroot00000000000000mod any; mod bitstring; mod boolean; mod choice; mod embedded_pdv; mod end_of_content; mod enumerated; mod generalizedtime; mod integer; mod null; mod object_descriptor; mod octetstring; mod oid; mod optional; mod real; mod sequence; mod set; mod strings; mod tagged; mod utctime; pub use { any::*, bitstring::*, boolean::*, choice::*, embedded_pdv::*, end_of_content::*, enumerated::*, generalizedtime::*, integer::*, null::*, object_descriptor::*, octetstring::*, oid::*, real::*, sequence::*, set::*, strings::*, tagged::*, utctime::*, }; rusticata-asn1-rs-07e764c/src/asn1_types/null.rs000066400000000000000000000042041476576401200215450ustar00rootroot00000000000000use crate::*; use core::convert::TryFrom; /// ASN.1 `NULL` type #[derive(Debug, PartialEq, Eq)] pub struct Null {} impl Default for Null { fn default() -> Self { Self::new() } } impl Null { pub const fn new() -> Self { Null {} } } impl<'a> TryFrom> for Null { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Null { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; if !any.header.length.is_null() { return Err(Error::InvalidLength); } Ok(Null {}) } } impl CheckDerConstraints for Null { fn check_constraints(_any: &Any) -> Result<()> { Ok(()) } } impl DerAutoDerive for Null {} impl Tagged for Null { const TAG: Tag = Tag::Null; } #[cfg(feature = "std")] impl ToDer for Null { fn to_der_len(&self) -> Result { Ok(2) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&[0x05, 0x00]).map_err(Into::into) } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } } impl<'a> TryFrom> for () { type Error = Error; fn try_from(any: Any<'a>) -> Result<()> { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; if !any.header.length.is_null() { return Err(Error::InvalidLength); } Ok(()) } } impl CheckDerConstraints for () { fn check_constraints(_any: &Any) -> Result<()> { Ok(()) } } impl DerAutoDerive for () {} impl Tagged for () { const TAG: Tag = Tag::Null; } #[cfg(feature = "std")] impl ToDer for () { fn to_der_len(&self) -> Result { Ok(2) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&[0x05, 0x00]).map_err(Into::into) } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } } rusticata-asn1-rs-07e764c/src/asn1_types/object_descriptor.rs000066400000000000000000000007321476576401200243010ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; // X.680 section 44.3 // ObjectDescriptor ::= [UNIVERSAL 7] IMPLICIT GraphicString asn1_string!(ObjectDescriptor); impl TestValidCharset for ObjectDescriptor<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { if !i.iter().all(u8::is_ascii) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/octetstring.rs000066400000000000000000000073761476576401200231550ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; use core::convert::TryFrom; /// ASN.1 `OCTETSTRING` type #[derive(Debug, PartialEq, Eq)] pub struct OctetString<'a> { data: Cow<'a, [u8]>, } impl<'a> OctetString<'a> { pub const fn new(s: &'a [u8]) -> Self { OctetString { data: Cow::Borrowed(s), } } /// Get the bytes representation of the *content* pub fn as_cow(&'a self) -> &'a Cow<'a, [u8]> { &self.data } /// Get the bytes representation of the *content* pub fn into_cow(self) -> Cow<'a, [u8]> { self.data } } impl AsRef<[u8]> for OctetString<'_> { fn as_ref(&self) -> &[u8] { &self.data } } impl<'a> From<&'a [u8]> for OctetString<'a> { fn from(b: &'a [u8]) -> Self { OctetString { data: Cow::Borrowed(b), } } } impl<'a> TryFrom> for OctetString<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result> { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for OctetString<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; Ok(OctetString { data: Cow::Borrowed(any.data), }) } } impl CheckDerConstraints for OctetString<'_> { fn check_constraints(any: &Any) -> Result<()> { // X.690 section 10.2 any.header.assert_primitive()?; Ok(()) } } impl DerAutoDerive for OctetString<'_> {} impl Tagged for OctetString<'_> { const TAG: Tag = Tag::OctetString; } #[cfg(feature = "std")] impl ToDer for OctetString<'_> { fn to_der_len(&self) -> Result { let sz = self.data.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.data.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&self.data).map_err(Into::into) } } impl<'a> TryFrom> for &'a [u8] { type Error = Error; fn try_from(any: Any<'a>) -> Result<&'a [u8]> { any.tag().assert_eq(Self::TAG)?; let s = OctetString::try_from(any)?; match s.data { Cow::Borrowed(s) => Ok(s), Cow::Owned(_) => Err(Error::LifetimeError), } } } impl CheckDerConstraints for &'_ [u8] { fn check_constraints(any: &Any) -> Result<()> { // X.690 section 10.2 any.header.assert_primitive()?; Ok(()) } } impl DerAutoDerive for &'_ [u8] {} impl Tagged for &'_ [u8] { const TAG: Tag = Tag::OctetString; } #[cfg(feature = "std")] impl ToDer for &'_ [u8] { fn to_der_len(&self) -> Result { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.len()), ); Ok(header.to_der_len()? + self.len()) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(self).map_err(Into::into) } } rusticata-asn1-rs-07e764c/src/asn1_types/oid.rs000066400000000000000000000405171476576401200213550ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::format; #[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::{ convert::TryFrom, fmt, iter::FusedIterator, marker::PhantomData, ops::Shl, str::FromStr, }; use displaydoc::Display; use num_traits::Num; use thiserror::Error; /// An error for OID parsing functions. #[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Error)] pub enum OidParseError { /// Encoded data length too short TooShort, /** Signalizes that the first or second component is too large. * The first must be within the range 0 to 6 (inclusive). * The second component must be less than 40. */ FirstComponentsTooLarge, /// a ParseIntError, } /// Object ID (OID) representation which can be relative or non-relative. /// /// An example for an OID in string representation is `"1.2.840.113549.1.1.5"`. /// /// For non-relative OIDs restrictions apply to the first two components. /// /// This library contains a procedural macro `oid` which can be used to /// create oids. For example `oid!(1.2.44.233)` or `oid!(rel 44.233)` /// for relative oids. See the [module documentation](index.html) for more information. #[derive(Hash, PartialEq, Eq, Clone)] pub struct Oid<'a> { asn1: Cow<'a, [u8]>, relative: bool, } impl<'a> TryFrom> for Oid<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Oid<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { // check that any.data.last().unwrap() >> 7 == 0u8 let asn1 = Cow::Borrowed(any.data); Ok(Oid::new(asn1)) } } impl CheckDerConstraints for Oid<'_> { fn check_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; any.header.length.assert_definite()?; Ok(()) } } impl DerAutoDerive for Oid<'_> {} impl Tagged for Oid<'_> { const TAG: Tag = Tag::Oid; } #[cfg(feature = "std")] impl ToDer for Oid<'_> { fn to_der_len(&self) -> Result { // OID/REL-OID tag will not change header size, so we don't care here let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.asn1.len()), ); Ok(header.to_der_len()? + self.asn1.len()) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let tag = if self.relative { Tag::RelativeOid } else { Tag::Oid }; let header = Header::new( Class::Universal, false, tag, Length::Definite(self.asn1.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&self.asn1).map_err(Into::into) } } fn encode_relative(ids: &'_ [u64]) -> impl Iterator + '_ { ids.iter().flat_map(|id| { let bit_count = 64 - id.leading_zeros(); let octets_needed = ((bit_count + 6) / 7).max(1); (0..octets_needed).map(move |i| { let flag = if i == octets_needed - 1 { 0 } else { 1 << 7 }; ((id >> (7 * (octets_needed - 1 - i))) & 0b111_1111) as u8 | flag }) }) } impl<'a> Oid<'a> { /// Create an OID from the ASN.1 DER encoded form. See the [module documentation](index.html) /// for other ways to create oids. pub const fn new(asn1: Cow<'a, [u8]>) -> Oid<'a> { Oid { asn1, relative: false, } } /// Create a relative OID from the ASN.1 DER encoded form. See the [module documentation](index.html) /// for other ways to create relative oids. pub const fn new_relative(asn1: Cow<'a, [u8]>) -> Oid<'a> { Oid { asn1, relative: true, } } /// Build an OID from an array of object identifier components. /// This method allocates memory on the heap. pub fn from(s: &[u64]) -> core::result::Result, OidParseError> { if s.len() < 2 { if s.len() == 1 && s[0] == 0 { return Ok(Oid { asn1: Cow::Borrowed(&[0]), relative: false, }); } return Err(OidParseError::TooShort); } if s[0] >= 7 || s[1] >= 40 { return Err(OidParseError::FirstComponentsTooLarge); } let asn1_encoded: Vec = [(s[0] * 40 + s[1]) as u8] .iter() .copied() .chain(encode_relative(&s[2..])) .collect(); Ok(Oid { asn1: Cow::from(asn1_encoded), relative: false, }) } /// Build a relative OID from an array of object identifier components. pub fn from_relative(s: &[u64]) -> core::result::Result, OidParseError> { if s.is_empty() { return Err(OidParseError::TooShort); } let asn1_encoded: Vec = encode_relative(s).collect(); Ok(Oid { asn1: Cow::from(asn1_encoded), relative: true, }) } /// Create a deep copy of the oid. /// /// This method allocates data on the heap. The returned oid /// can be used without keeping the ASN.1 representation around. /// /// Cloning the returned oid does again allocate data. pub fn to_owned(&self) -> Oid<'static> { Oid { asn1: Cow::from(self.asn1.to_vec()), relative: self.relative, } } /// Get the encoded oid without the header. #[inline] pub fn as_bytes(&self) -> &[u8] { self.asn1.as_ref() } /// Get the encoded oid without the header. #[deprecated(since = "0.2.0", note = "Use `as_bytes` instead")] #[inline] pub fn bytes(&self) -> &[u8] { self.as_bytes() } /// Get the bytes representation of the encoded oid pub fn into_cow(self) -> Cow<'a, [u8]> { self.asn1 } /// Convert the OID to a string representation. /// The string contains the IDs separated by dots, for ex: "1.2.840.113549.1.1.5" #[cfg(feature = "bigint")] pub fn to_id_string(&self) -> String { let ints: Vec = self.iter_bigint().map(|i| i.to_string()).collect(); ints.join(".") } #[cfg(not(feature = "bigint"))] /// Convert the OID to a string representation. /// /// If every arc fits into a u64 a string like "1.2.840.113549.1.1.5" /// is returned, otherwise a hex representation. /// /// See also the "bigint" feature of this crate. pub fn to_id_string(&self) -> String { if let Some(arcs) = self.iter() { let ints: Vec = arcs.map(|i| i.to_string()).collect(); ints.join(".") } else { let mut ret = String::with_capacity(self.asn1.len() * 3); for (i, o) in self.asn1.iter().enumerate() { ret.push_str(&format!("{:02x}", o)); if i + 1 != self.asn1.len() { ret.push(' '); } } ret } } /// Return an iterator over the sub-identifiers (arcs). #[cfg(feature = "bigint")] pub fn iter_bigint(&'_ self) -> impl FusedIterator + ExactSizeIterator + '_ { SubIdentifierIterator { oid: self, pos: 0, first: false, n: PhantomData, } } /// Return an iterator over the sub-identifiers (arcs). /// Returns `None` if at least one arc does not fit into `u64`. pub fn iter(&'_ self) -> Option + ExactSizeIterator + '_> { // Check that every arc fits into u64 let bytes = if self.relative { &self.asn1 } else if self.asn1.is_empty() { &[] } else { &self.asn1[1..] }; let max_bits = bytes .iter() .fold((0usize, 0usize), |(max, cur), c| { let is_end = (c >> 7) == 0u8; if is_end { (max.max(cur + 7), 0) } else { (max, cur + 7) } }) .0; if max_bits > 64 { return None; } Some(SubIdentifierIterator { oid: self, pos: 0, first: false, n: PhantomData, }) } pub fn from_ber_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, any) = Any::from_ber(bytes)?; any.header.assert_primitive()?; any.header.assert_tag(Tag::RelativeOid)?; let asn1 = Cow::Borrowed(any.data); Ok((rem, Oid::new_relative(asn1))) } pub fn from_der_relative(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, any) = Any::from_der(bytes)?; any.header.assert_tag(Tag::RelativeOid)?; Self::check_constraints(&any)?; let asn1 = Cow::Borrowed(any.data); Ok((rem, Oid::new_relative(asn1))) } /// Returns true if `needle` is a prefix of the OID. pub fn starts_with(&self, needle: &Oid) -> bool { self.asn1.len() >= needle.asn1.len() && self.asn1.starts_with(needle.as_bytes()) } } trait Repr: Num + Shl + From {} impl Repr for N where N: Num + Shl + From {} struct SubIdentifierIterator<'a, N: Repr> { oid: &'a Oid<'a>, pos: usize, first: bool, n: PhantomData<&'a N>, } impl Iterator for SubIdentifierIterator<'_, N> { type Item = N; fn next(&mut self) -> Option { use num_traits::identities::Zero; if self.pos == self.oid.asn1.len() { return None; } if !self.oid.relative { if !self.first { debug_assert!(self.pos == 0); self.first = true; return Some((self.oid.asn1[0] / 40).into()); } else if self.pos == 0 { self.pos += 1; if self.oid.asn1[0] == 0 && self.oid.asn1.len() == 1 { return None; } return Some((self.oid.asn1[0] % 40).into()); } } // decode objet sub-identifier according to the asn.1 standard let mut res = ::zero(); for o in self.oid.asn1[self.pos..].iter() { self.pos += 1; res = (res << 7) + (o & 0b111_1111).into(); let flag = o >> 7; if flag == 0u8 { break; } } Some(res) } } impl FusedIterator for SubIdentifierIterator<'_, N> {} impl ExactSizeIterator for SubIdentifierIterator<'_, N> { fn len(&self) -> usize { if self.oid.relative { self.oid.asn1.iter().filter(|o| (*o >> 7) == 0u8).count() } else if self.oid.asn1.is_empty() { 0 } else if self.oid.asn1.len() == 1 { if self.oid.asn1[0] == 0 { 1 } else { 2 } } else { 2 + self.oid.asn1[2..] .iter() .filter(|o| (*o >> 7) == 0u8) .count() } } } impl fmt::Display for Oid<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if self.relative { f.write_str("rel. ")?; } f.write_str(&self.to_id_string()) } } impl fmt::Debug for Oid<'_> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("OID(")?; ::fmt(self, f)?; f.write_str(")") } } impl FromStr for Oid<'_> { type Err = OidParseError; fn from_str(s: &str) -> core::result::Result { let v: core::result::Result, _> = s.split('.').map(|c| c.parse::()).collect(); v.map_err(|_| OidParseError::ParseIntError) .and_then(|v| Oid::from(&v)) } } /// Helper macro to declare integers at compile-time /// /// Since the DER encoded oids are not very readable we provide a /// procedural macro `oid!`. The macro can be used the following ways: /// /// - `oid!(1.4.42.23)`: Create a const expression for the corresponding `Oid<'static>` /// - `oid!(rel 42.23)`: Create a const expression for the corresponding relative `Oid<'static>` /// - `oid!(raw 1.4.42.23)`/`oid!(raw rel 42.23)`: Obtain the DER encoded form as a byte array. /// /// # Comparing oids /// /// Comparing a parsed oid to a static oid is probably the most common /// thing done with oids in your code. The `oid!` macro can be used in expression positions for /// this purpose. For example /// ``` /// use asn1_rs::{oid, Oid}; /// /// # let some_oid: Oid<'static> = oid!(1.2.456); /// const SOME_STATIC_OID: Oid<'static> = oid!(1.2.456); /// assert_eq!(some_oid, SOME_STATIC_OID) /// ``` /// To get a relative Oid use `oid!(rel 1.2)`. /// /// Because of limitations for procedural macros ([rust issue](https://github.com/rust-lang/rust/issues/54727)) /// and constants used in patterns ([rust issue](https://github.com/rust-lang/rust/issues/31434)) /// the `oid` macro can not directly be used in patterns, also not through constants. /// You can do this, though: /// ``` /// # use asn1_rs::{oid, Oid}; /// # let some_oid: Oid<'static> = oid!(1.2.456); /// const SOME_OID: Oid<'static> = oid!(1.2.456); /// if some_oid == SOME_OID || some_oid == oid!(1.2.456) { /// println!("match"); /// } /// /// // Alternatively, compare the DER encoded form directly: /// const SOME_OID_RAW: &[u8] = &oid!(raw 1.2.456); /// match some_oid.as_bytes() { /// SOME_OID_RAW => println!("match"), /// _ => panic!("no match"), /// } /// ``` /// *Attention*, be aware that the latter version might not handle the case of a relative oid correctly. An /// extra check might be necessary. #[macro_export] macro_rules! oid { (raw $( $item:literal ).*) => { $crate::exports::asn1_rs_impl::encode_oid!( $( $item ).* ) }; (raw $items:expr) => { $crate::exports::asn1_rs_impl::encode_oid!($items) }; (rel $($item:literal ).*) => { $crate::Oid::new_relative($crate::exports::borrow::Cow::Borrowed( &$crate::exports::asn1_rs_impl::encode_oid!(rel $( $item ).*), )) }; ($($item:literal ).*) => { $crate::Oid::new($crate::exports::borrow::Cow::Borrowed( &$crate::oid!(raw $( $item ).*), )) }; } #[cfg(all(test, feature = "std"))] mod tests { use crate::{FromDer, Oid, ToDer}; use hex_literal::hex; #[test] fn declare_oid() { let oid = super::oid! {1.2.840.113549.1}; assert_eq!(oid.to_string(), "1.2.840.113549.1"); } const OID_RSA_ENCRYPTION: &[u8] = &oid! {raw 1.2.840.113549.1.1.1}; const OID_EC_PUBLIC_KEY: &[u8] = &oid! {raw 1.2.840.10045.2.1}; #[allow(clippy::match_like_matches_macro)] fn compare_oid(oid: &Oid) -> bool { match oid.as_bytes() { OID_RSA_ENCRYPTION => true, OID_EC_PUBLIC_KEY => true, _ => false, } } #[test] fn test_compare_oid() { let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap(); assert_eq!(oid, oid! {1.2.840.113549.1.1.1}); let oid = Oid::from(&[1, 2, 840, 113_549, 1, 1, 1]).unwrap(); assert!(compare_oid(&oid)); } #[test] fn oid_to_der() { let oid = super::oid! {1.2.840.113549.1}; assert_eq!(oid.to_der_len(), Ok(9)); let v = oid.to_der_vec().expect("could not serialize"); assert_eq!(&v, &hex! {"06 07 2a 86 48 86 f7 0d 01"}); let (_, oid2) = Oid::from_der(&v).expect("could not re-parse"); assert_eq!(&oid, &oid2); } #[test] fn oid_starts_with() { const OID_RSA_ENCRYPTION: Oid = oid! {1.2.840.113549.1.1.1}; const OID_EC_PUBLIC_KEY: Oid = oid! {1.2.840.10045.2.1}; let oid = super::oid! {1.2.840.113549.1}; assert!(OID_RSA_ENCRYPTION.starts_with(&oid)); assert!(!OID_EC_PUBLIC_KEY.starts_with(&oid)); } #[test] fn oid_macro_parameters() { // Code inspired from https://github.com/rusticata/der-parser/issues/68 macro_rules! foo { ($a:literal $b:literal $c:literal) => { super::oid!($a.$b.$c) }; } let oid = foo!(1 2 3); assert_eq!(oid, oid! {1.2.3}); } } rusticata-asn1-rs-07e764c/src/asn1_types/optional.rs000066400000000000000000000057711476576401200224320ustar00rootroot00000000000000use crate::*; // note: we cannot implement `TryFrom> with generic errors for Option`, // because this conflicts with generic `T` implementation in // `src/traits.rs`, since `T` always satisfies `T: Into>` // // for the same reason, we cannot use a generic error type here impl<'a, T> FromBer<'a> for Option where T: FromBer<'a>, T: Tagged, { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> { if bytes.is_empty() { return Ok((bytes, None)); } if let Ok((_, header)) = Header::from_ber(bytes) { if T::TAG != header.tag { // not the expected tag, early return return Ok((bytes, None)); } } match T::from_ber(bytes) { Ok((rem, t)) => Ok((rem, Some(t))), Err(e) => Err(e), } } } impl<'a> FromBer<'a> for Option> { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> { if bytes.is_empty() { return Ok((bytes, None)); } match Any::from_ber(bytes) { Ok((rem, t)) => Ok((rem, Some(t))), Err(e) => Err(e), } } } impl<'a, T> FromDer<'a> for Option where T: FromDer<'a>, T: Tagged, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { if bytes.is_empty() { return Ok((bytes, None)); } if let Ok((_, header)) = Header::from_der(bytes) { if T::TAG != header.tag { // not the expected tag, early return return Ok((bytes, None)); } } match T::from_der(bytes) { Ok((rem, t)) => Ok((rem, Some(t))), Err(e) => Err(e), } } } impl<'a> FromDer<'a> for Option> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { if bytes.is_empty() { return Ok((bytes, None)); } match Any::from_der(bytes) { Ok((rem, t)) => Ok((rem, Some(t))), Err(e) => Err(e), } } } impl CheckDerConstraints for Option where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { T::check_constraints(any) } } impl DynTagged for Option where T: DynTagged, { fn tag(&self) -> Tag { if self.is_some() { self.tag() } else { Tag(0) } } } #[cfg(feature = "std")] impl ToDer for Option where T: ToDer, { fn to_der_len(&self) -> Result { match self { None => Ok(0), Some(t) => t.to_der_len(), } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { match self { None => Ok(0), Some(t) => t.write_der_header(writer), } } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { match self { None => Ok(0), Some(t) => t.write_der_content(writer), } } } rusticata-asn1-rs-07e764c/src/asn1_types/real.rs000066400000000000000000000332561476576401200215270ustar00rootroot00000000000000use crate::*; use alloc::format; use core::convert::TryFrom; mod f32; mod f64; /// ASN.1 `REAL` type /// /// # Limitations /// /// When encoding binary values, only base 2 is supported #[derive(Debug, PartialEq)] pub enum Real { /// Non-special values Binary { mantissa: f64, base: u32, exponent: i32, enc_base: u8, }, /// Infinity (∞). Infinity, /// Negative infinity (−∞). NegInfinity, /// Zero Zero, } impl Real { /// Create a new `REAL` from the `f64` value. pub fn new(f: f64) -> Self { if f.is_infinite() { if f.is_sign_positive() { Self::Infinity } else { Self::NegInfinity } } else if f.abs() == 0.0 { Self::Zero } else { let mut e = 0; let mut f = f; while f.fract() != 0.0 { f *= 10.0_f64; e -= 1; } Real::Binary { mantissa: f, base: 10, exponent: e, enc_base: 10, } .normalize_base10() } } pub const fn with_enc_base(self, enc_base: u8) -> Self { match self { Real::Binary { mantissa, base, exponent, .. } => Real::Binary { mantissa, base, exponent, enc_base, }, e => e, } } fn normalize_base10(self) -> Self { match self { Real::Binary { mantissa, base: 10, exponent, enc_base: _enc_base, } => { let mut m = mantissa; let mut e = exponent; while m.abs() > f64::EPSILON && m.rem_euclid(10.0).abs() < f64::EPSILON { m /= 10.0; e += 1; } Real::Binary { mantissa: m, base: 10, exponent: e, enc_base: _enc_base, } } _ => self, } } /// Create a new binary `REAL` #[inline] pub const fn binary(mantissa: f64, base: u32, exponent: i32) -> Self { Self::Binary { mantissa, base, exponent, enc_base: 2, } } /// Returns `true` if this value is positive infinity or negative infinity, and /// `false` otherwise. #[inline] pub fn is_infinite(&self) -> bool { matches!(self, Real::Infinity | Real::NegInfinity) } /// Returns `true` if this number is not infinite. #[inline] pub fn is_finite(&self) -> bool { matches!(self, Real::Zero | Real::Binary { .. }) } /// Returns the 'f64' value of this `REAL`. /// /// Returned value is a float, and may be infinite. pub fn f64(&self) -> f64 { match self { Real::Binary { mantissa, base, exponent, .. } => { let f = mantissa; let exp = (*base as f64).powi(*exponent); f * exp } Real::Zero => 0.0_f64, Real::Infinity => f64::INFINITY, Real::NegInfinity => f64::NEG_INFINITY, } } /// Returns the 'f32' value of this `REAL`. /// /// This functions casts the result of [`Real::f64`] to a `f32`, and loses precision. pub fn f32(&self) -> f32 { self.f64() as f32 } } impl<'a> TryFrom> for Real { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Real { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; let data = &any.data; if data.is_empty() { return Ok(Real::Zero); } // code inspired from pyasn1 let first = data[0]; let rem = &data[1..]; if first & 0x80 != 0 { // binary encoding (X.690 section 8.5.6) // format of exponent let (n, rem) = match first & 0x03 { 4 => { let (b, rem) = rem .split_first() .ok_or_else(|| Error::Incomplete(Needed::new(1)))?; (*b as usize, rem) } b => (b as usize + 1, rem), }; if n >= rem.len() { return Err(any.tag().invalid_value("Invalid float value(exponent)")); } // n cannot be 0 (see the +1 above) let (eo, rem) = rem.split_at(n); // so 'eo' cannot be empty let mut e = if eo[0] & 0x80 != 0 { -1 } else { 0 }; // safety check: 'eo' length must be <= container type for 'e' if eo.len() > 4 { return Err(any.tag().invalid_value("Exponent too large (REAL)")); } for b in eo { e = (e << 8) | (*b as i32); } // base bits let b = (first >> 4) & 0x03; let _enc_base = match b { 0 => 2, 1 => 8, 2 => 16, _ => return Err(any.tag().invalid_value("Illegal REAL encoding base")), }; let e = match b { // base 2 0 => e, // base 8 1 => e * 3, // base 16 2 => e * 4, _ => return Err(any.tag().invalid_value("Illegal REAL base")), }; if rem.len() > 8 { return Err(any.tag().invalid_value("Mantissa too large (REAL)")); } let mut p = 0; for b in rem { p = (p << 8) | (*b as i64); } // sign bit let p = if first & 0x40 != 0 { -p } else { p }; // scale bits let sf = (first >> 2) & 0x03; let p = match sf { 0 => p as f64, sf => { // 2^sf: cannot overflow, sf is between 0 and 3 let scale = 2_f64.powi(sf as _); (p as f64) * scale } }; Ok(Real::Binary { mantissa: p, base: 2, exponent: e, enc_base: _enc_base, }) } else if first & 0x40 != 0 { // special real value (X.690 section 8.5.8) // there shall be only one contents octet, if any.header.length != Length::Definite(1) { return Err(Error::InvalidLength); } // with values as follows match first { 0x40 => Ok(Real::Infinity), 0x41 => Ok(Real::NegInfinity), _ => Err(any.tag().invalid_value("Invalid float special value")), } } else { // decimal encoding (X.690 section 8.5.7) let s = alloc::str::from_utf8(rem)?; match first & 0x03 { 0x1 => { // NR1 match s.parse::() { Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")), Ok(v) => Ok(Real::new(v.into())), } } 0x2 /* NR2 */ | 0x3 /* NR3 */=> { match s.parse::() { Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")), Ok(v) => Ok(Real::new(v)), } } c => { Err(any.tag().invalid_value(&format!("Invalid NR ({})", c))) } } } } } impl CheckDerConstraints for Real { fn check_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; any.header.length.assert_definite()?; // XXX more checks Ok(()) } } impl DerAutoDerive for Real {} impl Tagged for Real { const TAG: Tag = Tag::RealType; } #[cfg(feature = "std")] impl ToDer for Real { fn to_der_len(&self) -> Result { match self { Real::Zero => Ok(0), Real::Infinity | Real::NegInfinity => Ok(1), Real::Binary { .. } => { let mut sink = std::io::sink(); let n = self .write_der_content(&mut sink) .map_err(|_| Self::TAG.invalid_value("Serialization of REAL failed"))?; Ok(n) } } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.to_der_len()?), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { match self { Real::Zero => Ok(0), Real::Infinity => writer.write(&[0x40]).map_err(Into::into), Real::NegInfinity => writer.write(&[0x41]).map_err(Into::into), Real::Binary { mantissa, base, exponent, enc_base: _enc_base, } => { if *base == 10 { // using character form let sign = if *exponent == 0 { "+" } else { "" }; let s = format!("\x03{}E{}{}", mantissa, sign, exponent); return writer.write(s.as_bytes()).map_err(Into::into); } if *base != 2 { return Err(Self::TAG.invalid_value("Invalid base for REAL").into()); } let mut first: u8 = 0x80; // choose encoding base let enc_base = *_enc_base; let (ms, mut m, enc_base, mut e) = drop_floating_point(*mantissa, enc_base, *exponent); assert!(m != 0); if ms < 0 { first |= 0x40 }; // exponent & mantissa normalization match enc_base { 2 => { while m & 0x1 == 0 { m >>= 1; e += 1; } } 8 => { while m & 0x7 == 0 { m >>= 3; e += 1; } first |= 0x10; } _ /* 16 */ => { while m & 0xf == 0 { m >>= 4; e += 1; } first |= 0x20; } } // scale factor // XXX in DER, sf is always 0 (11.3.1) let mut sf = 0; while m & 0x1 == 0 && sf < 4 { m >>= 1; sf += 1; } first |= sf << 2; // exponent length and bytes let len_e = match e.abs() { 0..=0xff => 1, 0x100..=0xffff => 2, 0x1_0000..=0xff_ffff => 3, // e is an `i32` so it can't be longer than 4 bytes // use 4, so `first` is ORed with 3 _ => 4, }; first |= (len_e - 1) & 0x3; // write first byte let mut n = writer.write(&[first])?; // write exponent // special case: number of bytes from exponent is > 3 and cannot fit in 2 bits #[allow(clippy::identity_op)] if len_e == 4 { let b = len_e & 0xff; n += writer.write(&[b])?; } // we only need to write e.len() bytes let bytes = e.to_be_bytes(); n += writer.write(&bytes[(4 - len_e) as usize..])?; // write mantissa let bytes = m.to_be_bytes(); let mut idx = 0; for &b in bytes.iter() { if b != 0 { break; } idx += 1; } n += writer.write(&bytes[idx..])?; Ok(n) } } } } impl From for Real { fn from(f: f32) -> Self { Real::new(f.into()) } } impl From for Real { fn from(f: f64) -> Self { Real::new(f) } } impl From for f32 { fn from(r: Real) -> Self { r.f32() } } impl From for f64 { fn from(r: Real) -> Self { r.f64() } } #[cfg(feature = "std")] fn drop_floating_point(m: f64, b: u8, e: i32) -> (i8, u64, u8, i32) { let ms = if m.is_sign_positive() { 1 } else { -1 }; let es = if e.is_positive() { 1 } else { -1 }; let mut m = m.abs(); let mut e = e; // if b == 8 { m *= 2_f64.powi((e.abs() / 3) * es); e = (e.abs() / 3) * es; } else if b == 16 { m *= 2_f64.powi((e.abs() / 4) * es); e = (e.abs() / 4) * es; } // while m.abs() > f64::EPSILON { if m.fract() != 0.0 { m *= b as f64; e -= 1; } else { break; } } (ms, m as u64, b, e) } rusticata-asn1-rs-07e764c/src/asn1_types/real/000077500000000000000000000000001476576401200211505ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/real/f32.rs000066400000000000000000000012471476576401200221140ustar00rootroot00000000000000use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged}; use core::convert::{TryFrom, TryInto}; impl<'a> TryFrom> for f32 { type Error = Error; fn try_from(any: Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; let real: Real = any.try_into()?; Ok(real.f32()) } } impl CheckDerConstraints for f32 { fn check_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; any.header.length.assert_definite()?; Ok(()) } } impl DerAutoDerive for f32 {} impl Tagged for f32 { const TAG: Tag = Tag::RealType; } rusticata-asn1-rs-07e764c/src/asn1_types/real/f64.rs000066400000000000000000000012471476576401200221210ustar00rootroot00000000000000use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged}; use core::convert::{TryFrom, TryInto}; impl<'a> TryFrom> for f64 { type Error = Error; fn try_from(any: Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; any.header.assert_primitive()?; let real: Real = any.try_into()?; Ok(real.f64()) } } impl CheckDerConstraints for f64 { fn check_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; any.header.length.assert_definite()?; Ok(()) } } impl DerAutoDerive for f64 {} impl Tagged for f64 { const TAG: Tag = Tag::RealType; } rusticata-asn1-rs-07e764c/src/asn1_types/sequence.rs000066400000000000000000000312261476576401200224070ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; mod iterator; mod sequence_of; mod vec; pub use iterator::*; pub use sequence_of::*; /// The `SEQUENCE` object is an ordered list of heteregeneous types. /// /// Sequences can usually be of 2 types: /// - a list of different objects (`SEQUENCE`, usually parsed as a `struct`) /// - a list of similar objects (`SEQUENCE OF`, usually parsed as a `Vec`) /// /// The current object covers the former. For the latter, see the [`SequenceOf`] documentation. /// /// The `Sequence` object contains the (*unparsed*) encoded representation of its content. It provides /// methods to parse and iterate contained objects, or convert the sequence to other types. /// /// # Building a Sequence /// /// To build a DER sequence: /// - if the sequence is composed of objects of the same type, the [`Sequence::from_iter_to_der`] method can be used /// - otherwise, the [`ToDer`] trait can be used to create content incrementally /// #[cfg_attr(feature = "std", doc = r#"```"#)] #[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] /// use asn1_rs::{Integer, Sequence, SerializeResult, ToDer}; /// /// fn build_seq<'a>() -> SerializeResult> { /// let mut v = Vec::new(); /// // add an Integer object (construct type): /// let i = Integer::from_u32(4); /// let _ = i.write_der(&mut v)?; /// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`: /// let _ = "abcd".write_der(&mut v)?; /// // return the sequence built from the DER content /// Ok(Sequence::new(v.into())) /// } /// /// let seq = build_seq().unwrap(); /// /// ``` /// /// # Examples /// #[cfg_attr(feature = "std", doc = r#"```"#)] #[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] /// use asn1_rs::{Error, Sequence}; /// /// // build sequence /// let it = [2, 3, 4].iter(); /// let seq = Sequence::from_iter_to_der(it).unwrap(); /// /// // `seq` now contains the serialized DER representation of the array /// /// // iterate objects /// let mut sum = 0; /// for item in seq.der_iter::() { /// // item has type `Result`, since parsing the serialized bytes could fail /// sum += item.expect("parsing list item failed"); /// } /// assert_eq!(sum, 9); /// /// ``` /// /// Note: the above example encodes a `SEQUENCE OF INTEGER` object, the [`SequenceOf`] object could /// be used to provide a simpler API. /// #[derive(Clone, Debug, PartialEq, Eq)] pub struct Sequence<'a> { /// Serialized DER representation of the sequence content pub content: Cow<'a, [u8]>, } impl<'a> Sequence<'a> { /// Build a sequence, given the provided content pub const fn new(content: Cow<'a, [u8]>) -> Self { Sequence { content } } /// Consume the sequence and return the content #[inline] pub fn into_content(self) -> Cow<'a, [u8]> { self.content } /// Apply the parsing function to the sequence content, consuming the sequence /// /// Note: this function expects the caller to take ownership of content. /// In some cases, handling the lifetime of objects is not easy (when keeping only references on /// data). Other methods are provided (depending on the use case): /// - [`Sequence::parse`] takes a reference on the sequence data, but does not consume it, /// - [`Sequence::from_der_and_then`] does the parsing of the sequence and applying the function /// in one step, ensuring there are only references (and dropping the temporary sequence). pub fn and_then(self, op: F) -> ParseResult<'a, U, E> where F: FnOnce(Cow<'a, [u8]>) -> ParseResult<'a, U, E>, { op(self.content) } /// Same as [`Sequence::from_der_and_then`], but using BER encoding (no constraints). pub fn from_ber_and_then(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>, E: From, { let (rem, seq) = Sequence::from_ber(bytes).map_err(Err::convert)?; let data = match seq.content { Cow::Borrowed(b) => b, // Since 'any' is built from 'bytes', it is borrowed by construction Cow::Owned(_) => unreachable!(), }; let (_, res) = op(data)?; Ok((rem, res)) } /// Parse a DER sequence and apply the provided parsing function to content /// /// After parsing, the sequence object and header are discarded. /// /// ``` /// use asn1_rs::{FromDer, ParseResult, Sequence}; /// /// // Parse a SEQUENCE { /// // a INTEGER (0..255), /// // b INTEGER (0..4294967296) /// // } /// // and return only `(a,b) /// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> { /// Sequence::from_der_and_then(i, |i| { /// let (i, a) = u8::from_der(i)?; /// let (i, b) = u32::from_der(i)?; /// Ok((i, (a, b))) /// } /// ) /// } /// ``` pub fn from_der_and_then(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>, E: From, { let (rem, seq) = Sequence::from_der(bytes).map_err(Err::convert)?; let data = match seq.content { Cow::Borrowed(b) => b, // Since 'any' is built from 'bytes', it is borrowed by construction Cow::Owned(_) => unreachable!(), }; let (_, res) = op(data)?; Ok((rem, res)) } /// Apply the parsing function to the sequence content (non-consuming version) pub fn parse(&'a self, mut f: F) -> ParseResult<'a, T, E> where F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, { let input: &[u8] = &self.content; f(input) } /// Apply the parsing function to the sequence content (consuming version) /// /// Note: to parse and apply a parsing function in one step, use the /// [`Sequence::from_der_and_then`] method. /// /// # Limitations /// /// This function fails if the sequence contains `Owned` data, because the parsing function /// takes a reference on data (which is dropped). pub fn parse_into(self, mut f: F) -> ParseResult<'a, T, E> where F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, E: From, { match self.content { Cow::Borrowed(b) => f(b), _ => Err(Err::Error(Error::LifetimeError.into())), } } /// Return an iterator over the sequence content, attempting to decode objects as BER /// /// This method can be used when all objects from the sequence have the same type. pub fn ber_iter(&'a self) -> SequenceIterator<'a, T, BerParser, E> where T: FromBer<'a, E>, { SequenceIterator::new(&self.content) } /// Return an iterator over the sequence content, attempting to decode objects as DER /// /// This method can be used when all objects from the sequence have the same type. pub fn der_iter(&'a self) -> SequenceIterator<'a, T, DerParser, E> where T: FromDer<'a, E>, { SequenceIterator::new(&self.content) } /// Attempt to parse the sequence as a `SEQUENCE OF` items (BER), and return the parsed items as a `Vec`. pub fn ber_sequence_of(&'a self) -> Result, E> where T: FromBer<'a, E>, E: From, { self.ber_iter().collect() } /// Attempt to parse the sequence as a `SEQUENCE OF` items (DER), and return the parsed items as a `Vec`. pub fn der_sequence_of(&'a self) -> Result, E> where T: FromDer<'a, E>, E: From, { self.der_iter().collect() } /// Attempt to parse the sequence as a `SEQUENCE OF` items (BER) (consuming input), /// and return the parsed items as a `Vec`. /// /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. pub fn into_ber_sequence_of(self) -> Result, E> where for<'b> T: FromBer<'b, E>, E: From, T: ToStatic, { match self.content { Cow::Borrowed(bytes) => SequenceIterator::::new(bytes).collect(), Cow::Owned(data) => { let v1 = SequenceIterator::::new(&data) .collect::, E>>()?; let v2 = v1.iter().map(|t| t.to_static()).collect::>(); Ok(v2) } } } /// Attempt to parse the sequence as a `SEQUENCE OF` items (DER) (consuming input), /// and return the parsed items as a `Vec`. /// /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. pub fn into_der_sequence_of(self) -> Result, E> where for<'b> T: FromDer<'b, E>, E: From, T: ToStatic, { match self.content { Cow::Borrowed(bytes) => SequenceIterator::::new(bytes).collect(), Cow::Owned(data) => { let v1 = SequenceIterator::::new(&data) .collect::, E>>()?; let v2 = v1.iter().map(|t| t.to_static()).collect::>(); Ok(v2) } } } pub fn into_der_sequence_of_ref(self) -> Result, E> where T: FromDer<'a, E>, E: From, { match self.content { Cow::Borrowed(bytes) => SequenceIterator::::new(bytes).collect(), Cow::Owned(_) => Err(Error::LifetimeError.into()), } } } impl ToStatic for Sequence<'_> { type Owned = Sequence<'static>; fn to_static(&self) -> Self::Owned { Sequence { content: Cow::Owned(self.content.to_vec()), } } } impl ToStatic for Vec where T: ToStatic, U: 'static, { type Owned = Vec; fn to_static(&self) -> Self::Owned { self.iter().map(|t| t.to_static()).collect() } } impl AsRef<[u8]> for Sequence<'_> { fn as_ref(&self) -> &[u8] { &self.content } } impl<'a> TryFrom> for Sequence<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result> { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Sequence<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; Ok(Sequence { content: Cow::Borrowed(any.data), }) } } impl CheckDerConstraints for Sequence<'_> { fn check_constraints(_any: &Any) -> Result<()> { // TODO: iterate on ANY objects and check constraints? -> this will not be exhaustive // test, for ex INTEGER encoding will not be checked Ok(()) } } impl DerAutoDerive for Sequence<'_> {} impl Tagged for Sequence<'_> { const TAG: Tag = Tag::Sequence; } #[cfg(feature = "std")] impl ToDer for Sequence<'_> { fn to_der_len(&self) -> Result { let sz = self.content.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, true, Self::TAG, Length::Definite(self.content.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&self.content).map_err(Into::into) } } #[cfg(feature = "std")] impl Sequence<'_> { /// Attempt to create a `Sequence` from an iterator over serializable objects (to DER) /// /// # Examples /// /// ``` /// use asn1_rs::Sequence; /// /// // build sequence /// let it = [2, 3, 4].iter(); /// let seq = Sequence::from_iter_to_der(it).unwrap(); /// ``` pub fn from_iter_to_der(it: IT) -> SerializeResult where IT: Iterator, T: ToDer, T: Tagged, { let mut v = Vec::new(); for item in it { let item_v = ::to_der_vec(&item)?; v.extend_from_slice(&item_v); } Ok(Sequence { content: Cow::Owned(v), }) } } rusticata-asn1-rs-07e764c/src/asn1_types/sequence/000077500000000000000000000000001476576401200220355ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/sequence/iterator.rs000066400000000000000000000055121476576401200242370ustar00rootroot00000000000000use crate::{ASN1Parser, BerParser, DerParser, Error, FromBer, FromDer}; use core::marker::PhantomData; /// An Iterator over binary data, parsing elements of type `T` /// /// This helps parsing `SEQUENCE OF` items of type `T`. The type of parser /// (BER/DER) is specified using the generic parameter `F` of this struct. /// /// Note: the iterator must start on the sequence *contents*, not the sequence itself. /// /// # Examples /// /// ```rust /// use asn1_rs::{DerParser, Integer, SequenceIterator}; /// /// let data = &[0x30, 0x6, 0x2, 0x1, 0x1, 0x2, 0x1, 0x2]; /// for (idx, item) in SequenceIterator::::new(&data[2..]).enumerate() { /// let item = item.unwrap(); // parsing could have failed /// let i = item.as_u32().unwrap(); // integer can be negative, or too large to fit into u32 /// assert_eq!(i as usize, idx + 1); /// } /// ``` #[derive(Debug)] pub struct SequenceIterator<'a, T, F, E = Error> where F: ASN1Parser, { data: &'a [u8], has_error: bool, _t: PhantomData, _f: PhantomData, _e: PhantomData, } impl<'a, T, F, E> SequenceIterator<'a, T, F, E> where F: ASN1Parser, { pub fn new(data: &'a [u8]) -> Self { SequenceIterator { data, has_error: false, _t: PhantomData, _f: PhantomData, _e: PhantomData, } } } impl<'a, T, E> Iterator for SequenceIterator<'a, T, BerParser, E> where T: FromBer<'a, E>, E: From, { type Item = Result; fn next(&mut self) -> Option { if self.has_error || self.data.is_empty() { return None; } match T::from_ber(self.data) { Ok((rem, obj)) => { self.data = rem; Some(Ok(obj)) } Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => { self.has_error = true; Some(Err(e)) } Err(nom::Err::Incomplete(n)) => { self.has_error = true; Some(Err(Error::Incomplete(n).into())) } } } } impl<'a, T, E> Iterator for SequenceIterator<'a, T, DerParser, E> where T: FromDer<'a, E>, E: From, { type Item = Result; fn next(&mut self) -> Option { if self.has_error || self.data.is_empty() { return None; } match T::from_der(self.data) { Ok((rem, obj)) => { self.data = rem; Some(Ok(obj)) } Err(nom::Err::Error(e)) | Err(nom::Err::Failure(e)) => { self.has_error = true; Some(Err(e)) } Err(nom::Err::Incomplete(n)) => { self.has_error = true; Some(Err(Error::Incomplete(n).into())) } } } } rusticata-asn1-rs-07e764c/src/asn1_types/sequence/sequence_of.rs000066400000000000000000000116321476576401200247020ustar00rootroot00000000000000use crate::*; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; use core::fmt::{Debug, Display}; use core::iter::FromIterator; use core::ops::{Deref, DerefMut}; use self::debug::{trace, trace_generic}; /// The `SEQUENCE OF` object is an ordered list of homogeneous types. /// /// This type implements `Deref` and `DerefMut`, so all methods /// like `.iter()`, `.len()` and others can be used transparently as if using a vector. /// /// # Examples /// /// ``` /// use asn1_rs::SequenceOf; /// use std::iter::FromIterator; /// /// // build set /// let seq = SequenceOf::from_iter([2, 3, 4]); /// /// // `seq` now contains the serialized DER representation of the array /// /// // iterate objects /// let mut sum = 0; /// for item in seq.iter() { /// // item has type `Result`, since parsing the serialized bytes could fail /// sum += *item; /// } /// assert_eq!(sum, 9); /// /// ``` #[derive(Debug, PartialEq)] pub struct SequenceOf { pub(crate) items: Vec, } impl SequenceOf { /// Builds a `SEQUENCE OF` from the provided content #[inline] pub const fn new(items: Vec) -> Self { SequenceOf { items } } /// Converts `self` into a vector without clones or allocation. #[inline] pub fn into_vec(self) -> Vec { self.items } /// Appends an element to the back of a collection #[inline] pub fn push(&mut self, item: T) { self.items.push(item) } } impl AsRef<[T]> for SequenceOf { fn as_ref(&self) -> &[T] { &self.items } } impl AsMut<[T]> for SequenceOf { fn as_mut(&mut self) -> &mut [T] { &mut self.items } } impl Deref for SequenceOf { type Target = [T]; fn deref(&self) -> &Self::Target { &self.items } } impl DerefMut for SequenceOf { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items } } impl From> for Vec { fn from(seq: SequenceOf) -> Self { seq.items } } impl FromIterator for SequenceOf { fn from_iter>(iter: IT) -> Self { let items = Vec::from_iter(iter); SequenceOf::new(items) } } impl<'a, T> TryFrom> for SequenceOf where T: FromBer<'a>, { type Error = Error; fn try_from(any: Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; if !any.header.is_constructed() { return Err(Error::ConstructExpected); } let items = SequenceIterator::::new(any.data).collect::>>()?; Ok(SequenceOf::new(items)) } } impl CheckDerConstraints for SequenceOf where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; for item in SequenceIterator::::new(any.data) { let item = item?; T::check_constraints(&item)?; } Ok(()) } } /// manual impl of FromDer, so we do not need to require `TryFrom + CheckDerConstraints` impl<'a, T, E> FromDer<'a, E> for SequenceOf where T: FromDer<'a, E>, E: From + Display + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { trace_generic( core::any::type_name::(), "SequenceOf::from_der", |bytes| { let (rem, any) = trace(core::any::type_name::(), parse_der_any, bytes) .map_err(Err::convert)?; any.header .assert_tag(Self::TAG) .map_err(|e| Err::Error(e.into()))?; let items = SequenceIterator::::new(any.data) .collect::, E>>() .map_err(Err::Error)?; Ok((rem, SequenceOf::new(items))) }, bytes, ) } } impl Tagged for SequenceOf { const TAG: Tag = Tag::Sequence; } #[cfg(feature = "std")] impl ToDer for SequenceOf where T: ToDer, { fn to_der_len(&self) -> Result { self.items.to_der_len() } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.items.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.items.write_der_content(writer) } } #[cfg(test)] mod tests { use crate::SequenceOf; use core::iter::FromIterator; /// Test use of object, available methods and syntax for different use cases #[test] fn use_sequence_of() { let mut set = SequenceOf::from_iter([1, 2, 3]); set.push(4); // deref as slice let sum: i32 = set.iter().sum(); assert_eq!(sum, 10); // range operator assert_eq!(&set[1..3], &[2, 3]); } } rusticata-asn1-rs-07e764c/src/asn1_types/sequence/vec.rs000066400000000000000000000120171476576401200231610ustar00rootroot00000000000000use crate::*; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; use core::fmt::Debug; use self::debug::{macros::debug_eprintln, trace, trace_generic}; // // XXX this compiles but requires bound TryFrom :/ // impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec // where // T: TryFrom<&'b Any<'a>>, // for<'e> >>::Error: From, // T: FromBer<'a, >>::Error>, // // T: FromBer<'a, E>, // // E: From, // { // type Error = >>::Error; // fn try_from(any: &'b Any<'a>) -> Result, Self::Error> { // any.tag().assert_eq(Self::TAG)?; // any.header.assert_constructed()?; // let v = SequenceIterator::::new(any.data) // .collect::, Self::Error>>()?; // Ok(v) // } // } // // XXX this compiles but requires bound TryFrom :/ // impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec // where // T: TryFrom<&'b Any<'a>>, // >>::Error: From, // T: FromBer<'a, >>::Error>, // // T: FromBer<'a, E>, // // E: From, // { // type Error = >>::Error; // fn try_from(any: &'b Any<'a>) -> Result, Self::Error> { // any.tag().assert_eq(Self::TAG)?; // any.header.assert_constructed()?; // let v = SequenceIterator::::new(any.data) // .collect::, Self::Error>>()?; // Ok(v) // } // } impl<'a, T> TryFrom> for Vec where T: FromBer<'a>, { type Error = Error; fn try_from(any: Any<'a>) -> Result { trace_generic( core::any::type_name::(), "T::from(Any)", |any| { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; let res_items: Result> = SetIterator::::new(any.data).collect(); if res_items.is_err() { debug_eprintln!( core::any::type_name::(), "≠ {}", "Conversion from Any failed".red() ); } res_items }, any, ) } } impl CheckDerConstraints for Vec where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; for item in SequenceIterator::::new(any.data) { let item = item?; ::check_constraints(&item)?; } Ok(()) } } impl Tagged for Vec { const TAG: Tag = Tag::Sequence; } // impl<'a, T> FromBer<'a> for Vec // where // T: FromBer<'a>, // { // fn from_ber(bytes: &'a [u8]) -> ParseResult { // let (rem, any) = Any::from_ber(bytes)?; // any.header.assert_tag(Self::TAG)?; // let v = SequenceIterator::::new(any.data).collect::>>()?; // Ok((rem, v)) // } // } /// manual impl of FromDer, so we do not need to require `TryFrom + CheckDerConstraints` impl<'a, T, E> FromDer<'a, E> for Vec where T: FromDer<'a, E>, E: From + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { trace_generic( core::any::type_name::(), "Sequence::from_der", |bytes| { let (rem, any) = trace(core::any::type_name::(), parse_der_any, bytes) .map_err(Err::convert)?; any.header .assert_tag(Self::TAG) .map_err(|e| Err::Error(e.into()))?; let v = SequenceIterator::::new(any.data) .collect::, E>>() .map_err(Err::Error)?; Ok((rem, v)) }, bytes, ) } } #[cfg(feature = "std")] impl ToDer for Vec where T: ToDer, { fn to_der_len(&self) -> Result { let mut len = 0; for t in self.iter() { len += t.to_der_len()?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); Ok(header.to_der_len()? + len) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut len = 0; for t in self.iter() { len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut sz = 0; for t in self.iter() { sz += t.write_der(writer)?; } Ok(sz) } } rusticata-asn1-rs-07e764c/src/asn1_types/set.rs000066400000000000000000000275551476576401200214040ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; mod btreeset; mod hashset; mod iterator; mod set_of; pub use iterator::*; pub use set_of::*; /// The `SET` object is an unordered list of heteregeneous types. /// /// Sets can usually be of 2 types: /// - a list of different objects (`SET`, usually parsed as a `struct`) /// - a list of similar objects (`SET OF`, usually parsed as a `BTreeSet` or `HashSet`) /// /// The current object covers the former. For the latter, see the [`SetOf`] documentation. /// /// The `Set` object contains the (*unparsed*) encoded representation of its content. It provides /// methods to parse and iterate contained objects, or convert the sequence to other types. /// /// # Building a Set /// /// To build a DER set: /// - if the set is composed of objects of the same type, the [`Set::from_iter_to_der`] method can be used /// - otherwise, the [`ToDer`] trait can be used to create content incrementally /// #[cfg_attr(feature = "std", doc = r#"```"#)] #[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] /// use asn1_rs::{Integer, Set, SerializeResult, ToDer}; /// /// fn build_set<'a>() -> SerializeResult> { /// let mut v = Vec::new(); /// // add an Integer object (construct type): /// let i = Integer::from_u32(4); /// let _ = i.write_der(&mut v)?; /// // some primitive objects also implement `ToDer`. A string will be mapped as `Utf8String`: /// let _ = "abcd".write_der(&mut v)?; /// // return the set built from the DER content /// Ok(Set::new(v.into())) /// } /// /// let seq = build_set().unwrap(); /// /// ``` /// /// # Examples /// #[cfg_attr(feature = "std", doc = r#"```"#)] #[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] /// use asn1_rs::{Error, Set}; /// /// // build set /// let it = [2, 3, 4].iter(); /// let set = Set::from_iter_to_der(it).unwrap(); /// /// // `set` now contains the serialized DER representation of the array /// /// // iterate objects /// let mut sum = 0; /// for item in set.der_iter::() { /// // item has type `Result`, since parsing the serialized bytes could fail /// sum += item.expect("parsing list item failed"); /// } /// assert_eq!(sum, 9); /// /// ``` /// /// Note: the above example encodes a `SET OF INTEGER` object, the [`SetOf`] object could /// be used to provide a simpler API. /// #[derive(Clone, Debug)] pub struct Set<'a> { /// Serialized DER representation of the set content pub content: Cow<'a, [u8]>, } impl<'a> Set<'a> { /// Build a set, given the provided content pub const fn new(content: Cow<'a, [u8]>) -> Self { Set { content } } /// Consume the set and return the content #[inline] pub fn into_content(self) -> Cow<'a, [u8]> { self.content } /// Apply the parsing function to the set content, consuming the set /// /// Note: this function expects the caller to take ownership of content. /// In some cases, handling the lifetime of objects is not easy (when keeping only references on /// data). Other methods are provided (depending on the use case): /// - [`Set::parse`] takes a reference on the set data, but does not consume it, /// - [`Set::from_der_and_then`] does the parsing of the set and applying the function /// in one step, ensuring there are only references (and dropping the temporary set). pub fn and_then(self, op: F) -> ParseResult<'a, U, E> where F: FnOnce(Cow<'a, [u8]>) -> ParseResult<'a, U, E>, { op(self.content) } /// Same as [`Set::from_der_and_then`], but using BER encoding (no constraints). pub fn from_ber_and_then(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>, E: From, { let (rem, seq) = Set::from_ber(bytes).map_err(Err::convert)?; let data = match seq.content { Cow::Borrowed(b) => b, // Since 'any' is built from 'bytes', it is borrowed by construction Cow::Owned(_) => unreachable!(), }; let (_, res) = op(data)?; Ok((rem, res)) } /// Parse a DER set and apply the provided parsing function to content /// /// After parsing, the set object and header are discarded. /// /// ``` /// use asn1_rs::{FromDer, ParseResult, Set}; /// /// // Parse a SET { /// // a INTEGER (0..255), /// // b INTEGER (0..4294967296) /// // } /// // and return only `(a,b) /// fn parser(i: &[u8]) -> ParseResult<(u8, u32)> { /// Set::from_der_and_then(i, |i| { /// let (i, a) = u8::from_der(i)?; /// let (i, b) = u32::from_der(i)?; /// Ok((i, (a, b))) /// } /// ) /// } /// ``` pub fn from_der_and_then(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>, E: From, { let (rem, seq) = Set::from_der(bytes).map_err(Err::convert)?; let data = match seq.content { Cow::Borrowed(b) => b, // Since 'any' is built from 'bytes', it is borrowed by construction Cow::Owned(_) => unreachable!(), }; let (_, res) = op(data)?; Ok((rem, res)) } /// Apply the parsing function to the set content (non-consuming version) pub fn parse(&'a self, mut f: F) -> ParseResult<'a, T, E> where F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, { let input: &[u8] = &self.content; f(input) } /// Apply the parsing function to the set content (consuming version) /// /// Note: to parse and apply a parsing function in one step, use the /// [`Set::from_der_and_then`] method. /// /// # Limitations /// /// This function fails if the set contains `Owned` data, because the parsing function /// takes a reference on data (which is dropped). pub fn parse_into(self, mut f: F) -> ParseResult<'a, T, E> where F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>, E: From, { match self.content { Cow::Borrowed(b) => f(b), _ => Err(Err::Error(Error::LifetimeError.into())), } } /// Return an iterator over the set content, attempting to decode objects as BER /// /// This method can be used when all objects from the set have the same type. pub fn ber_iter(&'a self) -> SetIterator<'a, T, BerParser, E> where T: FromBer<'a, E>, { SetIterator::new(&self.content) } /// Return an iterator over the set content, attempting to decode objects as DER /// /// This method can be used when all objects from the set have the same type. pub fn der_iter(&'a self) -> SetIterator<'a, T, DerParser, E> where T: FromDer<'a, E>, { SetIterator::new(&self.content) } /// Attempt to parse the set as a `SET OF` items (BER), and return the parsed items as a `Vec`. pub fn ber_set_of(&'a self) -> Result, E> where T: FromBer<'a, E>, E: From, { self.ber_iter().collect() } /// Attempt to parse the set as a `SET OF` items (DER), and return the parsed items as a `Vec`. pub fn der_set_of(&'a self) -> Result, E> where T: FromDer<'a, E>, E: From, { self.der_iter().collect() } /// Attempt to parse the set as a `SET OF` items (BER) (consuming input), /// and return the parsed items as a `Vec`. /// /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. pub fn into_ber_set_of(self) -> Result, E> where for<'b> T: FromBer<'b, E>, E: From, T: ToStatic, { match self.content { Cow::Borrowed(bytes) => SetIterator::::new(bytes).collect(), Cow::Owned(data) => { let v1 = SetIterator::::new(&data).collect::, E>>()?; let v2 = v1.iter().map(|t| t.to_static()).collect::>(); Ok(v2) } } } /// Attempt to parse the set as a `SET OF` items (DER) (consuming input), /// and return the parsed items as a `Vec`. /// /// Note: if `Self` is an `Owned` object, the data will be duplicated (causing allocations) into separate objects. pub fn into_der_set_of(self) -> Result, E> where for<'b> T: FromDer<'b, E>, E: From, T: ToStatic, { match self.content { Cow::Borrowed(bytes) => SetIterator::::new(bytes).collect(), Cow::Owned(data) => { let v1 = SetIterator::::new(&data).collect::, E>>()?; let v2 = v1.iter().map(|t| t.to_static()).collect::>(); Ok(v2) } } } pub fn into_der_set_of_ref(self) -> Result, E> where T: FromDer<'a, E>, E: From, { match self.content { Cow::Borrowed(bytes) => SetIterator::::new(bytes).collect(), Cow::Owned(_) => Err(Error::LifetimeError.into()), } } } impl ToStatic for Set<'_> { type Owned = Set<'static>; fn to_static(&self) -> Self::Owned { Set { content: Cow::Owned(self.content.to_vec()), } } } impl AsRef<[u8]> for Set<'_> { fn as_ref(&self) -> &[u8] { &self.content } } impl<'a> TryFrom> for Set<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result> { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for Set<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; Ok(Set { content: Cow::Borrowed(any.data), }) } } impl CheckDerConstraints for Set<'_> { fn check_constraints(_any: &Any) -> Result<()> { Ok(()) } } impl DerAutoDerive for Set<'_> {} impl Tagged for Set<'_> { const TAG: Tag = Tag::Set; } #[cfg(feature = "std")] impl ToDer for Set<'_> { fn to_der_len(&self) -> Result { let sz = self.content.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, true, Self::TAG, Length::Definite(self.content.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(&self.content).map_err(Into::into) } } #[cfg(feature = "std")] impl Set<'_> { /// Attempt to create a `Set` from an iterator over serializable objects (to DER) /// /// # Examples /// /// ``` /// use asn1_rs::Set; /// /// // build set /// let it = [2, 3, 4].iter(); /// let seq = Set::from_iter_to_der(it).unwrap(); /// ``` pub fn from_iter_to_der(it: IT) -> SerializeResult where IT: Iterator, T: ToDer, T: Tagged, { let mut v = Vec::new(); for item in it { let item_v = ::to_der_vec(&item)?; v.extend_from_slice(&item_v); } Ok(Set { content: Cow::Owned(v), }) } } rusticata-asn1-rs-07e764c/src/asn1_types/set/000077500000000000000000000000001476576401200210205ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/set/btreeset.rs000066400000000000000000000100361476576401200232030ustar00rootroot00000000000000use crate::*; use alloc::collections::BTreeSet; use core::{convert::TryFrom, fmt::Debug}; use self::debug::{trace, trace_generic}; impl Tagged for BTreeSet { const TAG: Tag = Tag::Set; } impl<'a, T> TryFrom> for BTreeSet where T: FromBer<'a>, T: Ord, { type Error = Error; fn try_from(any: Any<'a>) -> Result { trace_generic( core::any::type_name::(), "BTreeSet::from_der", |any| { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; let items = SetIterator::::new(any.data).collect::>>()?; Ok(items) }, any, ) } } impl CheckDerConstraints for BTreeSet where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; for item in SetIterator::::new(any.data) { let item = item?; T::check_constraints(&item)?; } Ok(()) } } /// manual impl of FromDer, so we do not need to require `TryFrom + CheckDerConstraints` impl<'a, T, E> FromDer<'a, E> for BTreeSet where T: FromDer<'a, E>, T: Ord, E: From + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { trace_generic( core::any::type_name::(), "BTreeSet::from_der", |bytes| { let (rem, any) = trace(core::any::type_name::(), Any::from_der, bytes) .map_err(Err::convert)?; any.tag() .assert_eq(Self::TAG) .map_err(|e| Err::Error(e.into()))?; any.header .assert_constructed() .map_err(|e| Err::Error(e.into()))?; let items = SetIterator::::new(any.data) .collect::, E>>() .map_err(Err::Error)?; Ok((rem, items)) }, bytes, ) } } #[cfg(feature = "std")] impl ToDer for BTreeSet where T: ToDer, { fn to_der_len(&self) -> Result { let mut len = 0; for t in self.iter() { len += t.to_der_len()?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); Ok(header.to_der_len()? + len) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut len = 0; for t in self.iter() { len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut sz = 0; for t in self.iter() { sz += t.write_der(writer)?; } Ok(sz) } } #[cfg(all(test, feature = "std"))] mod tests { use crate::*; use core::convert::TryFrom; use hex_literal::hex; use std::collections::BTreeSet; #[test] fn ber_btreeset() { let input = &hex! {"31 06 02 01 00 02 01 01"}; let (_, any) = Any::from_ber(input).expect("parsing hashset failed"); >::check_constraints(&any).unwrap(); let h = >::try_from(any).unwrap(); assert_eq!(h.len(), 2); } #[test] fn der_btreeset() { let input = &hex! {"31 06 02 01 00 02 01 01"}; let r: IResult<_, _, Error> = BTreeSet::::from_der(input); let (_, h) = r.expect("parsing hashset failed"); assert_eq!(h.len(), 2); assert_eq!(h.to_der_len(), Ok(8)); let v = h.to_der_vec().expect("could not serialize"); let (_, h2) = SetOf::::from_der(&v).unwrap(); assert!(h.iter().eq(h2.iter())); } } rusticata-asn1-rs-07e764c/src/asn1_types/set/hashset.rs000066400000000000000000000075121476576401200230320ustar00rootroot00000000000000#![cfg(feature = "std")] use crate::*; use core::fmt::Debug; use std::collections::HashSet; use std::convert::TryFrom; use std::hash::Hash; use self::debug::{trace, trace_generic}; impl Tagged for HashSet { const TAG: Tag = Tag::Set; } impl<'a, T> TryFrom> for HashSet where T: FromBer<'a>, T: Hash + Eq, { type Error = Error; fn try_from(any: Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; let items = SetIterator::::new(any.data).collect::>>()?; Ok(items) } } impl CheckDerConstraints for HashSet where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; for item in SetIterator::::new(any.data) { let item = item?; T::check_constraints(&item)?; } Ok(()) } } /// manual impl of FromDer, so we do not need to require `TryFrom + CheckDerConstraints` impl<'a, T, E> FromDer<'a, E> for HashSet where T: FromDer<'a, E>, T: Hash + Eq, E: From + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { trace_generic( core::any::type_name::(), "BTreeSet::from_der", |bytes| { let (rem, any) = trace(core::any::type_name::(), Any::from_der, bytes) .map_err(Err::convert)?; any.tag() .assert_eq(Self::TAG) .map_err(|e| Err::Error(e.into()))?; any.header .assert_constructed() .map_err(|e| Err::Error(e.into()))?; let items = SetIterator::::new(any.data) .collect::, E>>() .map_err(Err::Error)?; Ok((rem, items)) }, bytes, ) } } impl ToDer for HashSet where T: ToDer, { fn to_der_len(&self) -> Result { let mut len = 0; for t in self.iter() { len += t.to_der_len()?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); Ok(header.to_der_len()? + len) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut len = 0; for t in self.iter() { len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut sz = 0; for t in self.iter() { sz += t.write_der(writer)?; } Ok(sz) } } #[cfg(test)] mod tests { use crate::*; use core::convert::TryFrom; use hex_literal::hex; use std::collections::HashSet; #[test] fn ber_hashset() { let input = &hex! {"31 06 02 01 00 02 01 01"}; let (_, any) = Any::from_ber(input).expect("parsing hashset failed"); >::check_constraints(&any).unwrap(); let h = >::try_from(any).unwrap(); assert_eq!(h.len(), 2); } #[test] fn der_hashset() { let input = &hex! {"31 06 02 01 00 02 01 01"}; let r: IResult<_, _, Error> = HashSet::::from_der(input); let (_, h) = r.expect("parsing hashset failed"); assert_eq!(h.len(), 2); assert_eq!(h.to_der_len(), Ok(8)); let v = h.to_der_vec().expect("could not serialize"); let (_, h2) = SetOf::::from_der(&v).unwrap(); assert!(h.iter().eq(h2.iter())); } } rusticata-asn1-rs-07e764c/src/asn1_types/set/iterator.rs000066400000000000000000000015401476576401200232170ustar00rootroot00000000000000pub use crate::{Error, SequenceIterator}; /// An Iterator over binary data, parsing elements of type `T` /// /// This helps parsing `SET OF` items of type `T`. The type of parser /// (BER/DER) is specified using the generic parameter `F` of this struct. /// /// Note: the iterator must start on the set *contents*, not the set itself. /// /// # Examples /// /// ```rust /// use asn1_rs::{DerParser, Integer, SetIterator}; /// /// let data = &[0x30, 0x6, 0x2, 0x1, 0x1, 0x2, 0x1, 0x2]; /// for (idx, item) in SetIterator::::new(&data[2..]).enumerate() { /// let item = item.unwrap(); // parsing could have failed /// let i = item.as_u32().unwrap(); // integer can be negative, or too large to fit into u32 /// assert_eq!(i as usize, idx + 1); /// } /// ``` pub type SetIterator<'a, T, F, E = Error> = SequenceIterator<'a, T, F, E>; rusticata-asn1-rs-07e764c/src/asn1_types/set/set_of.rs000066400000000000000000000112251476576401200226460ustar00rootroot00000000000000use crate::*; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; use core::fmt::{Debug, Display}; use core::iter::FromIterator; use core::ops::{Deref, DerefMut}; use self::debug::{trace, trace_generic}; /// The `SET OF` object is an unordered list of homogeneous types. /// /// This type implements `Deref` and `DerefMut`, so all methods /// like `.iter()`, `.len()` and others can be used transparently as if using a vector. /// /// # Examples /// /// ``` /// use asn1_rs::SetOf; /// use std::iter::FromIterator; /// /// // build set /// let set = SetOf::from_iter([2, 3, 4]); /// /// // `set` now contains the serialized DER representation of the array /// /// // iterate objects /// let mut sum = 0; /// for item in set.iter() { /// // item has type `Result`, since parsing the serialized bytes could fail /// sum += *item; /// } /// assert_eq!(sum, 9); /// /// ``` #[derive(Debug, PartialEq)] pub struct SetOf { items: Vec, } impl SetOf { /// Builds a `SET OF` from the provided content #[inline] pub const fn new(items: Vec) -> Self { SetOf { items } } /// Converts `self` into a vector without clones or allocation. #[inline] pub fn into_vec(self) -> Vec { self.items } /// Appends an element to the back of a collection #[inline] pub fn push(&mut self, item: T) { self.items.push(item) } } impl AsRef<[T]> for SetOf { fn as_ref(&self) -> &[T] { &self.items } } impl AsMut<[T]> for SetOf { fn as_mut(&mut self) -> &mut [T] { &mut self.items } } impl Deref for SetOf { type Target = [T]; fn deref(&self) -> &Self::Target { &self.items } } impl DerefMut for SetOf { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.items } } impl From> for Vec { fn from(set: SetOf) -> Self { set.items } } impl FromIterator for SetOf { fn from_iter>(iter: IT) -> Self { let items = Vec::from_iter(iter); SetOf::new(items) } } impl<'a, T> TryFrom> for SetOf where T: FromBer<'a>, { type Error = Error; fn try_from(any: Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; if !any.header.is_constructed() { return Err(Error::ConstructExpected); } let items = SetIterator::::new(any.data).collect::>>()?; Ok(SetOf::new(items)) } } impl CheckDerConstraints for SetOf where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.tag().assert_eq(Self::TAG)?; any.header.assert_constructed()?; for item in SetIterator::::new(any.data) { let item = item?; T::check_constraints(&item)?; } Ok(()) } } /// manual impl of FromDer, so we do not need to require `TryFrom + CheckDerConstraints` impl<'a, T, E> FromDer<'a, E> for SetOf where T: FromDer<'a, E>, E: From + Display + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { trace_generic( core::any::type_name::(), "SetOf::from_der", |bytes| { let (rem, any) = trace(core::any::type_name::(), Any::from_der, bytes) .map_err(Err::convert)?; any.header .assert_tag(Self::TAG) .map_err(|e| Err::Error(e.into()))?; let items = SetIterator::::new(any.data) .collect::, E>>() .map_err(Err::Error)?; Ok((rem, SetOf::new(items))) }, bytes, ) } } impl Tagged for SetOf { const TAG: Tag = Tag::Set; } #[cfg(feature = "std")] impl ToDer for SetOf where T: ToDer, { fn to_der_len(&self) -> Result { self.items.to_der_len() } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // Do not call self.items.write_der_header(), this will encode the wrong tag (items is a Vec) let mut len = 0; for t in self.items.iter() { len += t.to_der_len().map_err(|_| SerializeError::InvalidLength)?; } let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.items.write_der_content(writer) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings.rs000066400000000000000000000115451476576401200222720ustar00rootroot00000000000000mod bmpstring; mod generalstring; mod graphicstring; mod ia5string; mod numericstring; mod printablestring; mod str; mod string; mod teletexstring; mod universalstring; mod utf8string; mod videotexstring; mod visiblestring; pub use bmpstring::*; pub use generalstring::*; pub use graphicstring::*; pub use ia5string::*; pub use numericstring::*; pub use printablestring::*; pub use teletexstring::*; pub use universalstring::*; pub use utf8string::*; pub use videotexstring::*; pub use visiblestring::*; /// Base trait for BER string objects and character set validation /// /// This trait is implemented by several types, and is used to determine if some bytes /// would be valid for the given type. /// /// # Example /// /// ```rust /// use asn1_rs::{PrintableString, TestValidCharset, VisibleString}; /// /// let bytes: &[u8] = b"abcd*4"; /// let res = PrintableString::test_valid_charset(bytes); /// assert!(res.is_err()); /// let res = VisibleString::test_valid_charset(bytes); /// assert!(res.is_ok()); /// ``` pub trait TestValidCharset { /// Check character set for this object type. fn test_valid_charset(i: &[u8]) -> crate::Result<()>; } #[doc(hidden)] #[macro_export] macro_rules! asn1_string { (IMPL $name:ident, $sname:expr) => { #[doc="ASN.1 restricted character string type (`"] #[doc = $sname] #[doc = "`)"] #[derive(Debug, PartialEq, Eq)] pub struct $name<'a> { pub(crate) data: alloc::borrow::Cow<'a, str>, } impl<'a> $name<'a> { pub const fn new(s: &'a str) -> Self { $name { data: alloc::borrow::Cow::Borrowed(s), } } pub fn string(&self) -> String { use alloc::string::ToString; self.data.to_string() } } impl<'a> AsRef for $name<'a> { fn as_ref(&self) -> &str { &self.data } } impl<'a> From<&'a str> for $name<'a> { fn from(s: &'a str) -> Self { Self::new(s) } } impl From for $name<'_> { fn from(s: String) -> Self { Self { data: alloc::borrow::Cow::Owned(s), } } } impl<'a> core::convert::TryFrom<$crate::Any<'a>> for $name<'a> { type Error = $crate::Error; fn try_from(any: $crate::Any<'a>) -> $crate::Result<$name<'a>> { use core::convert::TryFrom; TryFrom::try_from(&any) } } impl<'a, 'b> core::convert::TryFrom<&'b $crate::Any<'a>> for $name<'a> { type Error = $crate::Error; fn try_from(any: &'b $crate::Any<'a>) -> $crate::Result<$name<'a>> { use $crate::traits::Tagged; use alloc::borrow::Cow; any.tag().assert_eq(Self::TAG)?; <$name>::test_valid_charset(any.data)?; let s = alloc::str::from_utf8(any.data)?; let data = Cow::Borrowed(s); Ok($name { data }) } } impl<'a> $crate::CheckDerConstraints for $name<'a> { fn check_constraints(any: &$crate::Any) -> $crate::Result<()> { any.header.assert_primitive()?; Ok(()) } } impl $crate::DerAutoDerive for $name<'_> {} impl<'a> $crate::Tagged for $name<'a> { const TAG: $crate::Tag = $crate::Tag::$name; } #[cfg(feature = "std")] impl $crate::ToDer for $name<'_> { fn to_der_len(&self) -> Result { let sz = self.data.as_bytes().len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = $crate::Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header( &self, writer: &mut dyn std::io::Write, ) -> $crate::SerializeResult { use $crate::Tagged; let header = $crate::Header::new( $crate::Class::Universal, false, Self::TAG, $crate::Length::Definite(self.data.len()), ); header.write_der_header(writer).map_err(Into::into) } fn write_der_content( &self, writer: &mut dyn std::io::Write, ) -> $crate::SerializeResult { writer.write(self.data.as_bytes()).map_err(Into::into) } } }; ($name:ident) => { asn1_string!(IMPL $name, stringify!($name)); }; } rusticata-asn1-rs-07e764c/src/asn1_types/strings/000077500000000000000000000000001476576401200217165ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/strings/bmpstring.rs000066400000000000000000000072051476576401200242750ustar00rootroot00000000000000// do not use the `asn1_string` macro, since types are not the same // X.680 section 37.15 use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; /// ASN.1 `BMPSTRING` type /// /// Note: parsing a `BmpString` allocates memory since the UTF-16 to UTF-8 conversion requires a memory allocation. /// (see `String::from_utf16` method). #[derive(Debug, PartialEq, Eq)] pub struct BmpString<'a> { pub(crate) data: Cow<'a, str>, } impl<'a> BmpString<'a> { pub const fn new(s: &'a str) -> Self { BmpString { data: Cow::Borrowed(s), } } pub fn string(&self) -> String { self.data.to_string() } } impl AsRef for BmpString<'_> { fn as_ref(&self) -> &str { &self.data } } impl<'a> From<&'a str> for BmpString<'a> { fn from(s: &'a str) -> Self { Self::new(s) } } impl From for BmpString<'_> { fn from(s: String) -> Self { Self { data: Cow::Owned(s), } } } impl<'a, 'r> core::convert::TryFrom<&'r Any<'a>> for BmpString<'a> { type Error = Error; fn try_from(any: &'r Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; // read slice as big-endian UTF-16 string let v = &any .data .chunks(2) .map(|s| match s { [a, b] => ((*a as u16) << 8) | (*b as u16), [a] => *a as u16, _ => unreachable!(), }) .collect::>(); let s = String::from_utf16(v)?; let data = Cow::Owned(s); Ok(BmpString { data }) } } impl<'a> core::convert::TryFrom> for BmpString<'a> { type Error = Error; #[inline] fn try_from(any: Any<'a>) -> Result> { BmpString::try_from(&any) } } impl CheckDerConstraints for BmpString<'_> { fn check_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; Ok(()) } } impl DerAutoDerive for BmpString<'_> {} impl Tagged for BmpString<'_> { const TAG: Tag = Tag::BmpString; } impl TestValidCharset for BmpString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { if i.len() % 2 != 0 { return Err(Error::StringInvalidCharset); } let iter = i.chunks(2).map(|s| ((s[0] as u16) << 8) | (s[1] as u16)); for c in char::decode_utf16(iter) { if c.is_err() { return Err(Error::StringInvalidCharset); } } Ok(()) } } #[cfg(feature = "std")] impl ToDer for BmpString<'_> { fn to_der_len(&self) -> Result { // compute the UTF-16 length let sz = self.data.encode_utf16().count() * 2; if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // compute the UTF-16 length let l = self.data.encode_utf16().count() * 2; let header = Header::new(Class::Universal, false, Self::TAG, Length::Definite(l)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut v = Vec::new(); for u in self.data.encode_utf16() { v.push((u >> 8) as u8); v.push((u & 0xff) as u8); } writer.write(&v).map_err(Into::into) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/generalstring.rs000066400000000000000000000006001476576401200251240ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(GeneralString); impl TestValidCharset for GeneralString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { if !i.iter().all(u8::is_ascii) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/graphicstring.rs000066400000000000000000000006001476576401200251240ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(GraphicString); impl TestValidCharset for GraphicString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { if !i.iter().all(u8::is_ascii) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/ia5string.rs000066400000000000000000000005701476576401200241730ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(Ia5String); impl TestValidCharset for Ia5String<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { if !i.iter().all(u8::is_ascii) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/numericstring.rs000066400000000000000000000010221476576401200251500ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(NumericString); impl TestValidCharset for NumericString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { #[allow(clippy::trivially_copy_pass_by_ref)] fn is_numeric(b: &u8) -> bool { matches!(*b, b'0'..=b'9' | b' ') } if !i.iter().all(is_numeric) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/printablestring.rs000066400000000000000000000016131476576401200254740ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(PrintableString); impl TestValidCharset for PrintableString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { // Argument must be a reference, because of the .iter().all(F) call below #[allow(clippy::trivially_copy_pass_by_ref)] fn is_printable(b: &u8) -> bool { matches!(*b, b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b' ' | b'\'' | b'(' | b')' | b'+' | b',' | b'-' | b'.' | b'/' | b':' | b'=' | b'?') } if !i.iter().all(is_printable) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/str.rs000066400000000000000000000032421476576401200230750ustar00rootroot00000000000000use crate::*; use alloc::borrow::Cow; use core::convert::TryFrom; impl<'a> TryFrom> for &'a str { type Error = Error; fn try_from(any: Any<'a>) -> Result<&'a str> { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for &'a str { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result<&'a str> { any.tag().assert_eq(Self::TAG)?; let s = Utf8String::try_from(any)?; match s.data { Cow::Borrowed(s) => Ok(s), Cow::Owned(_) => Err(Error::LifetimeError), } } } impl CheckDerConstraints for &'_ str { fn check_constraints(any: &Any) -> Result<()> { // X.690 section 10.2 any.header.assert_primitive()?; Ok(()) } } impl DerAutoDerive for &'_ str {} impl Tagged for &'_ str { const TAG: Tag = Tag::Utf8String; } #[cfg(feature = "std")] impl ToDer for &'_ str { fn to_der_len(&self) -> Result { let sz = self.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(self.as_bytes()).map_err(Into::into) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/string.rs000066400000000000000000000031301476576401200235670ustar00rootroot00000000000000use crate::*; #[cfg(not(feature = "std"))] use alloc::string::String; use core::convert::TryFrom; impl<'a> TryFrom> for String { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for String { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; let s = Utf8String::try_from(any)?; Ok(s.data.into_owned()) } } impl CheckDerConstraints for String { fn check_constraints(any: &Any) -> Result<()> { // X.690 section 10.2 any.header.assert_primitive()?; Ok(()) } } impl DerAutoDerive for String {} impl Tagged for String { const TAG: Tag = Tag::Utf8String; } #[cfg(feature = "std")] impl ToDer for String { fn to_der_len(&self) -> Result { let sz = self.len(); if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.len()), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { writer.write(self.as_ref()).map_err(Into::into) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/teletexstring.rs000066400000000000000000000010121476576401200251570ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(TeletexString); impl TestValidCharset for TeletexString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { #[allow(clippy::trivially_copy_pass_by_ref)] fn is_visible(b: &u8) -> bool { 0x20 <= *b && *b <= 0x7f } if !i.iter().all(is_visible) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/universalstring.rs000066400000000000000000000070771476576401200255360ustar00rootroot00000000000000// do not use the `asn1_string` macro, since types are not the same // X.680 section 37.6 and X.690 section 8.21.7 use crate::*; use alloc::borrow::Cow; #[cfg(not(feature = "std"))] use alloc::string::{String, ToString}; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::convert::TryFrom; use core::iter::FromIterator; /// ASN.1 `UniversalString` type /// /// Note: parsing a `UniversalString` allocates memory since the UCS-4 to UTF-8 conversion requires a memory allocation. #[derive(Debug, PartialEq, Eq)] pub struct UniversalString<'a> { pub(crate) data: Cow<'a, str>, } impl<'a> UniversalString<'a> { pub const fn new(s: &'a str) -> Self { UniversalString { data: Cow::Borrowed(s), } } pub fn string(&self) -> String { self.data.to_string() } } impl AsRef for UniversalString<'_> { fn as_ref(&self) -> &str { &self.data } } impl<'a> From<&'a str> for UniversalString<'a> { fn from(s: &'a str) -> Self { Self::new(s) } } impl From for UniversalString<'_> { fn from(s: String) -> Self { Self { data: Cow::Owned(s), } } } impl<'a> TryFrom> for UniversalString<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result> { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for UniversalString<'a> { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result> { any.tag().assert_eq(Self::TAG)?; if any.data.len() % 4 != 0 { return Err(Error::StringInvalidCharset); } // read slice as big-endian UCS-4 string let v = &any .data .chunks(4) .map(|s| match s { [a, b, c, d] => { let u32_val = ((*a as u32) << 24) | ((*b as u32) << 16) | ((*c as u32) << 8) | (*d as u32); char::from_u32(u32_val) } _ => unreachable!(), }) .collect::>>() .ok_or(Error::StringInvalidCharset)?; let s = String::from_iter(v); let data = Cow::Owned(s); Ok(UniversalString { data }) } } impl CheckDerConstraints for UniversalString<'_> { fn check_constraints(any: &Any) -> Result<()> { any.header.assert_primitive()?; Ok(()) } } impl DerAutoDerive for UniversalString<'_> {} impl Tagged for UniversalString<'_> { const TAG: Tag = Tag::UniversalString; } #[cfg(feature = "std")] impl ToDer for UniversalString<'_> { fn to_der_len(&self) -> Result { // UCS-4: 4 bytes per character let sz = self.data.len() * 4; if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let header = Header::new( Class::Universal, false, Self::TAG, Length::Definite(self.data.len() * 4), ); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.data .chars() .try_for_each(|c| writer.write(&(c as u32).to_be_bytes()[..]).map(|_| ()))?; Ok(self.data.len() * 4) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/utf8string.rs000066400000000000000000000004741476576401200244060ustar00rootroot00000000000000use crate::asn1_string; use crate::Result; use crate::TestValidCharset; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(Utf8String); impl TestValidCharset for Utf8String<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { let _ = core::str::from_utf8(i)?; Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/videotexstring.rs000066400000000000000000000010371476576401200253430ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(VideotexString); impl TestValidCharset for VideotexString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { #[allow(clippy::trivially_copy_pass_by_ref)] fn is_visible(b: &u8) -> bool { // XXX 0x20 <= *b && *b <= 0x7f } if !i.iter().all(is_visible) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/strings/visiblestring.rs000066400000000000000000000010121476576401200251420ustar00rootroot00000000000000use crate::{asn1_string, TestValidCharset}; use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::String; asn1_string!(VisibleString); impl TestValidCharset for VisibleString<'_> { fn test_valid_charset(i: &[u8]) -> Result<()> { #[allow(clippy::trivially_copy_pass_by_ref)] fn is_visible(b: &u8) -> bool { 0x20 <= *b && *b <= 0x7f } if !i.iter().all(is_visible) { return Err(Error::StringInvalidCharset); } Ok(()) } } rusticata-asn1-rs-07e764c/src/asn1_types/tagged.rs000066400000000000000000000066311476576401200220340ustar00rootroot00000000000000use crate::{Class, Error, Tag, Tagged}; use core::marker::PhantomData; mod application; mod builder; mod explicit; mod helpers; mod implicit; mod optional; mod parser; mod private; pub use application::*; pub use builder::*; pub use explicit::*; pub use helpers::*; pub use implicit::*; pub use optional::*; pub use parser::*; pub use private::*; pub(crate) const CONTEXT_SPECIFIC: u8 = Class::ContextSpecific as u8; /// A type parameter for `IMPLICIT` tagged values. #[derive(Debug, PartialEq, Eq)] pub enum Implicit {} /// A type parameter for `EXPLICIT` tagged values. #[derive(Debug, PartialEq, Eq)] pub enum Explicit {} /// A type parameter for tagged values either [`Explicit`] or [`Implicit`]. pub trait TagKind {} impl TagKind for Implicit {} impl TagKind for Explicit {} /// Helper object for creating `FromBer`/`FromDer` types for TAGGED OPTIONAL types /// /// When parsing `ContextSpecific` (the most common class), see [`TaggedExplicit`] and /// [`TaggedImplicit`] alias types. /// /// # Notes /// /// `CLASS` must be between 0 and 4. See [`Class`] for possible values for the `CLASS` parameter. /// Constants from this class can be used, but they must be wrapped in braces due to /// [Rust syntax for generics](https://doc.rust-lang.org/reference/items/generics.html) /// (see example below). /// /// # Examples /// /// To parse a `[APPLICATION 0] EXPLICIT INTEGER` object: /// /// ```rust /// use asn1_rs::{Class, Error, Explicit, FromBer, Integer, TaggedValue}; /// /// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2]; /// /// // If tagged object is present (and has expected tag), parsing succeeds: /// let (_, tagged) = /// TaggedValue::::from_ber(bytes) /// .unwrap(); /// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); /// ``` #[derive(Debug, PartialEq, Eq)] pub struct TaggedValue { pub(crate) inner: T, tag_kind: PhantomData, _e: PhantomData, } impl TaggedValue { /// Consumes the `TaggedParser`, returning the wrapped value. #[inline] pub fn into_inner(self) -> T { self.inner } /// Return the (outer) tag of this object pub const fn tag(&self) -> Tag { Self::TAG } /// Return the (outer) class of this object #[inline] pub const fn class(&self) -> u8 { CLASS } } impl TaggedValue { /// Constructs a new `EXPLICIT TaggedParser` with the provided value #[inline] pub const fn explicit(inner: T) -> Self { TaggedValue { inner, tag_kind: PhantomData, _e: PhantomData, } } } impl TaggedValue { /// Constructs a new `IMPLICIT TaggedParser` with the provided value #[inline] pub const fn implicit(inner: T) -> Self { TaggedValue { inner, tag_kind: PhantomData, _e: PhantomData, } } } impl AsRef for TaggedValue { fn as_ref(&self) -> &T { &self.inner } } impl Tagged for TaggedValue { const TAG: Tag = Tag(TAG); } rusticata-asn1-rs-07e764c/src/asn1_types/tagged/000077500000000000000000000000001476576401200214605ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/asn1_types/tagged/application.rs000066400000000000000000000030141476576401200243270ustar00rootroot00000000000000use crate::{Class, Explicit, Implicit, TaggedValue}; /// A helper object to parse `[APPLICATION n] EXPLICIT T` /// /// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to /// parse explicit application-tagged values. /// /// # Examples /// /// To parse a `[APPLICATION 0] EXPLICIT INTEGER` object: /// /// ```rust /// use asn1_rs::{ApplicationExplicit, Error, FromBer, Integer, TaggedValue}; /// /// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2]; /// /// // If tagged object is present (and has expected tag), parsing succeeds: /// let (_, tagged) = ApplicationExplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); /// ``` pub type ApplicationExplicit = TaggedValue; /// A helper object to parse `[APPLICATION n] IMPLICIT T` /// /// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to /// parse explicit application-tagged values. /// /// # Examples /// /// To parse a `[APPLICATION 0] IMPLICIT INTEGER` object: /// /// ```rust /// use asn1_rs::{ApplicationImplicit, Error, FromBer, Integer, TaggedValue}; /// /// let bytes = &[0x60, 0x1, 0x2]; /// /// let (_, tagged) = ApplicationImplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8))); /// ``` pub type ApplicationImplicit = TaggedValue; rusticata-asn1-rs-07e764c/src/asn1_types/tagged/builder.rs000066400000000000000000000063051476576401200234600ustar00rootroot00000000000000use super::{Error, Explicit, Implicit, TaggedParser}; use crate::{Class, FromBer, FromDer, ParseResult, Tag}; use core::marker::PhantomData; /// A builder for parsing tagged values (`IMPLICIT` or `EXPLICIT`) /// /// # Examples /// /// ``` /// use asn1_rs::{Class, Tag, TaggedParserBuilder}; /// /// let parser = TaggedParserBuilder::explicit() /// .with_class(Class::ContextSpecific) /// .with_tag(Tag(0)) /// .der_parser::(); /// /// let input = &[0xa0, 0x03, 0x02, 0x01, 0x02]; /// let (rem, tagged) = parser(input).expect("parsing failed"); /// /// assert!(rem.is_empty()); /// assert_eq!(tagged.tag(), Tag(0)); /// assert_eq!(tagged.as_ref(), &2); /// ``` #[derive(Clone, Copy, Debug)] pub struct TaggedParserBuilder { class: Class, tag: Tag, tag_kind: PhantomData, _e: PhantomData, } impl Default for TaggedParserBuilder { fn default() -> Self { Self::new() } } impl TaggedParserBuilder { /// Create a default `TaggedParserBuilder` builder /// /// `TagKind` must be specified as either [`Explicit`] or [`Implicit`] /// /// ``` /// use asn1_rs::{Explicit, TaggedParserBuilder}; /// /// let builder = TaggedParserBuilder::::new(); /// ``` pub const fn new() -> Self { TaggedParserBuilder { class: Class::Universal, tag: Tag(0), tag_kind: PhantomData, _e: PhantomData, } } /// Set the expected `Class` for the builder pub const fn with_class(self, class: Class) -> Self { Self { class, ..self } } /// Set the expected `Tag` for the builder pub const fn with_tag(self, tag: Tag) -> Self { Self { tag, ..self } } } impl TaggedParserBuilder { /// Create a `TagParser` builder for `EXPLICIT` tagged values pub const fn explicit() -> Self { TaggedParserBuilder::new() } } impl TaggedParserBuilder { /// Create a `TagParser` builder for `IMPLICIT` tagged values pub const fn implicit() -> Self { TaggedParserBuilder::new() } } impl TaggedParserBuilder { /// Create the BER parser from the builder parameters /// /// This method will consume the builder and return a parser (to be used as a function). pub fn ber_parser<'a, T>( self, ) -> impl Fn(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, TagKind, T, E>, E> where TaggedParser<'a, TagKind, T, E>: FromBer<'a, E>, E: From, { move |bytes: &[u8]| TaggedParser::::parse_ber(self.class, self.tag, bytes) } } impl TaggedParserBuilder { /// Create the DER parser from the builder parameters /// /// This method will consume the builder and return a parser (to be used as a function). pub fn der_parser<'a, T>( self, ) -> impl Fn(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, TagKind, T, E>, E> where TaggedParser<'a, TagKind, T, E>: FromDer<'a, E>, E: From, { move |bytes: &[u8]| TaggedParser::::parse_der(self.class, self.tag, bytes) } } rusticata-asn1-rs-07e764c/src/asn1_types/tagged/explicit.rs000066400000000000000000000176571476576401200236670ustar00rootroot00000000000000use crate::*; use core::convert::TryFrom; use core::marker::PhantomData; impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom> for TaggedValue where T: FromBer<'a, E>, E: From, { type Error = E; fn try_from(any: Any<'a>) -> Result { Self::try_from(&any) } } impl<'a, 'b, T, E, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>> for TaggedValue where T: FromBer<'a, E>, E: From, { type Error = E; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Tag(TAG))?; any.header.assert_constructed()?; if any.class() as u8 != CLASS { let class = Class::try_from(CLASS).ok(); return Err(Error::unexpected_class(class, any.class()).into()); } let (_, inner) = match T::from_ber(any.data) { Ok((rem, res)) => (rem, res), Err(Err::Error(e)) | Err(Err::Failure(e)) => return Err(e), Err(Err::Incomplete(n)) => return Err(Error::Incomplete(n).into()), }; Ok(TaggedValue::explicit(inner)) } } impl<'a, T, E, const CLASS: u8, const TAG: u32> FromDer<'a, E> for TaggedValue where T: FromDer<'a, E>, E: From, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; any.tag() .assert_eq(Tag(TAG)) .map_err(|e| Err::Error(e.into()))?; any.header .assert_constructed() .map_err(|e| Err::Error(e.into()))?; if any.class() as u8 != CLASS { let class = Class::try_from(CLASS).ok(); return Err(Err::Error( Error::unexpected_class(class, any.class()).into(), )); } let (_, inner) = T::from_der(any.data)?; Ok((rem, TaggedValue::explicit(inner))) } } impl CheckDerConstraints for TaggedValue where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.header.length.assert_definite()?; let (_, inner) = Any::from_ber(any.data)?; T::check_constraints(&inner)?; Ok(()) } } #[cfg(feature = "std")] impl ToDer for TaggedValue where T: ToDer, { fn to_der_len(&self) -> Result { let sz = self.inner.to_der_len()?; if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let inner_len = self.inner.to_der_len()?; let class = Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?; let header = Header::new(class, true, self.tag(), Length::Definite(inner_len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.inner.write_der(writer) } } /// A helper object to parse `[ n ] EXPLICIT T` /// /// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged /// optional values. /// /// This helper expects context-specific tags. /// See [`TaggedValue`] or [`TaggedParser`] for more generic implementations if needed. /// /// # Examples /// /// To parse a `[0] EXPLICIT INTEGER` object: /// /// ```rust /// use asn1_rs::{Error, FromBer, Integer, TaggedExplicit, TaggedValue}; /// /// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; /// /// // If tagged object is present (and has expected tag), parsing succeeds: /// let (_, tagged) = TaggedExplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); /// ``` pub type TaggedExplicit = TaggedValue; // implementations for TaggedParser impl<'a, T, E> TaggedParser<'a, Explicit, T, E> { pub const fn new_explicit(class: Class, tag: u32, inner: T) -> Self { Self { header: Header::new(class, true, Tag(tag), Length::Definite(0)), inner, tag_kind: PhantomData, _e: PhantomData, } } /// Parse a BER tagged value and apply the provided parsing function to content /// /// After parsing, the sequence object and header are discarded. /// /// Note: this function is provided for `Explicit`, but there is not difference between /// explicit or implicit tags. The `op` function is responsible of handling the content. #[inline] pub fn from_ber_and_then( class: Class, tag: u32, bytes: &'a [u8], op: F, ) -> ParseResult<'a, T, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>, E: From, { Any::from_ber_and_then(class, tag, bytes, op) } /// Parse a DER tagged value and apply the provided parsing function to content /// /// After parsing, the sequence object and header are discarded. /// /// Note: this function is provided for `Explicit`, but there is not difference between /// explicit or implicit tags. The `op` function is responsible of handling the content. #[inline] pub fn from_der_and_then( class: Class, tag: u32, bytes: &'a [u8], op: F, ) -> ParseResult<'a, T, E> where F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>, E: From, { Any::from_der_and_then(class, tag, bytes, op) } } impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Explicit, T, E> where T: FromBer<'a, E>, E: From, { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; let header = any.header; let (_, inner) = T::from_ber(any.data)?; let tagged = TaggedParser { header, inner, tag_kind: PhantomData, _e: PhantomData, }; Ok((rem, tagged)) } } impl<'a, T, E> FromDer<'a, E> for TaggedParser<'a, Explicit, T, E> where T: FromDer<'a, E>, E: From, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; let header = any.header; let (_, inner) = T::from_der(any.data)?; let tagged = TaggedParser { header, inner, tag_kind: PhantomData, _e: PhantomData, }; Ok((rem, tagged)) } } impl CheckDerConstraints for TaggedParser<'_, Explicit, T> where T: CheckDerConstraints, { fn check_constraints(any: &Any) -> Result<()> { any.header.length.assert_definite()?; let (_, inner_any) = Any::from_der(any.data)?; T::check_constraints(&inner_any)?; Ok(()) } } #[cfg(feature = "std")] impl ToDer for TaggedParser<'_, Explicit, T> where T: ToDer, { fn to_der_len(&self) -> Result { let sz = self.inner.to_der_len()?; if sz < 127 { // 1 (class+tag) + 1 (length) + len Ok(2 + sz) } else { // 1 (class+tag) + n (length) + len let n = Length::Definite(sz).to_der_len()?; Ok(1 + n + sz) } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let inner_len = self.inner.to_der_len()?; let header = Header::new(self.class(), true, self.tag(), Length::Definite(inner_len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.inner.write_der(writer) } } rusticata-asn1-rs-07e764c/src/asn1_types/tagged/helpers.rs000066400000000000000000000055051476576401200234750ustar00rootroot00000000000000use super::{Explicit, Implicit, TaggedParser}; use crate::{Any, Error, FromDer, Header, ParseResult, Tag, Tagged}; use nom::error::ParseError; use nom::{Err, IResult}; // helper functions for parsing tagged objects pub fn parse_der_tagged_explicit<'a, IntoTag, T, E>( tag: IntoTag, ) -> impl FnMut(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, Explicit, T, E>, E> where IntoTag: Into, TaggedParser<'a, Explicit, T, E>: FromDer<'a, E>, E: From, { let tag = tag.into(); move |i| { let (rem, tagged) = TaggedParser::from_der(i)?; tagged.assert_tag(tag).map_err(|e| Err::Error(e.into()))?; Ok((rem, tagged)) } } pub fn parse_der_tagged_explicit_g<'a, IntoTag, T, F, E>( tag: IntoTag, f: F, ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E> where F: Fn(&'a [u8], Header<'a>) -> IResult<&'a [u8], T, E>, E: ParseError<&'a [u8]> + From, IntoTag: Into, { let tag = tag.into(); parse_der_container(tag, move |any: Any<'a>| { any.header .assert_tag(tag) .map_err(|e| Err::convert(e.into()))?; f(any.data, any.header) }) } pub fn parse_der_tagged_implicit<'a, IntoTag, T, E>( tag: IntoTag, ) -> impl FnMut(&'a [u8]) -> ParseResult<'a, TaggedParser<'a, Implicit, T, E>, E> where IntoTag: Into, // T: TryFrom, Error = Error> + Tagged, TaggedParser<'a, Implicit, T, E>: FromDer<'a, E>, E: From, { let tag = tag.into(); move |i| { let (rem, tagged) = TaggedParser::from_der(i)?; tagged.assert_tag(tag).map_err(|e| Err::convert(e.into()))?; Ok((rem, tagged)) } } pub fn parse_der_tagged_implicit_g<'a, IntoTag, T, F, E>( tag: IntoTag, f: F, ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E> where F: Fn(&'a [u8], Tag, Header<'a>) -> IResult<&'a [u8], T, E>, E: ParseError<&'a [u8]> + From, IntoTag: Into, T: Tagged, { let tag = tag.into(); parse_der_container(tag, move |any: Any<'a>| { // verify tag of external header any.header .assert_tag(tag) .map_err(|e| Err::convert(e.into()))?; // build a fake header with the expected tag let Any { header, data } = any; let header = Header { tag: T::TAG, ..header.clone() }; f(data, tag, header) }) } fn parse_der_container<'a, T, F, E>( tag: Tag, f: F, ) -> impl FnMut(&'a [u8]) -> IResult<&'a [u8], T, E> where F: Fn(Any<'a>) -> IResult<&'a [u8], T, E>, E: ParseError<&'a [u8]> + From, { move |i: &[u8]| { let (rem, any) = Any::from_der(i).map_err(Err::convert)?; any.header .assert_tag(tag) .map_err(|e| Err::convert(e.into()))?; let (_, output) = f(any)?; Ok((rem, output)) } } rusticata-asn1-rs-07e764c/src/asn1_types/tagged/implicit.rs000066400000000000000000000222441476576401200236440ustar00rootroot00000000000000use crate::*; use core::convert::TryFrom; use core::marker::PhantomData; impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom> for TaggedValue where T: TryFrom, Error = E>, T: Tagged, E: From, { type Error = E; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b, E, T, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>> for TaggedValue where T: TryFrom, Error = E>, T: Tagged, E: From, { type Error = E; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Tag(TAG))?; // XXX if input is empty, this function is not called if any.class() as u8 != CLASS { let class = Class::try_from(CLASS).ok(); return Err(Error::unexpected_class(class, any.class()).into()); } let any = Any { header: Header { tag: T::TAG, ..any.header.clone() }, data: any.data, }; match T::try_from(any) { Ok(inner) => Ok(TaggedValue::implicit(inner)), Err(e) => Err(e), } } } impl<'a, T, E, const CLASS: u8, const TAG: u32> FromDer<'a, E> for TaggedValue where T: TryFrom, Error = E>, T: Tagged, E: From, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; any.tag() .assert_eq(Tag(TAG)) .map_err(|e| Err::Error(e.into()))?; if any.class() as u8 != CLASS { let class = Class::try_from(CLASS).ok(); return Err(Err::Error( Error::unexpected_class(class, any.class()).into(), )); } let any = Any { header: Header { tag: T::TAG, ..any.header.clone() }, data: any.data, }; match T::try_from(any) { Ok(inner) => Ok((rem, TaggedValue::implicit(inner))), Err(e) => Err(Err::Error(e)), } } } impl CheckDerConstraints for TaggedValue where T: CheckDerConstraints, T: Tagged, { fn check_constraints(any: &Any) -> Result<()> { any.header.length.assert_definite()?; let header = any.header.clone().with_tag(T::TAG); let inner = Any::new(header, any.data); T::check_constraints(&inner)?; Ok(()) } } #[cfg(feature = "std")] impl ToDer for TaggedValue where T: ToDer, { fn to_der_len(&self) -> Result { self.inner.to_der_len() } fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let class = Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?; let mut v = Vec::new(); let inner_len = self.inner.write_der_content(&mut v)?; // XXX X.690 section 8.14.3: if implicing tagging was used [...]: // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise let constructed = matches!(self.inner.tag(), Tag::Sequence | Tag::Set); let header = Header::new(class, constructed, self.tag(), Length::Definite(inner_len)); let sz = header.write_der_header(writer)?; let sz = sz + writer.write(&v)?; Ok(sz) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut sink = std::io::sink(); let class = Class::try_from(CLASS).map_err(|_| SerializeError::InvalidClass { class: CLASS })?; let inner_len = self.inner.write_der_content(&mut sink)?; // XXX X.690 section 8.14.3: if implicing tagging was used [...]: // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise let constructed = matches!(self.inner.tag(), Tag::Sequence | Tag::Set); let header = Header::new(class, constructed, self.tag(), Length::Definite(inner_len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.inner.write_der_content(writer) } } /// A helper object to parse `[ n ] IMPLICIT T` /// /// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged /// optional values. /// /// This helper expects context-specific tags. /// See [`TaggedValue`] or [`TaggedParser`] for more generic implementations if needed. /// /// # Examples /// /// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{Error, FromBer, Integer, TaggedImplicit, TaggedValue}; /// /// let bytes = &[0xa0, 0x1, 0x2]; /// /// let (_, tagged) = TaggedImplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2))); /// ``` pub type TaggedImplicit = TaggedValue; impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Implicit, T, E> where T: TryFrom, Error = E>, T: Tagged, E: From, { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; let Any { header, data } = any; let any = Any { header: Header { tag: T::TAG, ..header.clone() }, data, }; match T::try_from(any) { Ok(t) => { let tagged_value = TaggedParser { header, inner: t, tag_kind: PhantomData, _e: PhantomData, }; Ok((rem, tagged_value)) } Err(e) => Err(Err::Error(e)), } } } // implementations for TaggedParser impl TaggedParser<'_, Implicit, T, E> { pub const fn new_implicit(class: Class, constructed: bool, tag: u32, inner: T) -> Self { Self { header: Header::new(class, constructed, Tag(tag), Length::Definite(0)), inner, tag_kind: PhantomData, _e: PhantomData, } } } impl<'a, T, E> FromDer<'a, E> for TaggedParser<'a, Implicit, T, E> where T: TryFrom, Error = E>, T: CheckDerConstraints, T: Tagged, E: From, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; let Any { header, data } = any; let any = Any { header: Header { tag: T::TAG, ..header.clone() }, data, }; T::check_constraints(&any).map_err(|e| Err::Error(e.into()))?; match T::try_from(any) { Ok(t) => { let tagged_value = TaggedParser { header, inner: t, tag_kind: PhantomData, _e: PhantomData, }; Ok((rem, tagged_value)) } Err(e) => Err(Err::Error(e)), } } } impl CheckDerConstraints for TaggedParser<'_, Implicit, T> where T: CheckDerConstraints, T: Tagged, { fn check_constraints(any: &Any) -> Result<()> { any.header.length.assert_definite()?; let any = Any { header: Header { tag: T::TAG, ..any.header.clone() }, data: any.data, }; T::check_constraints(&any)?; Ok(()) } } #[cfg(feature = "std")] impl ToDer for TaggedParser<'_, Implicit, T> where T: ToDer, { fn to_der_len(&self) -> Result { self.inner.to_der_len() } fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut v = Vec::new(); let inner_len = self.inner.write_der_content(&mut v)?; // XXX X.690 section 8.14.3: if implicing tagging was used [...]: // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise let header = Header::new(self.class(), false, self.tag(), Length::Definite(inner_len)); let sz = header.write_der_header(writer)?; let sz = sz + writer.write(&v)?; Ok(sz) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let mut sink = std::io::sink(); let inner_len = self.inner.write_der_content(&mut sink)?; // XXX X.690 section 8.14.3: if implicing tagging was used [...]: // XXX a) the encoding shall be constructed if the base encoding is constructed, and shall be primitive otherwise let header = Header::new(self.class(), false, self.tag(), Length::Definite(inner_len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { self.inner.write_der_content(writer) } } rusticata-asn1-rs-07e764c/src/asn1_types/tagged/optional.rs000066400000000000000000000200411476576401200236500ustar00rootroot00000000000000use crate::*; /// Helper object to parse TAGGED OPTIONAL types (explicit or implicit) /// /// This object can be used similarly to a builder pattern, to specify the expected class and /// tag of the object to parse, and the content parsing function. /// /// The content parsing function takes two arguments: the outer header, and the data. /// /// It can be used for both EXPLICIT or IMPLICIT tagged objects by using parsing functions that /// expect a header (or not) in the contents. /// /// The [`OptTaggedParser::from`] method is a shortcut to build an object with `ContextSpecific` /// class and the given tag. The [`OptTaggedParser::new`] method is more generic. /// /// See also [`OptTaggedExplicit`] and [`OptTaggedImplicit`] for alternatives that implement [`FromBer`]/ /// [`FromDer`]. /// /// # Examples /// /// To parse a `[APPLICATION 0] EXPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{Class, FromDer, Integer, Tag, OptTaggedParser}; /// /// let bytes = &[0x60, 0x03, 0x2, 0x1, 0x2]; /// /// let (_, tagged) = OptTaggedParser::new(Class::Application, Tag(0)) /// .parse_der(bytes, |_, data| Integer::from_der(data)) /// .unwrap(); /// /// assert_eq!(tagged, Some(Integer::from(2))); /// ``` /// /// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{Error, Integer, OptTaggedParser}; /// /// let bytes = &[0xa0, 0x1, 0x2]; /// /// let (_, tagged) = OptTaggedParser::from(0) /// .parse_der::<_, Error, _>(bytes, |_, data| Ok((&[], Integer::new(data)))) /// .unwrap(); /// /// assert_eq!(tagged, Some(Integer::from(2))); /// ``` #[derive(Debug)] pub struct OptTaggedParser { /// The expected class for the object to parse pub class: Class, /// The expected tag for the object to parse pub tag: Tag, } impl OptTaggedParser { /// Build a new `OptTaggedParser` object. /// /// If using `Class::ContextSpecific`, using [`OptTaggedParser::from`] with either a `Tag` or `u32` is /// a shorter way to build this object. pub const fn new(class: Class, tag: Tag) -> Self { OptTaggedParser { class, tag } } pub const fn universal(tag: u32) -> Self { Self::new(Class::Universal, Tag(tag)) } pub const fn tagged(tag: u32) -> Self { Self::new(Class::ContextSpecific, Tag(tag)) } pub const fn application(tag: u32) -> Self { Self::new(Class::Application, Tag(tag)) } pub const fn private(tag: u32) -> Self { Self::new(Class::Private, Tag(tag)) } /// Parse input as BER, and apply the provided function to parse object. /// /// Returns the remaining bytes, and `Some(T)` if expected tag was found, else `None`. /// /// This function returns an error if tag was found but has a different class, or if parsing fails. /// /// # Examples /// /// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{FromBer, Integer, OptTaggedParser}; /// /// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; /// /// let (_, tagged) = OptTaggedParser::from(0) /// .parse_ber(bytes, |_, data| Integer::from_ber(data)) /// .unwrap(); /// /// assert_eq!(tagged, Some(Integer::from(2))); /// ``` pub fn parse_ber<'a, T, E, F>(&self, bytes: &'a [u8], f: F) -> ParseResult<'a, Option, E> where F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>, E: From, { if bytes.is_empty() { return Ok((bytes, None)); } let (rem, any) = Any::from_ber(bytes).map_err(Err::convert)?; if any.tag() != self.tag { return Ok((bytes, None)); } if any.class() != self.class { return Err(Err::Error( Error::unexpected_class(Some(self.class), any.class()).into(), )); } let Any { header, data } = any; let (_, res) = f(header, data)?; Ok((rem, Some(res))) } /// Parse input as DER, and apply the provided function to parse object. /// /// Returns the remaining bytes, and `Some(T)` if expected tag was found, else `None`. /// /// This function returns an error if tag was found but has a different class, or if parsing fails. /// /// # Examples /// /// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{FromDer, Integer, OptTaggedParser}; /// /// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; /// /// let (_, tagged) = OptTaggedParser::from(0) /// .parse_der(bytes, |_, data| Integer::from_der(data)) /// .unwrap(); /// /// assert_eq!(tagged, Some(Integer::from(2))); /// ``` pub fn parse_der<'a, T, E, F>(&self, bytes: &'a [u8], f: F) -> ParseResult<'a, Option, E> where F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>, E: From, { if bytes.is_empty() { return Ok((bytes, None)); } let (rem, any) = Any::from_der(bytes).map_err(Err::convert)?; if any.tag() != self.tag { return Ok((bytes, None)); } if any.class() != self.class { return Err(Err::Error( Error::unexpected_class(Some(self.class), any.class()).into(), )); } let Any { header, data } = any; let (_, res) = f(header, data)?; Ok((rem, Some(res))) } } impl From for OptTaggedParser { /// Build a `TaggedOptional` object with class `ContextSpecific` and given tag #[inline] fn from(tag: Tag) -> Self { OptTaggedParser::new(Class::ContextSpecific, tag) } } impl From for OptTaggedParser { /// Build a `TaggedOptional` object with class `ContextSpecific` and given tag #[inline] fn from(tag: u32) -> Self { OptTaggedParser::new(Class::ContextSpecific, Tag(tag)) } } /// A helper object to parse `[ n ] EXPLICIT T OPTIONAL` /// /// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged /// optional values. /// /// This helper expects context-specific tags. /// Use `Option<` [`TaggedValue`] `>` for a more generic implementation. /// /// # Examples /// /// To parse a `[0] EXPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{Error, FromBer, Integer, OptTaggedExplicit, TaggedValue}; /// /// let bytes = &[0xa0, 0x03, 0x2, 0x1, 0x2]; /// /// // If tagged object is present (and has expected tag), parsing succeeds: /// let (_, tagged) = OptTaggedExplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, Some(TaggedValue::explicit(Integer::from(2)))); /// /// // If tagged object is not present or has different tag, parsing /// // also succeeds (returning None): /// let (_, tagged) = OptTaggedExplicit::::from_ber(&[]).unwrap(); /// assert_eq!(tagged, None); /// let (_, tagged) = OptTaggedExplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, None); /// ``` pub type OptTaggedExplicit = Option>; /// A helper object to parse `[ n ] IMPLICIT T OPTIONAL` /// /// A helper object implementing [`FromBer`] and [`FromDer`], to parse tagged /// optional values. /// /// This helper expects context-specific tags. /// Use `Option<` [`TaggedValue`] `>` for a more generic implementation. /// /// # Examples /// /// To parse a `[0] IMPLICIT INTEGER OPTIONAL` object: /// /// ```rust /// use asn1_rs::{Error, FromBer, Integer, OptTaggedImplicit, TaggedValue}; /// /// let bytes = &[0xa0, 0x1, 0x2]; /// /// let (_, tagged) = OptTaggedImplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, Some(TaggedValue::implicit(Integer::from(2)))); /// /// // If tagged object is not present or has different tag, parsing /// // also succeeds (returning None): /// let (_, tagged) = OptTaggedImplicit::::from_ber(&[]).unwrap(); /// assert_eq!(tagged, None); /// ``` pub type OptTaggedImplicit = Option>; rusticata-asn1-rs-07e764c/src/asn1_types/tagged/parser.rs000066400000000000000000000037521476576401200233310ustar00rootroot00000000000000use crate::*; use core::marker::PhantomData; #[derive(Debug, PartialEq, Eq)] pub struct TaggedParser<'a, TagKind, T, E = Error> { pub header: Header<'a>, pub inner: T, pub(crate) tag_kind: PhantomData, pub(crate) _e: PhantomData, } impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> { pub const fn new(header: Header<'a>, inner: T) -> Self { TaggedParser { header, inner, tag_kind: PhantomData, _e: PhantomData, } } pub const fn assert_class(&self, class: Class) -> Result<()> { self.header.assert_class(class) } pub const fn assert_tag(&self, tag: Tag) -> Result<()> { self.header.assert_tag(tag) } #[inline] pub const fn class(&self) -> Class { self.header.class } #[inline] pub const fn tag(&self) -> Tag { self.header.tag } } impl AsRef for TaggedParser<'_, TagKind, T, E> { fn as_ref(&self) -> &T { &self.inner } } impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> where Self: FromBer<'a, E>, E: From, { pub fn parse_ber(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, t) = TaggedParser::::from_ber(bytes)?; t.assert_class(class).map_err(|e| Err::Error(e.into()))?; t.assert_tag(tag).map_err(|e| Err::Error(e.into()))?; Ok((rem, t)) } } impl<'a, TagKind, T, E> TaggedParser<'a, TagKind, T, E> where Self: FromDer<'a, E>, E: From, { pub fn parse_der(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> { let (rem, t) = TaggedParser::::from_der(bytes)?; t.assert_class(class).map_err(|e| Err::Error(e.into()))?; t.assert_tag(tag).map_err(|e| Err::Error(e.into()))?; Ok((rem, t)) } } impl DynTagged for TaggedParser<'_, TagKind, T, E> { fn tag(&self) -> Tag { self.tag() } } rusticata-asn1-rs-07e764c/src/asn1_types/tagged/private.rs000066400000000000000000000027241476576401200235050ustar00rootroot00000000000000use crate::{Class, Explicit, Implicit, TaggedValue}; /// A helper object to parse `[PRIVATE n] EXPLICIT T` /// /// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to /// parse explicit private-tagged values. /// /// # Examples /// /// To parse a `[PRIVATE 0] EXPLICIT INTEGER` object: /// /// ```rust /// use asn1_rs::{Error, FromBer, Integer, PrivateExplicit, TaggedValue}; /// /// let bytes = &[0xe0, 0x03, 0x2, 0x1, 0x2]; /// /// // If tagged object is present (and has expected tag), parsing succeeds: /// let (_, tagged) = PrivateExplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2))); /// ``` pub type PrivateExplicit = TaggedValue; /// A helper object to parse `[PRIVATE n] IMPLICIT T` /// /// A helper object implementing [`FromBer`](crate::FromBer) and [`FromDer`](crate::FromDer), to /// parse implicit private-tagged values. /// /// # Examples /// /// To parse a `[PRIVATE 0] IMPLICIT INTEGER` object: /// /// ```rust /// use asn1_rs::{Error, FromBer, Integer, PrivateImplicit, TaggedValue}; /// /// let bytes = &[0xe0, 0x1, 0x2]; /// /// let (_, tagged) = PrivateImplicit::::from_ber(bytes).unwrap(); /// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8))); /// ``` pub type PrivateImplicit = TaggedValue; rusticata-asn1-rs-07e764c/src/asn1_types/utctime.rs000066400000000000000000000201171476576401200222460ustar00rootroot00000000000000use crate::*; use core::convert::TryFrom; use core::fmt; #[cfg(feature = "datetime")] use time::OffsetDateTime; #[derive(Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct UtcTime(pub ASN1DateTime); impl UtcTime { pub const fn new(datetime: ASN1DateTime) -> Self { UtcTime(datetime) } pub fn from_bytes(bytes: &[u8]) -> Result { // X.680 section 43 defines a UniversalTime as a VisibleString restricted to: // // a) the six digits YYMMDD where YY is the two low-order digits of the Christian year, MM is the month // (counting January as 01), and DD is the day of the month (01 to 31); and // b) either: // 1) the four digits hhmm where hh is hour (00 to 23) and mm is minutes (00 to 59); or // 2) the six digits hhmmss where hh and mm are as in 1) above, and ss is seconds (00 to 59); and // c) either: // 1) the character Z ; or // 2) one of the characters + or - , followed by hhmm, where hh is hour and mm is minutes. // // XXX // RFC 5280 requires mandatory seconds and Z-normalized time zone let (year, month, day, hour, minute, rem) = match bytes { [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, rem @ ..] => { let year = decode_decimal(Self::TAG, *year1, *year2)?; let month = decode_decimal(Self::TAG, *mon1, *mon2)?; let day = decode_decimal(Self::TAG, *day1, *day2)?; let hour = decode_decimal(Self::TAG, *hour1, *hour2)?; let minute = decode_decimal(Self::TAG, *min1, *min2)?; (year, month, day, hour, minute, rem) } _ => return Err(Self::TAG.invalid_value("malformed time string (not yymmddhhmm)")), }; if rem.is_empty() { return Err(Self::TAG.invalid_value("malformed time string")); } // check for seconds let (second, rem) = match rem { [sec1, sec2, rem @ ..] => { let second = decode_decimal(Self::TAG, *sec1, *sec2)?; (second, rem) } _ => (0, rem), }; if month > 12 || day > 31 || hour > 23 || minute > 59 || second > 59 { return Err(Self::TAG.invalid_value("time components with invalid values")); } if rem.is_empty() { return Err(Self::TAG.invalid_value("malformed time string")); } let tz = match rem { [b'Z'] => ASN1TimeZone::Z, [b'+', h1, h2, m1, m2] => { let hh = decode_decimal(Self::TAG, *h1, *h2)?; let mm = decode_decimal(Self::TAG, *m1, *m2)?; ASN1TimeZone::Offset(hh as i8, mm as i8) } [b'-', h1, h2, m1, m2] => { let hh = decode_decimal(Self::TAG, *h1, *h2)?; let mm = decode_decimal(Self::TAG, *m1, *m2)?; ASN1TimeZone::Offset(-(hh as i8), mm as i8) } _ => return Err(Self::TAG.invalid_value("malformed time string: no time zone")), }; Ok(UtcTime(ASN1DateTime::new( year as u32, month, day, hour, minute, second, None, tz, ))) // match *bytes { // [year1, year2, mon1, mon2, day1, day2, hour1, hour2, min1, min2, sec1, sec2, b'Z'] => { // let year = decode_decimal(Self::TAG, year1, year2)?; // let month = decode_decimal(Self::TAG, mon1, mon2)?; // let day = decode_decimal(Self::TAG, day1, day2)?; // let hour = decode_decimal(Self::TAG, hour1, hour2)?; // let minute = decode_decimal(Self::TAG, min1, min2)?; // let second = decode_decimal(Self::TAG, sec1, sec2)?; // // RFC 5280 rules for interpreting the year // let year = if year >= 50 { year + 1900 } else { year + 2000 }; // Ok(UtcTime::new(year, month, day, hour, minute, second)) // } // _ => Err(Error::InvalidValue), // } } /// Return a ISO 8601 combined date and time with time zone. #[cfg(feature = "datetime")] #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] #[inline] pub fn utc_datetime(&self) -> Result { self.0.to_datetime() } /// Return an adjusted ISO 8601 combined date and time with time zone. /// According to Universal time definition in X.680 we add 2000 years /// from 0 to 49 year and 1900 otherwise. #[cfg(feature = "datetime")] #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] #[inline] pub fn utc_adjusted_datetime(&self) -> Result { self.0.to_datetime().and_then(|dt| { let year = dt.year(); // We follow the Universal time definition in X.680 for interpreting // the adjusted year let year = if year >= 50 { year + 1900 } else { year + 2000 }; time::Date::from_calendar_date(year, dt.month(), dt.day()) .map(|d| dt.replace_date(d)) .map_err(|_e| Self::TAG.invalid_value("Invalid adjusted date")) }) } /// Returns the number of non-leap seconds since the midnight on January 1, 1970. #[cfg(feature = "datetime")] #[cfg_attr(docsrs, doc(cfg(feature = "datetime")))] pub fn timestamp(&self) -> Result { let dt = self.0.to_datetime()?; Ok(dt.unix_timestamp()) } } impl<'a> TryFrom> for UtcTime { type Error = Error; fn try_from(any: Any<'a>) -> Result { TryFrom::try_from(&any) } } impl<'a, 'b> TryFrom<&'b Any<'a>> for UtcTime { type Error = Error; fn try_from(any: &'b Any<'a>) -> Result { any.tag().assert_eq(Self::TAG)?; #[allow(clippy::trivially_copy_pass_by_ref)] fn is_visible(b: &u8) -> bool { 0x20 <= *b && *b <= 0x7f } if !any.data.iter().all(is_visible) { return Err(Error::StringInvalidCharset); } UtcTime::from_bytes(any.data) } } impl fmt::Display for UtcTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let dt = &self.0; match dt.tz { ASN1TimeZone::Z | ASN1TimeZone::Undefined => write!( f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}Z", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second ), ASN1TimeZone::Offset(hh, mm) => { let (s, hh) = if hh > 0 { ('+', hh) } else { ('-', -hh) }; write!( f, "{:04}-{:02}-{:02} {:02}:{:02}:{:02}{}{:02}{:02}", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, s, hh, mm ) } } } } impl CheckDerConstraints for UtcTime { fn check_constraints(_any: &Any) -> Result<()> { Ok(()) } } impl DerAutoDerive for UtcTime {} impl Tagged for UtcTime { const TAG: Tag = Tag::UtcTime; } #[cfg(feature = "std")] impl ToDer for UtcTime { fn to_der_len(&self) -> Result { // data: // - 6 bytes for YYMMDD // - 6 for hhmmss in DER (X.690 section 11.8.2) // - 1 for the character Z in DER (X.690 section 11.8.1) // data length: 13 // // thus, length will always be on 1 byte (short length) and // class+structure+tag also on 1 // // total: 15 = 1 (class+constructed+tag) + 1 (length) + 13 Ok(15) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // see above for length value writer.write(&[Self::TAG.0 as u8, 13]).map_err(Into::into) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { write!( writer, "{:02}{:02}{:02}{:02}{:02}{:02}Z", self.0.year, self.0.month, self.0.day, self.0.hour, self.0.minute, self.0.second, )?; // write_fmt returns (), see above for length value Ok(13) } } rusticata-asn1-rs-07e764c/src/ber/000077500000000000000000000000001476576401200167075ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/ber/mod.rs000066400000000000000000000000401476576401200200260ustar00rootroot00000000000000mod parser; pub use parser::*; rusticata-asn1-rs-07e764c/src/ber/parser.rs000066400000000000000000000117061476576401200205560ustar00rootroot00000000000000use crate::error::*; use crate::header::*; use crate::{BerParser, DerParser, FromBer, Length, Tag}; use nom::bytes::streaming::take; use nom::{Err, Needed, Offset}; use rusticata_macros::custom_check; /// Default maximum recursion limit pub const MAX_RECURSION: usize = 50; // /// Default maximum object size (2^32) // pub const MAX_OBJECT_SIZE: usize = 4_294_967_295; pub trait GetObjectContent { /// Return the raw content (bytes) of the next ASN.1 encoded object /// /// Note: if using BER and length is indefinite, terminating End-Of-Content is NOT included fn get_object_content<'a>( i: &'a [u8], hdr: &'_ Header, max_depth: usize, ) -> ParseResult<'a, &'a [u8]>; } impl GetObjectContent for BerParser { fn get_object_content<'a>( i: &'a [u8], hdr: &'_ Header, max_depth: usize, ) -> ParseResult<'a, &'a [u8]> { let start_i = i; let (i, _) = ber_skip_object_content(i, hdr, max_depth)?; let len = start_i.offset(i); let (content, i) = start_i.split_at(len); // if len is indefinite, there are 2 extra bytes for EOC if hdr.length == Length::Indefinite { let len = content.len(); assert!(len >= 2); Ok((i, &content[..len - 2])) } else { Ok((i, content)) } } } impl GetObjectContent for DerParser { /// Skip object content, accepting only DER /// /// This this function is for DER only, it cannot go into recursion (no indefinite length) fn get_object_content<'a>( i: &'a [u8], hdr: &'_ Header, _max_depth: usize, ) -> ParseResult<'a, &'a [u8]> { match hdr.length { Length::Definite(l) => take(l)(i), Length::Indefinite => Err(Err::Error(Error::DerConstraintFailed( DerConstraint::IndefiniteLength, ))), } } } /// Skip object content, and return true if object was End-Of-Content fn ber_skip_object_content<'a>( i: &'a [u8], hdr: &Header, max_depth: usize, ) -> ParseResult<'a, bool> { if max_depth == 0 { return Err(Err::Error(Error::BerMaxDepth)); } match hdr.length { Length::Definite(l) => { if l == 0 && hdr.tag == Tag::EndOfContent { return Ok((i, true)); } let (i, _) = take(l)(i)?; Ok((i, false)) } Length::Indefinite => { hdr.assert_constructed()?; // read objects until EndOfContent (00 00) // this is recursive let mut i = i; loop { let (i2, header2) = Header::from_ber(i)?; let (i3, eoc) = ber_skip_object_content(i2, &header2, max_depth - 1)?; if eoc { // return false, since top object was not EndOfContent return Ok((i3, false)); } i = i3; } } } } /// Try to parse input bytes as u64 #[inline] pub(crate) fn bytes_to_u64(s: &[u8]) -> core::result::Result { let mut u: u64 = 0; for &c in s { if u & 0xff00_0000_0000_0000 != 0 { return Err(Error::IntegerTooLarge); } u <<= 8; u |= u64::from(c); } Ok(u) } pub(crate) fn parse_identifier(i: &[u8]) -> ParseResult<(u8, u8, u32, &[u8])> { if i.is_empty() { Err(Err::Incomplete(Needed::new(1))) } else { let a = i[0] >> 6; let b = u8::from(i[0] & 0b0010_0000 != 0); let mut c = u32::from(i[0] & 0b0001_1111); let mut tag_byte_count = 1; if c == 0x1f { c = 0; loop { // Make sure we don't read past the end of our data. custom_check!(i, tag_byte_count >= i.len(), Error::InvalidTag)?; // With tag defined as u32 the most we can fit in is four tag bytes. // (X.690 doesn't actually specify maximum tag width.) custom_check!(i, tag_byte_count > 5, Error::InvalidTag)?; c = (c << 7) | (u32::from(i[tag_byte_count]) & 0x7f); let done = i[tag_byte_count] & 0x80 == 0; tag_byte_count += 1; if done { break; } } } let (raw_tag, rem) = i.split_at(tag_byte_count); Ok((rem, (a, b, c, raw_tag))) } } /// Return the MSB and the rest of the first byte, or an error pub(crate) fn parse_ber_length_byte(i: &[u8]) -> ParseResult<(u8, u8)> { if i.is_empty() { Err(Err::Incomplete(Needed::new(1))) } else { let a = i[0] >> 7; let b = i[0] & 0b0111_1111; Ok((&i[1..], (a, b))) } } #[doc(hidden)] #[macro_export] macro_rules! der_constraint_fail_if( ($slice:expr, $cond:expr, $constraint:expr) => ( { if $cond { return Err(::nom::Err::Error(Error::DerConstraintFailed($constraint))); } } ); ); rusticata-asn1-rs-07e764c/src/class.rs000066400000000000000000000053311476576401200176140ustar00rootroot00000000000000use core::convert::TryFrom; use core::fmt; #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub struct BerClassFromIntError(pub(crate) ()); /// BER Object class of tag #[derive(Debug, Copy, Clone, Eq, PartialEq)] #[repr(u8)] pub enum Class { /// `Universal` class of tags (`0b00`) Universal = 0b00, /// `Application` class of tags (`0b01`) Application = 0b01, /// `Context-Specific` class of tags (`0b10`) ContextSpecific = 0b10, /// `Private` class of tags (`0b11`) Private = 0b11, } impl Class { /// `Universal` class of tags (`0b00`) pub const UNIVERSAL: u8 = 0b00; /// `Application` class of tags (`0b01`) pub const APPLICATION: u8 = 0b01; /// `Context-Specific` class of tags (`0b10`) pub const CONTEXT_SPECIFIC: u8 = 0b10; /// `Private` class of tags (`0b11`) pub const PRIVATE: u8 = 0b11; pub const fn assert_eq(&self, class: Class) -> Result<(), crate::error::Error> { if *self as u8 == class as u8 { Ok(()) } else { Err(crate::error::Error::UnexpectedClass { expected: Some(class), actual: *self, }) } } } impl fmt::Display for Class { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let s = match self { Class::Universal => "UNIVERSAL", Class::Application => "APPLICATION", Class::ContextSpecific => "CONTEXT-SPECIFIC", Class::Private => "PRIVATE", }; write!(f, "{}", s) } } impl TryFrom for Class { type Error = BerClassFromIntError; #[inline] fn try_from(value: u8) -> Result { match value { 0b00 => Ok(Class::Universal), 0b01 => Ok(Class::Application), 0b10 => Ok(Class::ContextSpecific), 0b11 => Ok(Class::Private), _ => Err(BerClassFromIntError(())), } } } #[cfg(all(test, feature = "std"))] mod tests { use super::*; #[test] fn methods_class() { let c = Class::Universal; assert!(c.assert_eq(Class::Universal).is_ok()); assert!(c.assert_eq(Class::Private).is_err()); assert_eq!(Class::Universal.to_string().as_str(), "UNIVERSAL"); assert_eq!(Class::Application.to_string().as_str(), "APPLICATION"); assert_eq!( Class::ContextSpecific.to_string().as_str(), "CONTEXT-SPECIFIC" ); assert_eq!(Class::Private.to_string().as_str(), "PRIVATE"); assert!(Class::try_from(0b00).is_ok()); assert!(Class::try_from(0b01).is_ok()); assert!(Class::try_from(0b10).is_ok()); assert!(Class::try_from(0b11).is_ok()); assert!(Class::try_from(4).is_err()); } } rusticata-asn1-rs-07e764c/src/const_int.rs000066400000000000000000000020431476576401200205040ustar00rootroot00000000000000use crate::{Tag, Tagged}; #[derive(Debug)] pub struct ConstInt { buffer: [u8; 10], n: usize, } // XXX only ToBer/ToDer trait supported? impl Tagged for ConstInt { const TAG: Tag = Tag::Integer; } #[derive(Debug)] pub struct IntBuilder {} impl IntBuilder { pub const fn build(&self, i: u64) -> ConstInt { let b = i.to_be_bytes(); let mut out = [0u8; 10]; out[0] = 0x4; let src_len = b.len(); let mut src_index = 0; while src_index < src_len && b[src_index] == 0 { src_index += 1; } out[1] = (src_len - src_index) as u8; let mut dst_index = 2; while src_index < src_len { out[dst_index] = b[src_index]; src_index += 1; dst_index += 1; } // XXX will not work: we need to allocate a Vec // also, we cannot just store the bytes (there are extra zeroes at end) // Integer::new(&out[..dst_index]) ConstInt { buffer: out, n: dst_index, } } } rusticata-asn1-rs-07e764c/src/datetime.rs000066400000000000000000000056271476576401200203130ustar00rootroot00000000000000use crate::{Result, Tag}; use alloc::format; #[cfg(not(feature = "std"))] use alloc::string::ToString; use core::fmt; #[cfg(feature = "datetime")] use time::OffsetDateTime; #[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Ord)] pub enum ASN1TimeZone { /// No timezone provided Undefined, /// Coordinated universal time Z, /// Local zone, with offset to coordinated universal time /// /// `(offset_hour, offset_minute)` Offset(i8, i8), } #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)] pub struct ASN1DateTime { pub year: u32, pub month: u8, pub day: u8, pub hour: u8, pub minute: u8, pub second: u8, pub millisecond: Option, pub tz: ASN1TimeZone, } impl ASN1DateTime { #[allow(clippy::too_many_arguments)] pub const fn new( year: u32, month: u8, day: u8, hour: u8, minute: u8, second: u8, millisecond: Option, tz: ASN1TimeZone, ) -> Self { ASN1DateTime { year, month, day, hour, minute, second, millisecond, tz, } } #[cfg(feature = "datetime")] fn to_time_datetime( &self, ) -> core::result::Result { use std::convert::TryFrom; use time::{Date, Month, PrimitiveDateTime, Time, UtcOffset}; let month = Month::try_from(self.month)?; let date = Date::from_calendar_date(self.year as i32, month, self.day)?; let time = Time::from_hms_milli( self.hour, self.minute, self.second, self.millisecond.unwrap_or(0), )?; let primitive_date = PrimitiveDateTime::new(date, time); let offset = match self.tz { ASN1TimeZone::Offset(h, m) => UtcOffset::from_hms(h, m, 0)?, ASN1TimeZone::Undefined | ASN1TimeZone::Z => UtcOffset::UTC, }; Ok(primitive_date.assume_offset(offset)) } #[cfg(feature = "datetime")] pub fn to_datetime(&self) -> Result { use crate::Error; self.to_time_datetime().map_err(|_| Error::InvalidDateTime) } } impl fmt::Display for ASN1DateTime { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let fractional = match self.millisecond { None => "".to_string(), Some(v) => format!(".{}", v), }; write!( f, "{:04}{:02}{:02}{:02}{:02}{:02}{}Z", self.year, self.month, self.day, self.hour, self.minute, self.second, fractional, ) } } /// Decode 2-digit decimal value pub(crate) fn decode_decimal(tag: Tag, hi: u8, lo: u8) -> Result { if hi.is_ascii_digit() && lo.is_ascii_digit() { Ok((hi - b'0') * 10 + (lo - b'0')) } else { Err(tag.invalid_value("expected digit")) } } rusticata-asn1-rs-07e764c/src/debug.rs000066400000000000000000000167001476576401200175770ustar00rootroot00000000000000#![allow(unused_imports)] use crate::ParseResult; pub(crate) mod macros { macro_rules! debug_eprintln { ($msg: expr, $( $args:expr ),* ) => { #[cfg(feature = "debug")] { use colored::Colorize; let s = $msg.to_string().green(); eprintln!("{} {}", s, format!($($args),*)); } }; } #[allow(unused_macros)] macro_rules! trace_eprintln { ($msg: expr, $( $args:expr ),* ) => { #[cfg(feature = "trace")] { use colored::Colorize; let s = $msg.to_string().green(); eprintln!("{} {}", s, format!($($args),*)); } }; } pub(crate) use debug_eprintln; pub(crate) use trace_eprintln; } use macros::*; #[cfg(feature = "debug")] fn eprintln_hex_dump(bytes: &[u8], max_len: usize) { use core::cmp::min; use nom::HexDisplay; let m = min(bytes.len(), max_len); eprint!("{}", &bytes[..m].to_hex(16)); if bytes.len() > max_len { eprintln!("... "); } } #[cfg(not(feature = "debug"))] #[inline] pub fn trace_generic(_msg: &str, _fname: &str, f: F, input: I) -> Result where F: Fn(I) -> Result, { f(input) } #[cfg(feature = "debug")] pub fn trace_generic(msg: &str, fname: &str, f: F, input: I) -> Result where F: Fn(I) -> Result, E: core::fmt::Display, { trace_eprintln!(msg, "⤷ {}", fname); let output = f(input); match &output { Err(e) => { debug_eprintln!(msg, "↯ {} failed: {}", fname, e.to_string().red()); } _ => { debug_eprintln!(msg, "⤶ {}", fname); } } output } #[cfg(not(feature = "debug"))] #[inline] pub fn trace<'a, T, E, F>(_msg: &str, f: F, input: &'a [u8]) -> ParseResult<'a, T, E> where F: Fn(&'a [u8]) -> ParseResult<'a, T, E>, { f(input) } #[cfg(feature = "debug")] pub fn trace<'a, T, E, F>(msg: &str, f: F, input: &'a [u8]) -> ParseResult<'a, T, E> where F: Fn(&'a [u8]) -> ParseResult<'a, T, E>, { trace_eprintln!( msg, "⤷ input (len={}, type={})", input.len(), core::any::type_name::() ); let res = f(input); match &res { Ok((_rem, _)) => { trace_eprintln!( msg, "⤶ Parsed {} bytes, {} remaining", input.len() - _rem.len(), _rem.len() ); } Err(_) => { // NOTE: we do not need to print error, caller should print it debug_eprintln!(msg, "↯ Parsing failed at location:"); eprintln_hex_dump(input, 16); } } res } #[cfg(feature = "debug")] #[cfg(test)] mod tests { use std::collections::HashSet; use crate::*; use alloc::collections::BTreeSet; use hex_literal::hex; #[test] fn debug_from_ber_any() { assert!(Any::from_ber(&hex!("01 01 ff")).is_ok()); } #[test] fn debug_from_ber_failures() { // wrong type eprintln!("--"); assert!(>::from_ber(&hex!("02 01 00")).is_err()); } #[test] fn debug_from_ber_sequence_indefinite() { let input = &hex!("30 80 02 03 01 00 01 00 00"); let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..7]); assert_eq!(rem, &[]); eprintln!("--"); let (rem, result) = >::from_ber(input).expect("parsing failed"); assert_eq!(&result, &[65537]); assert_eq!(rem, &[]); } #[test] fn debug_from_ber_sequence_of() { // parsing failure (wrong type) let input = &hex!("30 03 01 01 00"); eprintln!("--"); let _ = >::from_ber(input).expect_err("parsing should fail"); eprintln!("--"); let _ = >::from_ber(input).expect_err("parsing should fail"); } #[test] fn debug_from_ber_u32() { assert!(u32::from_ber(&hex!("02 01 01")).is_ok()); } #[test] fn debug_from_der_any() { assert!(Any::from_der(&hex!("01 01 ff")).is_ok()); } #[test] fn debug_from_der_bool() { eprintln!("** first test is ok**"); assert!(::from_der(&hex!("01 01 ff")).is_ok()); eprintln!("** second test fails when parsing ANY (eof)**"); assert!(::from_der(&hex!("01 02 ff")).is_err()); eprintln!("** second test fails when checking DER constraints**"); assert!(::from_der(&hex!("01 01 f0")).is_err()); eprintln!("** second test fails during TryFrom**"); assert!(::from_der(&hex!("01 02 ff ff")).is_err()); } #[test] fn debug_from_der_failures() { use crate::Sequence; // parsing any failed eprintln!("--"); assert!(u16::from_der(&hex!("ff 00")).is_err()); // Indefinite length eprintln!("--"); assert!(u16::from_der(&hex!("30 80 00 00")).is_err()); // DER constraints failed eprintln!("--"); assert!(bool::from_der(&hex!("01 01 7f")).is_err()); // Incomplete sequence eprintln!("--"); let _ = Sequence::from_der(&hex!("30 81 04 00 00")); } #[test] fn debug_from_der_sequence() { // parsing OK, recursive let input = &hex!("30 08 02 03 01 00 01 02 01 01"); let (rem, result) = >::from_der(input).expect("parsing failed"); assert_eq!(&result, &[65537, 1]); assert_eq!(rem, &[]); } #[test] fn debug_from_der_sequence_fail() { // tag is wrong let input = &hex!("31 03 01 01 44"); let _ = >::from_der(input).expect_err("parsing should fail"); // sequence is ok but contraint fails on element let input = &hex!("30 03 01 01 44"); let _ = >::from_der(input).expect_err("parsing should fail"); } #[test] fn debug_from_der_sequence_of() { use crate::SequenceOf; // parsing failure (wrong type) let input = &hex!("30 03 01 01 00"); eprintln!("--"); let _ = >::from_der(input).expect_err("parsing should fail"); eprintln!("--"); let _ = >::from_der(input).expect_err("parsing should fail"); } #[test] fn debug_from_der_set_fail() { // set is ok but contraint fails on element let input = &hex!("31 03 01 01 44"); let _ = >::from_der(input).expect_err("parsing should fail"); } #[test] fn debug_from_der_set_of() { use crate::SetOf; use alloc::collections::BTreeSet; // parsing failure (wrong type) let input = &hex!("31 03 01 01 00"); eprintln!("--"); let _ = >::from_der(input).expect_err("parsing should fail"); eprintln!("--"); let _ = >::from_der(input).expect_err("parsing should fail"); eprintln!("--"); let _ = >::from_der(input).expect_err("parsing should fail"); } #[test] fn debug_from_der_string_ok() { let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); let (rem, result) = Utf8String::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), "Some-State"); assert_eq!(rem, &[]); } #[test] fn debug_from_der_string_fail() { // wrong charset let input = &hex!("12 03 41 42 43"); let _ = NumericString::from_der(input).expect_err("parsing should fail"); } } rusticata-asn1-rs-07e764c/src/derive.rs000066400000000000000000000265031476576401200177710ustar00rootroot00000000000000/// # BerSequence custom derive /// /// `BerSequence` is a custom derive attribute, to derive a BER [`Sequence`](super::Sequence) parser automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`TryFrom`](super::Any), also providing [`FromBer`](super::FromBer) /// - [`Tagged`](super::Tagged) /// /// `DerSequence` implies `BerSequence`, and will conflict with this custom derive. Use `BerSequence` when you only want the /// above traits derived. /// /// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromBer`](super::FromBer) trait. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To parse the following ASN.1 structure: ///
/// S ::= SEQUENCE {
///     a INTEGER(0..2^32),
///     b INTEGER(0..2^16),
///     c INTEGER(0..2^16),
/// }
/// 
/// /// Define a structure and add the `BerSequence` derive: /// /// ```rust /// use asn1_rs::*; /// /// #[derive(BerSequence)] /// struct S { /// a: u32, /// b: u16, /// c: u16 /// } /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(BerSequence)] /// #[debug_derive] /// struct S { /// a: u32, /// } /// ``` pub use asn1_rs_derive::BerSequence; /// # DerSequence custom derive /// /// `DerSequence` is a custom derive attribute, to derive both BER and DER [`Sequence`](super::Sequence) parsers automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`TryFrom`](super::Any), also providing [`FromBer`](super::FromBer) /// - [`Tagged`](super::Tagged) /// - [`CheckDerConstraints`](super::CheckDerConstraints) /// - [`FromDer`](super::FromDer) /// /// `DerSequence` implies `BerSequence`, and will conflict with this custom derive. /// /// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromDer`](super::FromDer) trait. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To parse the following ASN.1 structure: ///
/// S ::= SEQUENCE {
///     a INTEGER(0..2^32),
///     b INTEGER(0..2^16),
///     c INTEGER(0..2^16),
/// }
/// 
/// /// Define a structure and add the `DerSequence` derive: /// /// ```rust /// use asn1_rs::*; /// /// #[derive(DerSequence)] /// struct S { /// a: u32, /// b: u16, /// c: u16 /// } /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(DerSequence)] /// #[debug_derive] /// struct S { /// a: u32, /// } /// ``` pub use asn1_rs_derive::DerSequence; /// # BerSet custom derive /// /// `BerSet` is a custom derive attribute, to derive a BER [`Set`](super::Set) parser automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`TryFrom`](super::Any), also providing [`FromBer`](super::FromBer) /// - [`Tagged`](super::Tagged) /// /// `DerSet` implies `BerSet`, and will conflict with this custom derive. Use `BerSet` when you only want the /// above traits derived. /// /// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromBer`](super::FromBer) trait. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To parse the following ASN.1 structure: ///
/// S ::= SET {
///     a INTEGER(0..2^32),
///     b INTEGER(0..2^16),
///     c INTEGER(0..2^16),
/// }
/// 
/// /// Define a structure and add the `BerSet` derive: /// /// ```rust /// use asn1_rs::*; /// /// #[derive(BerSet)] /// struct S { /// a: u32, /// b: u16, /// c: u16 /// } /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(BerSet)] /// #[debug_derive] /// struct S { /// a: u32, /// } /// ``` pub use asn1_rs_derive::BerSet; /// # DerSet custom derive /// /// `DerSet` is a custom derive attribute, to derive both BER and DER [`Set`](super::Set) parsers automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`TryFrom`](super::Any), also providing [`FromBer`](super::FromBer) /// - [`Tagged`](super::Tagged) /// - [`CheckDerConstraints`](super::CheckDerConstraints) /// - [`FromDer`](super::FromDer) /// /// `DerSet` implies `BerSet`, and will conflict with this custom derive. /// /// Parsers will be automatically derived from struct fields. Every field type must implement the [`FromDer`](super::FromDer) trait. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To parse the following ASN.1 structure: ///
/// S ::= SEt {
///     a INTEGER(0..2^32),
///     b INTEGER(0..2^16),
///     c INTEGER(0..2^16),
/// }
/// 
/// /// Define a structure and add the `DerSet` derive: /// /// ```rust /// use asn1_rs::*; /// /// #[derive(DerSet)] /// struct S { /// a: u32, /// b: u16, /// c: u16 /// } /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(DerSet)] /// #[debug_derive] /// struct S { /// a: u32, /// } /// ``` pub use asn1_rs_derive::DerSet; /// # BerAlias custom derive /// /// `BerAlias` is a custom derive attribute, to derive a BER object parser automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`TryFrom`](super::Any), also providing [`FromBer`](super::FromBer) /// - [`Tagged`](super::Tagged) /// - [`CheckDerConstraints`](super::CheckDerConstraints) /// - [`FromDer`](super::FromDer) /// /// `DerAlias` implies `BerAlias`, and will conflict with this custom derive. Use `BerAlias` when you only want the /// above traits derived. /// /// When defining alias, only unnamed (tuple) structs with one field are supported. /// /// Parser will be automatically derived from the struct field. This field type must implement the `TryFrom` trait. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To parse the following ASN.1 object: ///
/// MyInt ::= INTEGER(0..2^32)
/// 
/// /// Define a structure and add the `BerAlias` derive: /// /// ```rust /// use asn1_rs::*; /// /// #[derive(BerAlias)] /// struct S(pub u32); /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(BerAlias)] /// #[debug_derive] /// struct S(pub u32); /// ``` pub use asn1_rs_derive::BerAlias; /// # DerAlias custom derive /// /// `DerAlias` is a custom derive attribute, to derive a DER object parser automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`TryFrom`](super::Any), also providing [`FromBer`](super::FromBer) /// - [`Tagged`](super::Tagged) /// /// `DerAlias` implies `BerAlias`, and will conflict with this custom derive. /// /// When defining alias, only unnamed (tuple) structs with one field are supported. /// /// Parser will be automatically derived from the struct field. This field type must implement the `TryFrom` and `FromDer` traits. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To parse the following ASN.1 object: ///
/// MyInt ::= INTEGER(0..2^32)
/// 
/// /// Define a structure and add the `DerAlias` derive: /// /// ```rust /// use asn1_rs::*; /// /// #[derive(DerAlias)] /// struct S(pub u32); /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(DerAlias)] /// #[debug_derive] /// struct S(pub u32); /// ``` pub use asn1_rs_derive::DerAlias; /// # ToStatic custom derive /// /// `ToStatic` is a custom derive attribute, to derive the [`ToStatic`](ToStatic) trait automatically from the structure definition. /// /// ## Example /// /// ```rust /// use asn1_rs::ToStatic; /// use std::borrow::Cow; /// /// #[derive(ToStatic)] /// struct S<'a>(pub Cow<'a, str>); /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::ToStatic; /// use std::borrow::Cow; /// /// #[derive(ToStatic)] /// #[debug_derive] /// struct S<'a>(pub Cow<'a, str>); /// ``` pub use asn1_rs_derive::ToStatic; /// # ToDerSequence custom derive /// /// `ToDerSequence` is a custom derive attribute, to derive both DER [`Sequence`](super::Sequence) serialization automatically from the structure definition. /// This attribute will automatically derive implementations for the following traits: /// - [`ToDer`](super::ToDer) /// /// Serialization will be automatically derived from struct fields. Every field type must implement the [`ToDer`](super::ToDer) trait. /// /// See [`derive`](crate::doc::derive) documentation for more examples and documentation. /// /// ## Examples /// /// To serialize the following ASN.1 structure: ///
/// S ::= SEQUENCE {
///     a INTEGER(0..2^32),
///     b INTEGER(0..2^16),
///     c INTEGER(0..2^16),
/// }
/// 
/// /// Define a structure and add the `DerSequence` derive: /// #[cfg_attr(feature = "std", doc = r#"```rust"#)] #[cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] /// use asn1_rs::*; /// /// #[derive(DerSequence, ToDerSequence)] /// struct S { /// a: u32, /// b: u16, /// c: u16 /// } /// /// let s = S { a: 1, b: 2, c: 3 }; /// let data = s.to_der_vec().expect("Serialization failed"); /// ``` /// /// ## Debugging /// /// To help debugging the generated code, the `#[debug_derive]` attribute has been added. /// /// When this attribute is specified, the generated code will be printed to `stderr` during compilation. /// /// Example: /// ```rust /// use asn1_rs::*; /// /// #[derive(DerSequence, ToDerSequence)] /// #[debug_derive] /// struct S { /// a: u32, /// } /// ``` pub use asn1_rs_derive::ToDerSequence; rusticata-asn1-rs-07e764c/src/doc/000077500000000000000000000000001476576401200167045ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/src/doc/mod.rs000066400000000000000000000004451476576401200200340ustar00rootroot00000000000000//! Additional documentation: recipes, specific use cases and examples, etc. #[doc = include_str!("../../doc/RECIPES.md")] pub mod recipes {} #[cfg(feature = "std")] #[doc = include_str!("../../doc/DERIVE.md")] pub mod derive {} #[doc = include_str!("../../doc/DEBUG.md")] pub mod debug {} rusticata-asn1-rs-07e764c/src/error.rs000066400000000000000000000134321476576401200176410ustar00rootroot00000000000000#![allow(unknown_lints)] #![allow(non_local_definitions)] // false positive for displaydoc::Display: https://github.com/yaahc/displaydoc/issues/46 use crate::{Class, Tag}; use alloc::str; use alloc::string; #[cfg(not(feature = "std"))] use alloc::string::String; use displaydoc::Display; use nom::error::{ErrorKind, FromExternalError, ParseError}; use nom::IResult; #[cfg(feature = "std")] use std::io; use thiserror::Error; #[derive(Clone, Copy, Debug, Display, PartialEq, Eq, Error)] /// Error types for DER constraints pub enum DerConstraint { /// Indefinite length not allowed IndefiniteLength, /// Object must not be constructed Constructed, /// Object must be constructed NotConstructed, /// DateTime object is missing timezone MissingTimeZone, /// DateTime object is missing seconds MissingSeconds, /// Bitstring unused bits must be set to zero UnusedBitsNotZero, /// Boolean value must be 0x00 of 0xff InvalidBoolean, /// Integer must not be empty IntegerEmpty, /// Leading zeroes in Integer encoding IntegerLeadingZeroes, /// Leading 0xff in negative Integer encoding IntegerLeadingFF, } /// The error type for operations of the [`FromBer`](crate::FromBer), /// [`FromDer`](crate::FromDer), and associated traits. #[derive(Clone, Debug, Display, PartialEq, Eq, Error)] pub enum Error { /// BER object does not have the expected type BerTypeError, /// BER object does not have the expected value BerValueError, /// Invalid Length InvalidLength, /// Invalid Value when parsing object with tag {tag:?} {msg:} InvalidValue { tag: Tag, msg: String }, /// Invalid Tag InvalidTag, /// Unknown tag: {0:?} UnknownTag(u32), /// Unexpected Tag (expected: {expected:?}, actual: {actual:?}) UnexpectedTag { expected: Option, actual: Tag }, /// Unexpected Class (expected: {expected:?}, actual: {actual:?}) UnexpectedClass { expected: Option, actual: Class, }, /// Indefinite length not allowed IndefiniteLengthUnexpected, /// DER object was expected to be constructed (and found to be primitive) ConstructExpected, /// DER object was expected to be primitive (and found to be constructed) ConstructUnexpected, /// Integer too large to fit requested type IntegerTooLarge, /// BER integer is negative, while an unsigned integer was requested IntegerNegative, /// BER recursive parsing reached maximum depth BerMaxDepth, /// Invalid encoding or forbidden characters in string StringInvalidCharset, /// Invalid Date or Time InvalidDateTime, /// DER Failed constraint: {0:?} DerConstraintFailed(DerConstraint), /// Requesting borrowed data from a temporary object LifetimeError, /// Feature is not yet implemented Unsupported, /// incomplete data, missing: {0:?} Incomplete(nom::Needed), /// nom error: {0:?} NomError(ErrorKind), } impl Error { /// Build an error from the provided invalid value #[inline] pub const fn invalid_value(tag: Tag, msg: String) -> Self { Self::InvalidValue { tag, msg } } /// Build an error from the provided unexpected class #[inline] pub const fn unexpected_class(expected: Option, actual: Class) -> Self { Self::UnexpectedClass { expected, actual } } /// Build an error from the provided unexpected tag #[inline] pub const fn unexpected_tag(expected: Option, actual: Tag) -> Self { Self::UnexpectedTag { expected, actual } } } impl<'a> ParseError<&'a [u8]> for Error { fn from_error_kind(_input: &'a [u8], kind: ErrorKind) -> Self { Error::NomError(kind) } fn append(_input: &'a [u8], kind: ErrorKind, _other: Self) -> Self { Error::NomError(kind) } } impl From for nom::Err { fn from(e: Error) -> Self { nom::Err::Error(e) } } impl From for Error { fn from(_: str::Utf8Error) -> Self { Error::StringInvalidCharset } } impl From for Error { fn from(_: string::FromUtf8Error) -> Self { Error::StringInvalidCharset } } impl From for Error { fn from(_: string::FromUtf16Error) -> Self { Error::StringInvalidCharset } } impl From> for Error { fn from(e: nom::Err) -> Self { match e { nom::Err::Incomplete(n) => Self::Incomplete(n), nom::Err::Error(e) | nom::Err::Failure(e) => e, } } } impl FromExternalError for Error { fn from_external_error(_input: I, kind: ErrorKind, _e: E) -> Error { Error::NomError(kind) } } /// Flatten all `nom::Err` variants error into a single error type pub fn from_nom_error(e: nom::Err) -> F where F: From + From, { match e { nom::Err::Error(e) | nom::Err::Failure(e) => F::from(e), nom::Err::Incomplete(n) => F::from(Error::Incomplete(n)), } } /// Holds the result of BER/DER serialization functions pub type ParseResult<'a, T, E = Error> = IResult<&'a [u8], T, E>; /// A specialized `Result` type for all operations from this crate. pub type Result = core::result::Result; /// The error type for serialization operations of the [`ToDer`](crate::ToDer) trait. #[cfg(feature = "std")] #[derive(Debug, Error)] pub enum SerializeError { #[error("ASN.1 error: {0:?}")] ASN1Error(#[from] Error), #[error("Invalid Class {class:}")] InvalidClass { class: u8 }, #[error("Invalid Length")] InvalidLength, #[error("I/O error: {0:?}")] IOError(#[from] io::Error), } #[cfg(feature = "std")] /// Holds the result of BER/DER encoding functions pub type SerializeResult = std::result::Result; rusticata-asn1-rs-07e764c/src/header.rs000066400000000000000000000373351476576401200177500ustar00rootroot00000000000000use crate::ber::*; use crate::der_constraint_fail_if; use crate::error::*; #[cfg(feature = "std")] use crate::ToDer; use crate::{BerParser, Class, DerParser, DynTagged, FromBer, FromDer, Length, Tag, ToStatic}; use alloc::borrow::Cow; use core::convert::TryFrom; use nom::bytes::streaming::take; /// BER/DER object header (identifier and length) #[derive(Clone, Debug)] pub struct Header<'a> { /// Object class: universal, application, context-specific, or private pub(crate) class: Class, /// Constructed attribute: true if constructed, else false pub(crate) constructed: bool, /// Tag number pub(crate) tag: Tag, /// Object length: value if definite, or indefinite pub(crate) length: Length, /// Optionally, the raw encoding of the tag /// /// This is useful in some cases, where different representations of the same /// BER tags have different meanings (BER only) pub(crate) raw_tag: Option>, } impl<'a> Header<'a> { /// Build a new BER/DER header from the provided values pub const fn new(class: Class, constructed: bool, tag: Tag, length: Length) -> Self { Header { tag, constructed, class, length, raw_tag: None, } } /// Build a new BER/DER header from the provided tag, with default values for other fields #[inline] pub const fn new_simple(tag: Tag) -> Self { let constructed = matches!(tag, Tag::Sequence | Tag::Set); Self::new(Class::Universal, constructed, tag, Length::Definite(0)) } /// Set the class of this `Header` #[inline] pub fn with_class(self, class: Class) -> Self { Self { class, ..self } } /// Set the constructed flags of this `Header` #[inline] pub fn with_constructed(self, constructed: bool) -> Self { Self { constructed, ..self } } /// Set the tag of this `Header` #[inline] pub fn with_tag(self, tag: Tag) -> Self { Self { tag, ..self } } /// Set the length of this `Header` #[inline] pub fn with_length(self, length: Length) -> Self { Self { length, ..self } } /// Update header to add reference to raw tag #[inline] pub fn with_raw_tag(self, raw_tag: Option>) -> Self { Header { raw_tag, ..self } } /// Return the class of this header. #[inline] pub const fn class(&self) -> Class { self.class } /// Return true if this header has the 'constructed' flag. #[inline] pub const fn constructed(&self) -> bool { self.constructed } /// Return the tag of this header. #[inline] pub const fn tag(&self) -> Tag { self.tag } /// Return the length of this header. #[inline] pub const fn length(&self) -> Length { self.length } /// Return the raw tag encoding, if it was stored in this object #[inline] pub fn raw_tag(&self) -> Option<&[u8]> { self.raw_tag.as_ref().map(|cow| cow.as_ref()) } /// Test if object is primitive #[inline] pub const fn is_primitive(&self) -> bool { !self.constructed } /// Test if object is constructed #[inline] pub const fn is_constructed(&self) -> bool { self.constructed } /// Return error if class is not the expected class #[inline] pub const fn assert_class(&self, class: Class) -> Result<()> { self.class.assert_eq(class) } /// Return error if tag is not the expected tag #[inline] pub const fn assert_tag(&self, tag: Tag) -> Result<()> { self.tag.assert_eq(tag) } /// Return error if object is not primitive #[inline] pub const fn assert_primitive(&self) -> Result<()> { if self.is_primitive() { Ok(()) } else { Err(Error::ConstructUnexpected) } } /// Return error if object is primitive #[inline] pub const fn assert_constructed(&self) -> Result<()> { if !self.is_primitive() { Ok(()) } else { Err(Error::ConstructExpected) } } /// Test if object class is Universal #[inline] pub const fn is_universal(&self) -> bool { self.class as u8 == Class::Universal as u8 } /// Test if object class is Application #[inline] pub const fn is_application(&self) -> bool { self.class as u8 == Class::Application as u8 } /// Test if object class is Context-specific #[inline] pub const fn is_contextspecific(&self) -> bool { self.class as u8 == Class::ContextSpecific as u8 } /// Test if object class is Private #[inline] pub const fn is_private(&self) -> bool { self.class as u8 == Class::Private as u8 } /// Return error if object length is definite #[inline] pub const fn assert_definite(&self) -> Result<()> { if self.length.is_definite() { Ok(()) } else { Err(Error::DerConstraintFailed(DerConstraint::IndefiniteLength)) } } /// Get the content following a BER header #[inline] pub fn parse_ber_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> { // defaults to maximum depth 8 // depth is used only if BER, and length is indefinite BerParser::get_object_content(i, self, 8) } /// Get the content following a DER header #[inline] pub fn parse_der_content<'i>(&'_ self, i: &'i [u8]) -> ParseResult<'i, &'i [u8]> { self.assert_definite()?; DerParser::get_object_content(i, self, 8) } } impl From for Header<'_> { #[inline] fn from(tag: Tag) -> Self { let constructed = matches!(tag, Tag::Sequence | Tag::Set); Self::new(Class::Universal, constructed, tag, Length::Definite(0)) } } impl ToStatic for Header<'_> { type Owned = Header<'static>; fn to_static(&self) -> Self::Owned { let raw_tag: Option> = self.raw_tag.as_ref().map(|b| Cow::Owned(b.to_vec())); Header { tag: self.tag, constructed: self.constructed, class: self.class, length: self.length, raw_tag, } } } impl<'a> FromBer<'a> for Header<'a> { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (i1, el) = parse_identifier(bytes)?; let class = match Class::try_from(el.0) { Ok(c) => c, Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits }; let (i2, len) = parse_ber_length_byte(i1)?; let (i3, len) = match (len.0, len.1) { (0, l1) => { // Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4) (i2, Length::Definite(usize::from(l1))) } (_, 0) => { // Indefinite form: MSB is 1, the rest is 0 (8.1.3.6) // If encoding is primitive, definite form shall be used (8.1.3.2) if el.1 == 0 { return Err(nom::Err::Error(Error::ConstructExpected)); } (i2, Length::Indefinite) } (_, l1) => { // if len is 0xff -> error (8.1.3.5) if l1 == 0b0111_1111 { return Err(nom::Err::Error(Error::InvalidLength)); } let (i3, llen) = take(l1)(i2)?; match bytes_to_u64(llen) { Ok(l) => { let l = usize::try_from(l).or(Err(nom::Err::Error(Error::InvalidLength)))?; (i3, Length::Definite(l)) } Err(_) => { return Err(nom::Err::Error(Error::InvalidLength)); } } } }; let constructed = el.1 != 0; let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into())); Ok((i3, hdr)) } } impl<'a> FromDer<'a> for Header<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (i1, el) = parse_identifier(bytes)?; let class = match Class::try_from(el.0) { Ok(c) => c, Err(_) => unreachable!(), // Cannot fail, we have read exactly 2 bits }; let (i2, len) = parse_ber_length_byte(i1)?; let (i3, len) = match (len.0, len.1) { (0, l1) => { // Short form: MSB is 0, the rest encodes the length (which can be 0) (8.1.3.4) (i2, Length::Definite(usize::from(l1))) } (_, 0) => { // Indefinite form is not allowed in DER (10.1) return Err(nom::Err::Error(Error::DerConstraintFailed( DerConstraint::IndefiniteLength, ))); } (_, l1) => { // if len is 0xff -> error (8.1.3.5) if l1 == 0b0111_1111 { return Err(nom::Err::Error(Error::InvalidLength)); } // DER(9.1) if len is 0 (indefinite form), obj must be constructed der_constraint_fail_if!( &i[1..], len.1 == 0 && el.1 != 1, DerConstraint::NotConstructed ); let (i3, llen) = take(l1)(i2)?; match bytes_to_u64(llen) { Ok(l) => { // DER: should have been encoded in short form (< 127) // XXX der_constraint_fail_if!(i, l < 127); let l = usize::try_from(l).or(Err(nom::Err::Error(Error::InvalidLength)))?; (i3, Length::Definite(l)) } Err(_) => { return Err(nom::Err::Error(Error::InvalidLength)); } } } }; let constructed = el.1 != 0; let hdr = Header::new(class, constructed, Tag(el.2), len).with_raw_tag(Some(el.3.into())); Ok((i3, hdr)) } } impl DynTagged for (Class, bool, Tag) { fn tag(&self) -> Tag { self.2 } } #[cfg(feature = "std")] impl ToDer for (Class, bool, Tag) { fn to_der_len(&self) -> Result { let (_, _, tag) = self; match tag.0 { 0..=30 => Ok(1), t => { let mut sz = 1; let mut val = t; loop { if val <= 127 { return Ok(sz + 1); } else { val >>= 7; sz += 1; } } } } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let (class, constructed, tag) = self; let b0 = (*class as u8) << 6; let b0 = b0 | if *constructed { 0b10_0000 } else { 0 }; if tag.0 > 30 { let mut val = tag.0; const BUF_SZ: usize = 8; let mut buffer = [0u8; BUF_SZ]; let mut current_index = BUF_SZ - 1; // first byte: class+constructed+0x1f let b0 = b0 | 0b1_1111; let mut sz = writer.write(&[b0])?; // now write bytes from right (last) to left // last encoded byte buffer[current_index] = (val & 0x7f) as u8; val >>= 7; while val > 0 { current_index -= 1; if current_index == 0 { return Err(SerializeError::InvalidLength); } buffer[current_index] = (val & 0x7f) as u8 | 0x80; val >>= 7; } sz += writer.write(&buffer[current_index..])?; Ok(sz) } else { let b0 = b0 | (tag.0 as u8); let sz = writer.write(&[b0])?; Ok(sz) } } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } } impl DynTagged for Header<'_> { fn tag(&self) -> Tag { self.tag } } #[cfg(feature = "std")] impl ToDer for Header<'_> { fn to_der_len(&self) -> Result { let tag_len = (self.class, self.constructed, self.tag).to_der_len()?; let len_len = self.length.to_der_len()?; Ok(tag_len + len_len) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let sz = (self.class, self.constructed, self.tag).write_der_header(writer)?; let sz = sz + self.length.write_der_header(writer)?; Ok(sz) } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // use raw_tag if present let sz = match &self.raw_tag { Some(t) => writer.write(t)?, None => (self.class, self.constructed, self.tag).write_der_header(writer)?, }; let sz = sz + self.length.write_der_header(writer)?; Ok(sz) } } /// Compare two BER headers. `len` fields are compared only if both objects have it set (same for `raw_tag`) impl<'a> PartialEq> for Header<'a> { fn eq(&self, other: &Header) -> bool { self.class == other.class && self.tag == other.tag && self.constructed == other.constructed && { if self.length.is_null() && other.length.is_null() { self.length == other.length } else { true } } && { // it tag is present for both, compare it if self.raw_tag.as_ref().xor(other.raw_tag.as_ref()).is_none() { self.raw_tag == other.raw_tag } else { true } } } } impl Eq for Header<'_> {} #[cfg(all(test, feature = "std"))] mod tests { use crate::*; use hex_literal::hex; /// Generic tests on methods, and coverage tests #[test] fn methods_header() { // Getters let input = &hex! {"02 01 00"}; let (rem, header) = Header::from_ber(input).expect("parsing header failed"); assert_eq!(header.class(), Class::Universal); assert_eq!(header.tag(), Tag::Integer); assert!(header.assert_primitive().is_ok()); assert!(header.assert_constructed().is_err()); assert!(header.is_universal()); assert!(!header.is_application()); assert!(!header.is_private()); assert_eq!(rem, &input[2..]); // test PartialEq let hdr2 = Header::new_simple(Tag::Integer); assert_eq!(header, hdr2); // builder methods let hdr3 = hdr2 .with_class(Class::ContextSpecific) .with_constructed(true) .with_length(Length::Definite(1)); assert!(hdr3.constructed()); assert!(hdr3.is_constructed()); assert!(hdr3.assert_constructed().is_ok()); assert!(hdr3.is_contextspecific()); let xx = hdr3.to_der_vec().expect("serialize failed"); assert_eq!(&xx, &[0xa2, 0x01]); // indefinite length let hdr4 = hdr3.with_length(Length::Indefinite); assert!(hdr4.assert_definite().is_err()); let xx = hdr4.to_der_vec().expect("serialize failed"); assert_eq!(&xx, &[0xa2, 0x80]); // parse_*_content let hdr = Header::new_simple(Tag(2)).with_length(Length::Definite(1)); let (_, r) = hdr.parse_ber_content(&input[2..]).unwrap(); assert_eq!(r, &input[2..]); let (_, r) = hdr.parse_der_content(&input[2..]).unwrap(); assert_eq!(r, &input[2..]); } } rusticata-asn1-rs-07e764c/src/length.rs000066400000000000000000000113411476576401200177660ustar00rootroot00000000000000use crate::{DynTagged, Error, Result, Tag}; #[cfg(feature = "std")] use crate::{SerializeResult, ToDer}; use core::ops; /// BER Object Length #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum Length { /// Definite form (X.690 8.1.3.3) Definite(usize), /// Indefinite form (X.690 8.1.3.6) Indefinite, } impl Length { /// Return true if length is definite and equal to 0 #[inline] pub fn is_null(&self) -> bool { *self == Length::Definite(0) } /// Get length of primitive object #[inline] pub fn definite(&self) -> Result { match self { Length::Definite(sz) => Ok(*sz), Length::Indefinite => Err(Error::IndefiniteLengthUnexpected), } } /// Return true if length is definite #[inline] pub const fn is_definite(&self) -> bool { matches!(self, Length::Definite(_)) } /// Return error if length is not definite #[inline] pub const fn assert_definite(&self) -> Result<()> { match self { Length::Definite(_) => Ok(()), Length::Indefinite => Err(Error::IndefiniteLengthUnexpected), } } } impl From for Length { fn from(l: usize) -> Self { Length::Definite(l) } } impl ops::Add for Length { type Output = Self; fn add(self, rhs: Length) -> Self::Output { match self { Length::Indefinite => self, Length::Definite(lhs) => match rhs { Length::Indefinite => rhs, Length::Definite(rhs) => Length::Definite(lhs + rhs), }, } } } impl ops::Add for Length { type Output = Self; fn add(self, rhs: usize) -> Self::Output { match self { Length::Definite(lhs) => Length::Definite(lhs + rhs), Length::Indefinite => self, } } } impl ops::AddAssign for Length { fn add_assign(&mut self, rhs: usize) { match self { Length::Definite(ref mut lhs) => *lhs += rhs, Length::Indefinite => (), } } } impl DynTagged for Length { fn tag(&self) -> Tag { Tag(0) } } #[cfg(feature = "std")] impl ToDer for Length { fn to_der_len(&self) -> Result { match self { Length::Indefinite => Ok(1), Length::Definite(l) => match l { 0..=0x7f => Ok(1), 0x80..=0xff => Ok(2), 0x100..=0xffff => Ok(3), 0x1_0000..=0xffff_ffff => Ok(4), _ => Err(Error::InvalidLength), }, } } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { match *self { Length::Indefinite => { let sz = writer.write(&[0b1000_0000])?; Ok(sz) } Length::Definite(l) => { if l <= 127 { // Short form let sz = writer.write(&[l as u8])?; Ok(sz) } else { // Long form let b = l.to_be_bytes(); // skip leading zeroes // we do not have to test for length, l cannot be 0 let mut idx = 0; while b[idx] == 0 { idx += 1; } let b = &b[idx..]; // first byte: 0x80 + length of length let b0 = 0x80 | (b.len() as u8); let sz = writer.write(&[b0])?; let sz = sz + writer.write(b)?; Ok(sz) } } } } fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult { Ok(0) } } #[cfg(test)] mod tests { use crate::*; /// Generic and coverage tests #[test] fn methods_length() { let l = Length::from(2); assert_eq!(l.definite(), Ok(2)); assert!(l.assert_definite().is_ok()); let l = Length::Indefinite; assert!(l.definite().is_err()); assert!(l.assert_definite().is_err()); let l = Length::from(2); assert_eq!(l + 2, Length::from(4)); assert_eq!(l + Length::Indefinite, Length::Indefinite); let l = Length::Indefinite; assert_eq!(l + 2, Length::Indefinite); let l = Length::from(2); assert_eq!(l + Length::from(2), Length::from(4)); let l = Length::Indefinite; assert_eq!(l + Length::from(2), Length::Indefinite); let mut l = Length::from(2); l += 2; assert_eq!(l.definite(), Ok(4)); let mut l = Length::Indefinite; l += 2; assert_eq!(l, Length::Indefinite); } } rusticata-asn1-rs-07e764c/src/lib.rs000066400000000000000000000172351476576401200172630ustar00rootroot00000000000000//! [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE-MIT) //! [![Apache License 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](./LICENSE-APACHE) //! [![docs.rs](https://docs.rs/asn1-rs/badge.svg)](https://docs.rs/asn1-rs) //! [![crates.io](https://img.shields.io/crates/v/asn1-rs.svg)](https://crates.io/crates/asn1-rs) //! [![Download numbers](https://img.shields.io/crates/d/asn1-rs.svg)](https://crates.io/crates/asn1-rs) //! [![Github CI](https://github.com/rusticata/asn1-rs/workflows/Continuous%20integration/badge.svg)](https://github.com/rusticata/asn1-rs/actions) //! [![Minimum rustc version](https://img.shields.io/badge/rustc-1.63.0+-lightgray.svg)](#rust-version-requirements) //! //! # BER/DER Parsers/Encoders //! //! A set of parsers/encoders for Basic Encoding Rules (BER [[X.690]]) and Distinguished Encoding Rules(DER //! [[X.690]]) formats, implemented with the [nom] parser combinator framework. //! //! It is written in pure Rust, fast, and makes extensive use of zero-copy. A lot of care is taken //! to ensure security and safety of this crate, including design (recursion limit, defensive //! programming), tests, and fuzzing. It also aims to be panic-free. //! //! This crate is a rewrite of [der-parser](https://crates.io/crates/der-parser) to propose a more data-oriented API, //! and add generalized support for serialization. //! //! Many ideas were borrowed from the [crypto/utils/der](https://github.com/RustCrypto/utils/tree/master/der) crate (like //! the `Any`/`TryFrom`/`FromDer` mechanism), adapted and merged into a generalized BER/DER crate. //! Credits (and many thanks) go to Tony Arcieri for writing the original crate. //! //! # BER/DER parsers //! //! BER stands for Basic Encoding Rules, and is defined in [[X.690]]. It defines a set of rules to //! encode and decode ASN.1 [[X.680]] objects in binary. //! //! [[X.690]] also defines Distinguished Encoding Rules (DER), which is BER with added rules to //! ensure canonical and unequivocal binary representation of objects. //! //! The choice of which one to use is usually guided by the speficication of the data format based //! on BER or DER: for example, X.509 uses DER as encoding representation. //! //! The main traits for parsing are the [`FromBer`] and [`FromDer`] traits. //! These traits provide methods to parse binary input, and return either the remaining (unparsed) bytes //! and the parsed object, or an error. //! //! The parsers follow the interface from [nom], and the [`ParseResult`] object is a specialized version //! of `nom::IResult`. This means that most `nom` combinators (`map`, `many0`, etc.) can be used in //! combination to objects and methods from this crate. Reading the nom documentation may //! help understanding how to write and combine parsers and use the output. //! //! **Minimum Supported Rust Version**: 1.63.0 //! //! # Recipes //! //! See [doc::recipes] and [doc::derive] for more examples and recipes. //! //! See [doc::debug] for advice and tools to debug parsers. //! //! ## Examples //! //! Parse 2 BER integers: //! //! ```rust //! use asn1_rs::{Integer, FromBer}; //! //! let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, //! 0x02, 0x03, 0x01, 0x00, 0x00, //! ]; //! //! let (rem, obj1) = Integer::from_ber(&bytes).expect("parsing failed"); //! let (rem, obj2) = Integer::from_ber(&bytes).expect("parsing failed"); //! //! assert_eq!(obj1, Integer::from_u32(65537)); //! ``` //! //! In the above example, the generic [`Integer`] type is used. This type can contain integers of any //! size, but do not provide a simple API to manipulate the numbers. //! //! In most cases, the integer either has a limit, or is expected to fit into a primitive type. //! To get a simple value, just use the `from_ber`/`from_der` methods on the primitive types: //! //! ```rust //! use asn1_rs::FromBer; //! //! let bytes = [ 0x02, 0x03, 0x01, 0x00, 0x01, //! 0x02, 0x03, 0x01, 0x00, 0x00, //! ]; //! //! let (rem, obj1) = u32::from_ber(&bytes).expect("parsing failed"); //! let (rem, obj2) = u32::from_ber(&rem).expect("parsing failed"); //! //! assert_eq!(obj1, 65537); //! assert_eq!(obj2, 65536); //! ``` //! //! If the parsing succeeds, but the integer cannot fit into the expected type, the method will return //! an `IntegerTooLarge` error. //! //! # BER/DER encoders //! //! BER/DER encoding is symmetrical to decoding, using the traits `ToBer` and [`ToDer`] traits. //! These traits provide methods to write encoded content to objects with the `io::Write` trait, //! or return an allocated `Vec` with the encoded data. //! If the serialization fails, an error is returned. //! //! ## Examples //! //! Writing 2 BER integers: //! #![cfg_attr(feature = "std", doc = r#"```rust"#)] #![cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] //! use asn1_rs::{Integer, ToDer}; //! //! let mut writer = Vec::new(); //! //! let obj1 = Integer::from_u32(65537); //! let obj2 = Integer::from_u32(65536); //! //! let _ = obj1.write_der(&mut writer).expect("serialization failed"); //! let _ = obj2.write_der(&mut writer).expect("serialization failed"); //! //! let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, //! 0x02, 0x03, 0x01, 0x00, 0x00, //! ]; //! assert_eq!(&writer, bytes); //! ``` //! //! Similarly to `FromBer`/`FromDer`, serialization methods are also implemented for primitive types: //! #![cfg_attr(feature = "std", doc = r#"```rust"#)] #![cfg_attr(not(feature = "std"), doc = r#"```rust,compile_fail"#)] //! use asn1_rs::ToDer; //! //! let mut writer = Vec::new(); //! //! let _ = 65537.write_der(&mut writer).expect("serialization failed"); //! let _ = 65536.write_der(&mut writer).expect("serialization failed"); //! //! let bytes = &[ 0x02, 0x03, 0x01, 0x00, 0x01, //! 0x02, 0x03, 0x01, 0x00, 0x00, //! ]; //! assert_eq!(&writer, bytes); //! ``` //! //! If the parsing succeeds, but the integer cannot fit into the expected type, the method will return //! an `IntegerTooLarge` error. //! //! ## Changes //! //! See `CHANGELOG.md`. //! //! # References //! //! - [[X.680]] Abstract Syntax Notation One (ASN.1): Specification of basic notation. //! - [[X.690]] ASN.1 encoding rules: Specification of Basic Encoding Rules (BER), Canonical //! Encoding Rules (CER) and Distinguished Encoding Rules (DER). //! //! [X.680]: http://www.itu.int/rec/T-REC-X.680/en "Abstract Syntax Notation One (ASN.1): //! Specification of basic notation." //! [X.690]: https://www.itu.int/rec/T-REC-X.690/en "ASN.1 encoding rules: Specification of //! Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules //! (DER)." //! [nom]: https://github.com/Geal/nom "Nom parser combinator framework" #![deny(/*missing_docs,*/ unstable_features, unused_import_braces, unused_qualifications, // unreachable_pub )] #![forbid(unsafe_code)] #![warn( /* missing_docs, rust_2018_idioms,*/ missing_debug_implementations, )] // pragmas for doc #![deny(rustdoc::broken_intra_doc_links)] #![cfg_attr(docsrs, feature(doc_cfg))] #![doc(test( no_crate_inject, attr(deny(warnings/*, rust_2018_idioms*/), allow(dead_code, unused_variables)) ))] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(feature = "std")] extern crate core; // #[cfg(feature = "alloc")] extern crate alloc; mod asn1_types; mod ber; mod class; mod datetime; mod debug; mod derive; mod error; mod header; mod length; mod tag; mod tostatic; mod traits; pub use asn1_types::*; pub use class::*; pub use datetime::*; pub use derive::*; pub use error::*; pub use header::*; pub use length::*; pub use tag::*; pub use traits::*; pub use nom; pub use nom::{Err, IResult, Needed}; #[doc(hidden)] pub mod exports { pub use alloc::borrow; pub use asn1_rs_impl; } #[cfg(doc)] pub mod doc; rusticata-asn1-rs-07e764c/src/tag.rs000066400000000000000000000030331476576401200172570ustar00rootroot00000000000000use crate::{Error, Result}; #[cfg(not(feature = "std"))] use alloc::string::ToString; use rusticata_macros::newtype_enum; /// BER/DER Tag as defined in X.680 section 8.4 /// /// X.690 doesn't specify the maximum tag size so we're assuming that people /// aren't going to need anything more than a u32. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Tag(pub u32); newtype_enum! { impl display Tag { EndOfContent = 0, Boolean = 1, Integer = 2, BitString = 3, OctetString = 4, Null = 5, Oid = 6, ObjectDescriptor = 7, External = 8, RealType = 9, Enumerated = 10, EmbeddedPdv = 11, Utf8String = 12, RelativeOid = 13, Sequence = 16, Set = 17, NumericString = 18, PrintableString = 19, T61String = 20, TeletexString = 20, VideotexString = 21, Ia5String = 22, UtcTime = 23, GeneralizedTime = 24, GraphicString = 25, VisibleString = 26, GeneralString = 27, UniversalString = 28, CharacterString = 29, BmpString = 30, } } impl Tag { pub const fn assert_eq(&self, tag: Tag) -> Result<()> { if self.0 == tag.0 { Ok(()) } else { Err(Error::UnexpectedTag { expected: Some(tag), actual: *self, }) } } pub fn invalid_value(&self, msg: &str) -> Error { Error::InvalidValue { tag: *self, msg: msg.to_string(), } } } impl From for Tag { fn from(v: u32) -> Self { Tag(v) } } rusticata-asn1-rs-07e764c/src/tostatic.rs000066400000000000000000000037751476576401200203530ustar00rootroot00000000000000use alloc::borrow::Cow; use alloc::string::ToString; /// Common trait for objects that can be transformed to a `'static` version of `self` pub trait ToStatic { type Owned: 'static; fn to_static(&self) -> Self::Owned; } impl ToStatic for Cow<'_, str> { type Owned = Cow<'static, str>; fn to_static(&self) -> ::Owned { Cow::Owned(self.to_string()) } } impl ToStatic for Cow<'_, [u8]> { type Owned = Cow<'static, [u8]>; fn to_static(&self) -> ::Owned { Cow::Owned(self.to_vec()) } } macro_rules! impl_tostatic_primitive { ($t:ty) => { impl ToStatic for $t { type Owned = $t; fn to_static(&self) -> ::Owned { self.clone() } } }; ($t:ty => $to:ty, $closure:expr) => { impl ToStatic for $t { type Owned = $to; fn to_static(&self) -> ::Owned { let f: &dyn Fn(&Self) -> $to = &$closure; f(self) } } }; (I $($types: ty)+) => { $( impl_tostatic_primitive!($types); )* }; } impl_tostatic_primitive!(bool); impl_tostatic_primitive!(I i8 i16 i32 i64 i128 isize); impl_tostatic_primitive!(I u8 u16 u32 u64 u128 usize); impl ToStatic for &'_ T where T: ToStatic, { type Owned = T::Owned; fn to_static(&self) -> Self::Owned { (*self).to_static() } } impl ToStatic for Option where T: ToStatic, { type Owned = Option; fn to_static(&self) -> Self::Owned { self.as_ref().map(ToStatic::to_static) } } #[cfg(feature = "std")] const _: () = { impl_tostatic_primitive!(I String); impl_tostatic_primitive!(str => String, |s| s.to_string()); impl ToStatic for Box where T: ToStatic, { type Owned = Box; fn to_static(&self) -> Self::Owned { Box::new(self.as_ref().to_static()) } } }; rusticata-asn1-rs-07e764c/src/traits.rs000066400000000000000000000250701476576401200200170ustar00rootroot00000000000000use crate::debug::{trace, trace_generic}; use crate::error::*; use crate::{parse_der_any, Any, Class, Explicit, Implicit, Tag, TaggedParser}; use core::convert::{TryFrom, TryInto}; use core::fmt::{Debug, Display}; #[cfg(feature = "std")] use std::io::Write; /// Phantom type representing a BER parser #[doc(hidden)] #[derive(Debug)] pub enum BerParser {} /// Phantom type representing a DER parser #[doc(hidden)] #[derive(Debug)] pub enum DerParser {} #[doc(hidden)] pub trait ASN1Parser {} impl ASN1Parser for BerParser {} impl ASN1Parser for DerParser {} pub trait Tagged { const TAG: Tag; } impl Tagged for &'_ T where T: Tagged, { const TAG: Tag = T::TAG; } pub trait DynTagged { fn tag(&self) -> Tag; } impl DynTagged for T where T: Tagged, { fn tag(&self) -> Tag { T::TAG } } /// Base trait for BER object parsers /// /// Library authors should usually not directly implement this trait, but should prefer implementing the /// [`TryFrom`] trait, /// which offers greater flexibility and provides an equivalent `FromBer` implementation for free. /// /// # Examples /// /// ``` /// use asn1_rs::{Any, Result, Tag}; /// use std::convert::TryFrom; /// /// // The type to be decoded /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] /// pub struct MyType(pub u32); /// /// impl<'a> TryFrom> for MyType { /// type Error = asn1_rs::Error; /// /// fn try_from(any: Any<'a>) -> Result { /// any.tag().assert_eq(Tag::Integer)?; /// // for this fictive example, the type contains the number of characters /// let n = any.data.len() as u32; /// Ok(MyType(n)) /// } /// } /// /// // The above code provides a `FromBer` implementation for free. /// /// // Example of parsing code: /// use asn1_rs::FromBer; /// /// let input = &[2, 1, 2]; /// // Objects can be parsed using `from_ber`, which returns the remaining bytes /// // and the parsed object: /// let (rem, my_type) = MyType::from_ber(input).expect("parsing failed"); /// ``` pub trait FromBer<'a, E = Error>: Sized { /// Attempt to parse input bytes into a BER object fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self, E>; } impl<'a, T, E> FromBer<'a, E> for T where T: TryFrom, Error = E>, E: From, { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, T, E> { let (i, any) = Any::from_ber(bytes).map_err(nom::Err::convert)?; let result = any.try_into().map_err(nom::Err::Error)?; Ok((i, result)) } } /// Base trait for DER object parsers /// /// Library authors should usually not directly implement this trait, but should prefer implementing the /// [`TryFrom`] + [`CheckDerConstraints`] traits, /// which offers greater flexibility and provides an equivalent `FromDer` implementation for free /// (in fact, it provides both [`FromBer`] and `FromDer`). /// /// Note: if you already implemented [`TryFrom`] and [`CheckDerConstraints`], /// you can get a free [`FromDer`] implementation by implementing the /// [`DerAutoDerive`] trait. This is not automatic, so it is also possible to manually /// implement [`FromDer`] if preferred. /// /// # Examples /// /// ``` /// use asn1_rs::{Any, CheckDerConstraints, DerAutoDerive, Result, Tag}; /// use std::convert::TryFrom; /// /// // The type to be decoded /// #[derive(Clone, Copy, Debug, PartialEq, Eq)] /// pub struct MyType(pub u32); /// /// impl<'a> TryFrom> for MyType { /// type Error = asn1_rs::Error; /// /// fn try_from(any: Any<'a>) -> Result { /// any.tag().assert_eq(Tag::Integer)?; /// // for this fictive example, the type contains the number of characters /// let n = any.data.len() as u32; /// Ok(MyType(n)) /// } /// } /// /// impl CheckDerConstraints for MyType { /// fn check_constraints(any: &Any) -> Result<()> { /// any.header.assert_primitive()?; /// Ok(()) /// } /// } /// /// impl DerAutoDerive for MyType {} /// /// // The above code provides a `FromDer` implementation for free. /// /// // Example of parsing code: /// use asn1_rs::FromDer; /// /// let input = &[2, 1, 2]; /// // Objects can be parsed using `from_der`, which returns the remaining bytes /// // and the parsed object: /// let (rem, my_type) = MyType::from_der(input).expect("parsing failed"); /// ``` pub trait FromDer<'a, E = Error>: Sized { /// Attempt to parse input bytes into a DER object (enforcing constraints) fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E>; } /// Trait to automatically derive `FromDer` /// /// This trait is only a marker to control if a DER parser should be automatically derived. It is /// empty. /// /// This trait is used in combination with others: /// after implementing [`TryFrom`] and [`CheckDerConstraints`] for a type, /// a free [`FromDer`] implementation is provided by implementing the /// [`DerAutoDerive`] trait. This is the most common case. /// /// However, this is not automatic so it is also possible to manually /// implement [`FromDer`] if preferred. /// Manual implementation is generally only needed for generic containers (for ex. `Vec`), /// because the default implementation adds a constraint on `T` to implement also `TryFrom` /// and `CheckDerConstraints`. This is problematic when `T` only provides `FromDer`, and can be /// solved by providing a manual implementation of [`FromDer`]. pub trait DerAutoDerive {} impl<'a, T, E> FromDer<'a, E> for T where T: TryFrom, Error = E>, T: CheckDerConstraints, T: DerAutoDerive, E: From + Display + Debug, { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, T, E> { trace_generic( core::any::type_name::(), "T::from_der", |bytes| { let (i, any) = trace(core::any::type_name::(), parse_der_any, bytes) .map_err(nom::Err::convert)?; ::check_constraints(&any) .map_err(|e| nom::Err::Error(e.into()))?; let result = any.try_into().map_err(nom::Err::Error)?; Ok((i, result)) }, bytes, ) } } /// Verification of DER constraints pub trait CheckDerConstraints { fn check_constraints(any: &Any) -> Result<()>; } /// Common trait for all objects that can be encoded using the DER representation /// /// # Examples /// /// Objects from this crate can be encoded as DER: /// /// ``` /// use asn1_rs::{Integer, ToDer}; /// /// let int = Integer::from(4u32); /// let mut writer = Vec::new(); /// let sz = int.write_der(&mut writer).expect("serialization failed"); /// /// assert_eq!(&writer, &[0x02, 0x01, 0x04]); /// # assert_eq!(sz, 3); /// ``` /// /// Many of the primitive types can also directly be encoded as DER: /// /// ``` /// use asn1_rs::ToDer; /// /// let mut writer = Vec::new(); /// let sz = 4.write_der(&mut writer).expect("serialization failed"); /// /// assert_eq!(&writer, &[0x02, 0x01, 0x04]); /// # assert_eq!(sz, 3); /// ``` #[cfg(feature = "std")] pub trait ToDer where Self: DynTagged, { /// Get the length of the object (including the header), when encoded /// // Since we are using DER, length cannot be Indefinite, so we can use `usize`. // XXX can this function fail? fn to_der_len(&self) -> Result; /// Write the DER encoded representation to a newly allocated `Vec`. fn to_der_vec(&self) -> SerializeResult> { let mut v = Vec::new(); let _ = self.write_der(&mut v)?; Ok(v) } /// Similar to using `to_vec`, but uses provided values without changes. /// This can generate an invalid encoding for a DER object. fn to_der_vec_raw(&self) -> SerializeResult> { let mut v = Vec::new(); let _ = self.write_der_raw(&mut v)?; Ok(v) } /// Attempt to write the DER encoded representation (header and content) into this writer. /// /// # Examples /// /// ``` /// use asn1_rs::{Integer, ToDer}; /// /// let int = Integer::from(4u32); /// let mut writer = Vec::new(); /// let sz = int.write_der(&mut writer).expect("serialization failed"); /// /// assert_eq!(&writer, &[0x02, 0x01, 0x04]); /// # assert_eq!(sz, 3); /// ``` fn write_der(&self, writer: &mut dyn Write) -> SerializeResult { let sz = self.write_der_header(writer)?; let sz = sz + self.write_der_content(writer)?; Ok(sz) } /// Attempt to write the DER header to this writer. fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult; /// Attempt to write the DER content (all except header) to this writer. fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult; /// Similar to using `to_der`, but uses provided values without changes. /// This can generate an invalid encoding for a DER object. fn write_der_raw(&self, writer: &mut dyn Write) -> SerializeResult { self.write_der(writer) } } #[cfg(feature = "std")] impl<'a, T> ToDer for &'a T where T: ToDer, &'a T: DynTagged, { fn to_der_len(&self) -> Result { (*self).to_der_len() } fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult { (*self).write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult { (*self).write_der_content(writer) } } /// Helper trait for creating tagged EXPLICIT values /// /// # Examples /// /// ``` /// use asn1_rs::{AsTaggedExplicit, Class, Error, TaggedParser}; /// /// // create a `[1] EXPLICIT INTEGER` value /// let tagged: TaggedParser<_, _, Error> = 4u32.explicit(Class::ContextSpecific, 1); /// ``` pub trait AsTaggedExplicit<'a, E = Error>: Sized { fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E> { TaggedParser::new_explicit(class, tag, self) } } impl<'a, T, E> AsTaggedExplicit<'a, E> for T where T: Sized + 'a {} /// Helper trait for creating tagged IMPLICIT values /// /// # Examples /// /// ``` /// use asn1_rs::{AsTaggedImplicit, Class, Error, TaggedParser}; /// /// // create a `[1] IMPLICIT INTEGER` value, not constructed /// let tagged: TaggedParser<_, _, Error> = 4u32.implicit(Class::ContextSpecific, false, 1); /// ``` pub trait AsTaggedImplicit<'a, E = Error>: Sized { fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E> { TaggedParser::new_implicit(class, constructed, tag, self) } } impl<'a, T, E> AsTaggedImplicit<'a, E> for T where T: Sized + 'a {} pub use crate::tostatic::*; rusticata-asn1-rs-07e764c/tests/000077500000000000000000000000001476576401200165125ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/tests/ber.rs000066400000000000000000000441201476576401200176310ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; use nom::Needed; #[cfg(feature = "datetime")] use time::macros::datetime; #[test] fn from_ber_any() { let input = &hex!("02 01 02 ff ff"); let (rem, result) = Any::from_ber(input).expect("parsing failed"); // dbg!(&result); assert_eq!(rem, &[0xff, 0xff]); assert_eq!(result.header.tag(), Tag::Integer); } #[test] fn from_ber_bitstring() { // // correct DER encoding // let input = &hex!("03 04 06 6e 5d c0"); let (rem, result) = BitString::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.unused_bits, 6); assert_eq!(&result.data[..], &input[3..]); // // correct encoding, but wrong padding bits (not all set to 0) // let input = &hex!("03 04 06 6e 5d e0"); let (rem, result) = BitString::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.unused_bits, 6); assert_eq!(&result.data[..], &input[3..]); // // long form of length (invalid, < 127) // let input = &hex!("03 81 04 06 6e 5d c0"); let (rem, result) = BitString::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.unused_bits, 6); assert_eq!(&result.data[..], &input[4..]); } #[test] fn from_ber_embedded_pdv() { let input = &hex!("2b 0d a0 07 81 05 2a 03 04 05 06 82 02 aa a0"); let (rem, result) = EmbeddedPdv::from_ber(input).expect("parsing failed"); assert_eq!(rem, &[]); assert_eq!( result.identification, PdvIdentification::Syntax(Oid::from(&[1, 2, 3, 4, 5, 6]).unwrap()) ); assert_eq!(result.data_value, &[0xaa, 0xa0]); } #[test] fn embedded_pdv_variants() { // identification: syntaxes let input = &hex!("2b 11 a0 0c a0 0a 80 02 2a 03 81 04 2a 03 04 05 82 01 00"); let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); assert!(rem.is_empty()); assert!(matches!( res.identification, PdvIdentification::Syntaxes { .. } )); // identification: syntax let input = &hex!("2b 09 a0 04 81 02 2a 03 82 01 00"); let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); assert!(rem.is_empty()); assert!(matches!(res.identification, PdvIdentification::Syntax(_))); // identification: presentation-context-id let input = &hex!("2b 08 a0 03 82 01 02 82 01 00"); let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); assert!(rem.is_empty()); assert!(matches!( res.identification, PdvIdentification::PresentationContextId(_) )); // identification: context-negotiation let input = &hex!("2b 10 a0 0b a3 09 80 01 2a 81 04 2a 03 04 05 82 01 00"); let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); assert!(rem.is_empty()); assert!(matches!( res.identification, PdvIdentification::ContextNegotiation { .. } )); // identification: transfer-syntax let input = &hex!("2b 0b a0 06 84 04 2a 03 04 05 82 01 00"); let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); assert!(rem.is_empty()); assert!(matches!( res.identification, PdvIdentification::TransferSyntax(_) )); // identification: fixed let input = &hex!("2b 07 a0 02 85 00 82 01 00"); let (rem, res) = EmbeddedPdv::from_ber(input).expect("parsing EMBEDDED PDV failed"); assert!(rem.is_empty()); assert!(matches!(res.identification, PdvIdentification::Fixed)); // identification: invalid let input = &hex!("2b 07 a0 02 86 00 82 01 00"); let e = EmbeddedPdv::from_ber(input).expect_err("parsing should fail"); assert!(matches!(e, Err::Error(Error::InvalidValue { .. }))); } #[test] fn from_ber_endofcontent() { let input = &hex!("00 00"); let (rem, _result) = EndOfContent::from_ber(input).expect("parsing failed"); assert_eq!(rem, &[]); } #[test] fn from_ber_generalizedtime() { let input = &hex!("18 0F 32 30 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); assert_eq!(rem, &[0xff]); #[cfg(feature = "datetime")] { let datetime = datetime! {2002-12-13 14:29:23 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result; // local time with fractional seconds let input = b"\x18\x1019851106210627.3"; let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.0.millisecond, Some(300)); assert_eq!(result.0.tz, ASN1TimeZone::Undefined); #[cfg(feature = "datetime")] { let datetime = datetime! {1985-11-06 21:06:27.300_000_000 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result; // coordinated universal time with fractional seconds let input = b"\x18\x1119851106210627.3Z"; let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.0.millisecond, Some(300)); assert_eq!(result.0.tz, ASN1TimeZone::Z); #[cfg(feature = "datetime")] { let datetime = datetime! {1985-11-06 21:06:27.300_000_000 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result; // coordinated universal time with fractional seconds let input = b"\x18\x1219851106210627.03Z"; let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.0.millisecond, Some(30)); assert_eq!(result.0.tz, ASN1TimeZone::Z); #[cfg(feature = "datetime")] { let datetime = datetime! {1985-11-06 21:06:27.03 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result; // local time with fractional seconds, and with local time 5 hours retarded in relation to coordinated universal time. let input = b"\x18\x1519851106210627.3-0500"; let (rem, result) = GeneralizedTime::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.0.millisecond, Some(300)); assert_eq!(result.0.tz, ASN1TimeZone::Offset(-5, 0)); #[cfg(feature = "datetime")] { let datetime = datetime! {1985-11-06 21:06:27.300_000_000 -05:00}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result; } #[test] fn from_ber_int() { let input = &hex!("02 01 02 ff ff"); let (rem, result) = u8::from_ber(input).expect("parsing failed"); assert_eq!(result, 2); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_ber_relative_oid() { let input = &hex!("0d 04 c2 7b 03 02"); let (rem, result) = Oid::from_ber_relative(input).expect("parsing failed"); assert_eq!(result, Oid::from_relative(&[8571, 3, 2]).unwrap()); assert_eq!(rem, &[]); } #[test] fn from_ber_length_incomplete() { let input = &hex!("30"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Incomplete(Needed::new(1))); let input = &hex!("02"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Incomplete(Needed::new(1))); let input = &hex!("02 05"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Incomplete(Needed::new(5))); let input = &hex!("02 85"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Incomplete(Needed::new(5))); let input = &hex!("02 85 ff"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Incomplete(Needed::new(4))); } #[test] fn from_ber_length_invalid() { let input = &hex!("02 ff 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Error(Error::InvalidLength)); let input = &hex!("02 85 ff ff ff ff ff 00"); let res = u8::from_ber(input).expect_err("parsing should have failed"); assert!(res.is_incomplete()); } #[test] fn from_ber_octetstring() { let input = &hex!("04 05 41 41 41 41 41"); let (rem, result) = OctetString::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), b"AAAAA"); assert_eq!(rem, &[]); } #[test] fn from_ber_real_binary() { const EPSILON: f32 = 0.00001; // binary, base = 2 let input = &hex!("09 03 80 ff 01 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::binary(1.0, 2, -1)); assert!((result.f32() - 0.5).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); // binary, base = 2 and scale factor let input = &hex!("09 03 94 ff 0d ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::binary(26.0, 2, -3).with_enc_base(8)); assert!((result.f32() - 3.25).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); // binary, base = 16 let input = &hex!("09 03 a0 fe 01 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::binary(1.0, 2, -8).with_enc_base(16)); assert!((result.f32() - 0.00390625).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); // binary, exponent = 0 let input = &hex!("09 03 80 00 01 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::binary(1.0, 2, 0)); assert!((result.f32() - 1.0).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); // 2 octets for exponent and negative exponent let input = &hex!("09 04 a1 ff 01 03 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::binary(3.0, 2, -1020).with_enc_base(16)); let epsilon = 1e-311_f64; assert!((result.f64() - 2.67e-307).abs() < epsilon); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_ber_real_f32() { const EPSILON: f32 = 0.00001; // binary, base = 2 let input = &hex!("09 03 80 ff 01 ff ff"); let (rem, result) = ::from_ber(input).expect("parsing failed"); assert!((result - 0.5).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_ber_real_f64() { const EPSILON: f64 = 0.00001; // binary, base = 2 let input = &hex!("09 03 80 ff 01 ff ff"); let (rem, result) = ::from_ber(input).expect("parsing failed"); assert!((result - 0.5).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_ber_real_special() { // 0 let input = &hex!("09 00 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::from(0.0)); assert_eq!(rem, &[0xff, 0xff]); // infinity let input = &hex!("09 01 40 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::Infinity); assert_eq!(rem, &[0xff, 0xff]); // negative infinity let input = &hex!("09 01 41 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::NegInfinity); assert_eq!(rem, &[0xff, 0xff]); } #[test] #[allow(clippy::approx_constant)] fn from_ber_real_string() { // text representation, NR3 let input = &hex!("09 07 03 33 31 34 45 2D 32 ff ff"); let (rem, result) = Real::from_ber(input).expect("parsing failed"); assert_eq!(result, Real::from(3.14)); assert_eq!(rem, &[0xff, 0xff]); } #[test] #[allow(clippy::approx_constant)] fn from_ber_real_string_primitive() { // text representation, NR3 let input = &hex!("09 07 03 33 31 34 45 2D 32 ff ff"); let (rem, result) = f32::from_ber(input).expect("parsing failed"); assert!((result - 3.14).abs() < 0.01); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_ber_sequence() { let input = &hex!("30 05 02 03 01 00 01"); let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); // let (_, i) = Sequence::from_ber_and_then(input, Integer::from_ber).expect("parsing failed"); assert_eq!(i.as_u32(), Ok(0x10001)); } #[test] fn from_ber_sequence_vec() { let input = &hex!("30 05 02 03 01 00 01"); let (rem, result) = >::from_ber(input).expect("parsing failed"); assert_eq!(&result, &[65537]); assert_eq!(rem, &[]); } #[test] fn from_ber_sequence_of_vec() { let input = &hex!("30 05 02 03 01 00 01"); let (rem, result) = ::from_ber(input).expect("parsing failed"); let v = result .ber_sequence_of::() .expect("ber_sequence_of failed"); assert_eq!(rem, &[]); assert_eq!(&v, &[65537]); } #[test] fn from_ber_iter_sequence() { let input = &hex!("30 0a 02 03 01 00 01 02 03 01 00 01"); let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let v = result .ber_iter() .collect::>>() .expect("could not iterate sequence"); assert_eq!(&v, &[65537, 65537]); } #[test] fn from_ber_set() { let input = &hex!("31 05 02 03 01 00 01"); let (rem, result) = Set::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); // let (_, i) = Set::from_ber_and_then(input, Integer::from_ber).expect("parsing failed"); assert_eq!(i.as_u32(), Ok(0x10001)); } #[test] fn from_ber_set_of_vec() { let input = &hex!("31 05 02 03 01 00 01"); let (rem, result) = ::from_ber(input).expect("parsing failed"); let v = result.ber_set_of::().expect("ber_set_of failed"); assert_eq!(rem, &[]); assert_eq!(&v, &[65537]); } #[test] fn from_ber_iter_set() { let input = &hex!("31 0a 02 03 01 00 01 02 03 01 00 01"); let (rem, result) = Set::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let v = result .ber_iter() .collect::>>() .expect("could not iterate set"); assert_eq!(&v, &[65537, 65537]); } #[test] fn from_ber_tag_custom() { // canonical tag encoding let input = &hex!("8f 02 12 34"); let (rem, any) = Any::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(any.tag(), Tag(15)); // non-canonical tag encoding let input = &hex!("9f 0f 02 12 34"); let (rem, any) = Any::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(any.tag(), Tag(15)); assert_eq!(any.header.raw_tag(), Some(&[0x9f, 0x0f][..])); } #[test] fn from_ber_tag_incomplete() { let input = &hex!("9f a2 a2"); let res = Any::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Error(Error::InvalidTag)); } #[test] fn from_ber_tag_overflow() { let input = &hex!("9f a2 a2 a2 a2 a2 a2 22 01 00"); let res = Any::from_ber(input).expect_err("parsing should have failed"); assert_eq!(res, nom::Err::Error(Error::InvalidTag)); } #[test] fn from_ber_tag_long() { let input = &hex!("9f a2 22 01 00"); let (rem, any) = Any::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(any.tag(), Tag(0x1122)); assert_eq!(any.header.raw_tag(), Some(&[0x9f, 0xa2, 0x22][..])); } #[test] fn from_ber_iter_sequence_incomplete() { let input = &hex!("30 09 02 03 01 00 01 02 03 01 00"); let (rem, result) = Sequence::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let mut iter = result.ber_iter::(); assert_eq!(iter.next(), Some(Ok(65537))); assert_eq!(iter.next(), Some(Err(Error::Incomplete(Needed::new(1))))); assert_eq!(iter.next(), None); } #[test] fn from_ber_set_of() { let input = &hex!("31 05 02 03 01 00 01"); let (rem, result) = SetOf::::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), &[0x10001]); assert_eq!(rem, &[]); // not constructed let input = &hex!("11 05 02 03 01 00 01"); let err = SetOf::::from_ber(input).expect_err("should have failed"); assert_eq!(err, Err::Error(Error::ConstructExpected)); } #[test] fn from_ber_tagged_explicit_optional() { let input = &hex!("a0 03 02 01 02"); let (rem, result) = Option::>::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(result.is_some()); let tagged = result.unwrap(); assert_eq!(tagged.tag(), Tag(0)); assert_eq!(tagged.as_ref(), &2); let (rem, result) = Option::>::from_ber(input).expect("parsing failed"); assert!(result.is_none()); assert_eq!(rem, input); // using OptTaggedExplicit let (rem, result) = OptTaggedExplicit::::from_ber(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(result.is_some()); let tagged = result.unwrap(); assert_eq!(tagged.tag(), Tag(0)); assert_eq!(tagged.as_ref(), &2); // using OptTaggedParser let (rem, result) = OptTaggedParser::from(0) .parse_ber(input, |_, data| Integer::from_ber(data)) .expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result, Some(Integer::from(2))); } /// Generic tests on methods, and coverage tests #[test] fn from_ber_tagged_optional_cov() { let p = |input| OptTaggedParser::from(1).parse_ber::<_, Error, _>(input, |_, data| Ok((data, ()))); // empty input let input = &[]; let (_, r) = p(input).expect("parsing failed"); assert!(r.is_none()); // wrong tag let input = &hex!("a0 03 02 01 02"); let (_, r) = p(input).expect("parsing failed"); assert!(r.is_none()); // wrong class let input = &hex!("e1 03 02 01 02"); let r = p(input); assert!(r.is_err()); } #[test] fn from_ber_universalstring() { let input = &hex!("1C 10 00000061 00000062 00000063 00000064"); let (rem, result) = UniversalString::from_ber(input).expect("parsing failed"); assert_eq!(result.as_ref(), "abcd"); assert_eq!(rem, &[]); } rusticata-asn1-rs-07e764c/tests/compile-fail/000077500000000000000000000000001476576401200210535ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/tests/compile-fail/der_sequence_tag_errors.rs000066400000000000000000000010141476576401200263060ustar00rootroot00000000000000fn test_tag_errors() { use asn1_rs::*; /// Should not compile: both implicit and explicit tags #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[tag_explicit(0)] #[tag_implicit(0)] a: u16, } /// Should not compile: multiple explicit tags #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T1 { #[tag_explicit(0)] #[tag_explicit(1)] a: u16, } } fn main() { test_tag_errors(); } rusticata-asn1-rs-07e764c/tests/compile-fail/der_sequence_tag_errors.stderr000066400000000000000000000007441476576401200271760ustar00rootroot00000000000000error: proc-macro derive panicked --> tests/compile-fail/der_sequence_tag_errors.rs:5:32 | 5 | #[derive(Debug, PartialEq, DerSequence)] | ^^^^^^^^^^^ | = help: message: tag cannot be set twice! error: proc-macro derive panicked --> tests/compile-fail/der_sequence_tag_errors.rs:14:32 | 14 | #[derive(Debug, PartialEq, DerSequence)] | ^^^^^^^^^^^ | = help: message: tag cannot be set twice! rusticata-asn1-rs-07e764c/tests/compile_tests.rs000066400000000000000000000002301476576401200217250ustar00rootroot00000000000000#[test] fn compile_fail() { let t = trybuild::TestCases::new(); t.pass("tests/run-pass/*.rs"); t.compile_fail("tests/compile-fail/*.rs"); } rusticata-asn1-rs-07e764c/tests/cov.rs000066400000000000000000000054511476576401200176540ustar00rootroot00000000000000#![cfg(feature = "std")] //! Generic and coverage tests use asn1_rs::*; use std::io; #[test] fn new_embedded_pdv() { fn create_pdv(identification: PdvIdentification) -> EmbeddedPdv { let pdv = EmbeddedPdv { identification, data_value_descriptor: None, data_value: &[0x00, 0xff], }; assert!(pdv.data_value_descriptor.is_none()); assert_eq!(pdv.data_value.len(), 2); pdv } let identification = PdvIdentification::ContextNegotiation { presentation_context_id: Integer::from(42_u8), presentation_syntax: oid! { 1.2.3.4.5 }, }; let pdv1 = create_pdv(identification); let identification = PdvIdentification::Syntaxes { s_abstract: oid! { 1.2.3 }, s_transfer: oid! { 1.2.3.4.5 }, }; let pdv2 = create_pdv(identification); assert!(pdv1 != pdv2); let identification = PdvIdentification::Syntaxes { s_abstract: oid! { 1.2.3 }, s_transfer: oid! { 1.2.3.4.5 }, }; let pdv3 = create_pdv(identification); assert!(pdv3 == pdv2); } #[test] fn methods_error() { let e = Error::invalid_value(Tag(0), "msg".to_string()); assert_eq!( e, Error::InvalidValue { tag: Tag(0), msg: "msg".to_string(), } ); // let e = Error::unexpected_tag(None, Tag(0)); assert_eq!( e, Error::UnexpectedTag { expected: None, actual: Tag(0), } ); // let e = Error::unexpected_class(None, Class::Application); assert_eq!( e, Error::UnexpectedClass { expected: None, actual: Class::Application } ); // use nom::error::ParseError; let e = Error::from_error_kind(&[], nom::error::ErrorKind::Fail); let e = >::append(&[], nom::error::ErrorKind::Eof, e); let s = format!("{}", e); assert!(s.starts_with("nom error:")); // let e1 = Error::from(nom::Err::Error(Error::BerTypeError)); let e2 = Error::from(nom::Err::Incomplete(nom::Needed::new(2))); assert!(e1 != e2); // let e = SerializeError::from(Error::BerTypeError); let s = format!("{}", e); assert!(s.starts_with("ASN.1 error:")); // let e = SerializeError::InvalidClass { class: 4 }; let s = format!("{}", e); assert!(s.starts_with("Invalid Class")); // let e = SerializeError::from(io::Error::new(io::ErrorKind::Other, "msg")); let s = format!("{}", e); assert!(s.starts_with("I/O error:")); } #[test] fn methods_tag() { let t = Tag::from(2); assert_eq!(t, Tag::Integer); // let err = t.invalid_value("test"); if let Error::InvalidValue { tag, .. } = err { assert_eq!(tag, Tag::Integer); } else { unreachable!(); } } rusticata-asn1-rs-07e764c/tests/der.rs000066400000000000000000000524661476576401200176470ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; use nom::sequence::pair; use nom::Needed; use std::collections::BTreeSet; use std::convert::TryInto; #[test] fn from_der_any() { let input = &hex!("02 01 02 ff ff"); let (rem, result) = Any::from_der(input).expect("parsing failed"); // dbg!(&result); assert_eq!(rem, &[0xff, 0xff]); assert_eq!(result.header.tag(), Tag::Integer); } #[test] fn from_der_any_into() { let input = &hex!("02 01 02 ff ff"); let (rem, result) = Any::from_der(input).expect("parsing failed"); assert_eq!(rem, &[0xff, 0xff]); assert_eq!(result.header.tag(), Tag::Integer); let i: u32 = result.try_into().unwrap(); assert_eq!(i, 2); // let (_, result) = Any::from_der(input).expect("parsing failed"); let i = result.u32().unwrap(); assert_eq!(i, 2); } #[test] fn from_der_bitstring() { // // correct DER encoding // let input = &hex!("03 04 06 6e 5d c0"); let (rem, result) = BitString::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.unused_bits, 6); assert_eq!(&result.data[..], &input[3..]); // // correct encoding, but wrong padding bits (not all set to 0) // let input = &hex!("03 04 06 6e 5d e0"); let res = BitString::from_der(input); assert_eq!( res, Err(Err::Error(Error::DerConstraintFailed( DerConstraint::UnusedBitsNotZero ))) ); // // long form of length (invalid, < 127) // // let input = &hex!("03 81 04 06 6e 5d c0"); // let res = BitString::from_der(input); // assert_eq!(res, Err(Err::Error(Error::DerConstraintFailed))); } #[test] fn from_der_bitstring_constructed() { let bytes: &[u8] = &hex!("23 81 0c 03 03 00 0a 3b 03 05 04 5f 29 1c d0"); assert_eq!( BitString::from_der(bytes), Err(Err::Error(Error::ConstructUnexpected)) ); } #[test] fn from_der_bmpstring() { // taken from https://docs.microsoft.com/en-us/windows/win32/seccertenroll/about-bmpstring let input = &hex!("1e 08 00 55 00 73 00 65 00 72"); let (rem, result) = BmpString::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), "User"); assert_eq!(rem, &[]); } #[test] fn from_der_bool() { let input = &hex!("01 01 00"); let (rem, result) = Boolean::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result, Boolean::FALSE); // let input = &hex!("01 01 ff"); let (rem, result) = Boolean::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result, Boolean::TRUE); assert!(result.bool()); // let input = &hex!("01 01 7f"); let res = Boolean::from_der(input); assert_eq!( res, Err(Err::Error(Error::DerConstraintFailed( DerConstraint::InvalidBoolean ))) ); // bool type let input = &hex!("01 01 00"); let (rem, result) = ::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(!result); } #[test] fn from_der_embedded_pdv() { let input = &hex!("2b 0d a0 07 81 05 2a 03 04 05 06 82 02 aa a0"); let (rem, result) = EmbeddedPdv::from_der(input).expect("parsing failed"); assert_eq!(rem, &[]); assert_eq!( result.identification, PdvIdentification::Syntax(Oid::from(&[1, 2, 3, 4, 5, 6]).unwrap()) ); assert_eq!(result.data_value, &[0xaa, 0xa0]); } #[test] fn from_der_enumerated() { let input = &hex!("0a 01 02"); let (rem, result) = Enumerated::from_der(input).expect("parsing failed"); assert_eq!(rem, &[]); assert_eq!(result.0, 2); } #[test] fn from_der_generalizedtime() { let input = &hex!("18 0F 32 30 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); let (rem, result) = GeneralizedTime::from_der(input).expect("parsing failed"); assert_eq!(rem, &[0xff]); #[cfg(feature = "datetime")] { use time::macros::datetime; let datetime = datetime! {2002-12-13 14:29:23 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result; // local time with fractional seconds (should fail: no 'Z' at end) let input = b"\x18\x1019851106210627.3"; let result = GeneralizedTime::from_der(input).expect_err("should not parse"); assert_eq!( result, nom::Err::Error(Error::DerConstraintFailed(DerConstraint::MissingTimeZone)) ); // coordinated universal time with fractional seconds let input = b"\x18\x1119851106210627.3Z"; let (rem, result) = GeneralizedTime::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.0.millisecond, Some(300)); assert_eq!(result.0.tz, ASN1TimeZone::Z); #[cfg(feature = "datetime")] { use time::macros::datetime; let datetime = datetime! {1985-11-06 21:06:27.3 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result.to_string(); // local time with fractional seconds, and with local time 5 hours retarded in relation to coordinated universal time. // (should fail: no 'Z' at end) let input = b"\x18\x1519851106210627.3-0500"; let result = GeneralizedTime::from_der(input).expect_err("should not parse"); assert_eq!( result, nom::Err::Error(Error::DerConstraintFailed(DerConstraint::MissingTimeZone)) ); } #[test] fn from_der_indefinite_length() { let bytes: &[u8] = &hex!("23 80 03 03 00 0a 3b 03 05 04 5f 29 1c d0 00 00"); assert_eq!( BitString::from_der(bytes), Err(Err::Error(Error::DerConstraintFailed( DerConstraint::IndefiniteLength ))) ); let bytes: &[u8] = &hex!("02 80 01 00 00"); assert!(Integer::from_der(bytes).is_err()); } #[test] fn from_der_int() { let input = &hex!("02 01 02 ff ff"); let (rem, result) = u8::from_der(input).expect("parsing failed"); assert_eq!(result, 2); assert_eq!(rem, &[0xff, 0xff]); // attempt to parse a value too large for container type let input = &hex!("02 03 00 ff ff"); let err = u8::from_der(input).expect_err("parsing should fail"); assert_eq!(err, Err::Error(Error::IntegerTooLarge)); // attempt to parse a value too large (positive large value in signed integer) let input = &hex!("02 03 00 ff ff"); let err = i16::from_der(input).expect_err("parsing should fail"); assert_eq!(err, Err::Error(Error::IntegerTooLarge)); } #[test] fn from_der_null() { let input = &hex!("05 00 ff ff"); let (rem, result) = Null::from_der(input).expect("parsing failed"); assert_eq!(result, Null {}); assert_eq!(rem, &[0xff, 0xff]); // unit let (rem, _unit) = <()>::from_der(input).expect("parsing failed"); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_der_numericstring() { // let input = &hex!("12 03 31 32 33"); let (rem, result) = NumericString::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), "123"); assert_eq!(rem, &[]); // wrong charset let input = &hex!("12 03 41 42 43"); let _ = NumericString::from_der(input).expect_err("parsing should fail"); } #[test] fn from_der_octetstring() { // coverage use std::borrow::Cow; let s = OctetString::new(b"1234"); assert_eq!(s.as_cow().len(), 4); assert_eq!(s.into_cow(), Cow::Borrowed(b"1234")); // let input = &hex!("04 05 41 41 41 41 41"); let (rem, result) = OctetString::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), b"AAAAA"); assert_eq!(rem, &[]); // let (rem, result) = <&[u8]>::from_der(input).expect("parsing failed"); assert_eq!(result, b"AAAAA"); assert_eq!(rem, &[]); } #[test] fn from_der_octetstring_as_slice() { let input = &hex!("04 05 41 41 41 41 41"); let (rem, result) = <&[u8]>::from_der(input).expect("parsing failed"); assert_eq!(result, b"AAAAA"); assert_eq!(rem, &[]); } #[test] fn from_der_oid() { let input = &hex!("06 09 2a 86 48 86 f7 0d 01 01 05"); let (rem, result) = Oid::from_der(input).expect("parsing failed"); let expected = Oid::from(&[1, 2, 840, 113_549, 1, 1, 5]).unwrap(); assert_eq!(result, expected); assert_eq!(rem, &[]); } #[test] fn from_der_optional() { let input = &hex!("30 0a 0a 03 00 00 01 02 03 01 00 01"); let (rem, result) = Sequence::from_der_and_then(input, |input| { let (i, obj0) = >::from_der(input)?; let (i, obj1) = u32::from_der(i)?; Ok((i, (obj0, obj1))) }) .expect("parsing failed"); let expected = (Some(Enumerated::new(1)), 65537); assert_eq!(result, expected); assert_eq!(rem, &[]); } #[test] fn from_der_real_f32() { const EPSILON: f32 = 0.00001; // binary, base = 2 let input = &hex!("09 03 80 ff 01 ff ff"); let (rem, result) = ::from_der(input).expect("parsing failed"); assert!((result - 0.5).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_der_real_f64() { const EPSILON: f64 = 0.00001; // binary, base = 2 let input = &hex!("09 03 80 ff 01 ff ff"); let (rem, result) = ::from_der(input).expect("parsing failed"); assert!((result - 0.5).abs() < EPSILON); assert_eq!(rem, &[0xff, 0xff]); } #[test] fn from_der_relative_oid() { let input = &hex!("0d 04 c2 7b 03 02"); let (rem, result) = Oid::from_der_relative(input).expect("parsing failed"); let expected = Oid::from_relative(&[8571, 3, 2]).unwrap(); assert_eq!(result, expected); assert_eq!(rem, &[]); } #[test] fn from_der_sequence() { let input = &hex!("30 05 02 03 01 00 01"); let (rem, result) = Sequence::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); } #[test] fn from_der_sequence_vec() { let input = &hex!("30 05 02 03 01 00 01"); let (rem, result) = >::from_der(input).expect("parsing failed"); assert_eq!(&result, &[65537]); assert_eq!(rem, &[]); } #[test] fn from_der_iter_sequence_parse() { let input = &hex!("30 0a 02 03 01 00 01 02 03 01 00 01"); let (rem, result) = Sequence::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let (rem, v) = result .parse(pair(u32::from_der, u32::from_der)) .expect("parse sequence data"); assert_eq!(v, (65537, 65537)); assert!(rem.is_empty()); } #[test] fn from_der_iter_sequence() { let input = &hex!("30 0a 02 03 01 00 01 02 03 01 00 01"); let (rem, result) = Sequence::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let v = result .der_iter() .collect::>>() .expect("could not iterate sequence"); assert_eq!(&v, &[65537, 65537]); } #[test] fn from_der_iter_sequence_incomplete() { let input = &hex!("30 09 02 03 01 00 01 02 03 01 00"); let (rem, result) = Sequence::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let mut iter = result.der_iter::(); assert_eq!(iter.next(), Some(Ok(65537))); assert_eq!(iter.next(), Some(Err(Error::Incomplete(Needed::new(1))))); assert_eq!(iter.next(), None); } #[test] fn from_der_set() { let input = &hex!("31 05 02 03 01 00 01"); let (rem, result) = Set::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); // let (_, i) = Set::from_der_and_then(input, Integer::from_der).expect("parsing failed"); assert_eq!(i.as_u32(), Ok(0x10001)); } #[test] fn from_der_set_btreeset() { let input = &hex!("31 05 02 03 01 00 01"); let (rem, result) = >::from_der(input).expect("parsing failed"); assert!(result.contains(&65537)); assert_eq!(result.len(), 1); assert_eq!(rem, &[]); } #[test] fn from_der_set_of_vec() { let input = &hex!("31 05 02 03 01 00 01"); let (rem, result) = ::from_der(input).expect("parsing failed"); let v = result.der_set_of::().expect("ber_set_of failed"); assert_eq!(rem, &[]); assert_eq!(&v, &[65537]); } #[test] fn from_der_iter_set() { let input = &hex!("31 0a 02 03 01 00 01 02 03 01 00 01"); let (rem, result) = Set::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), &input[2..]); assert_eq!(rem, &[]); let v = result .der_iter() .collect::>>() .expect("could not iterate set"); assert_eq!(&v, &[65537, 65537]); } #[test] fn from_der_utctime() { let input = &hex!("17 0D 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); let (rem, result) = UtcTime::from_der(input).expect("parsing failed"); assert_eq!(rem, &[0xff]); #[cfg(feature = "datetime")] { use time::macros::datetime; let datetime = datetime! {2-12-13 14:29:23 UTC}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result.to_string(); // let input = &hex!("17 11 30 32 31 32 31 33 31 34 32 39 32 33 2b 30 33 30 30 FF"); let (rem, result) = UtcTime::from_der(input).expect("parsing failed"); assert_eq!(rem, &[0xff]); #[cfg(feature = "datetime")] { use time::macros::datetime; let datetime = datetime! {2-12-13 14:29:23 +03:00}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result.to_string(); // let input = &hex!("17 11 30 32 31 32 31 33 31 34 32 39 32 33 2d 30 33 30 30 FF"); let (rem, result) = UtcTime::from_der(input).expect("parsing failed"); assert_eq!(rem, &[0xff]); #[cfg(feature = "datetime")] { use time::macros::datetime; let datetime = datetime! {2-12-13 14:29:23 -03:00}; assert_eq!(result.utc_datetime(), Ok(datetime)); } let _ = result.to_string(); } #[cfg(feature = "datetime")] #[test] fn utctime_adjusted_datetime() { use time::macros::datetime; let input = &hex!("17 0D 30 32 31 32 31 33 31 34 32 39 32 33 5A FF"); let (_, result) = UtcTime::from_der(input).expect("parsing failed"); assert_eq!( result.utc_adjusted_datetime(), Ok(datetime! {2002-12-13 14:29:23 UTC}) ); let input = &hex!("17 0D 35 30 31 32 31 33 31 34 32 39 32 33 5A FF"); let (_, result) = UtcTime::from_der(input).expect("parsing failed"); assert_eq!( result.utc_adjusted_datetime(), Ok(datetime! {1950-12-13 14:29:23 UTC}) ); let _ = result.to_string(); } #[test] fn from_der_utf8string() { let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); let (rem, result) = Utf8String::from_der(input).expect("parsing failed"); assert_eq!(result.as_ref(), "Some-State"); assert_eq!(rem, &[]); } #[test] fn from_der_utf8string_as_str() { let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); let (rem, result) = <&str>::from_der(input).expect("parsing failed"); assert_eq!(result, "Some-State"); assert_eq!(rem, &[]); } #[test] fn from_der_utf8string_as_string() { let input = &hex!("0c 0a 53 6f 6d 65 2d 53 74 61 74 65"); let (rem, result) = String::from_der(input).expect("parsing failed"); assert_eq!(&result, "Some-State"); assert_eq!(rem, &[]); } #[test] fn from_der_opt_int() { let input = &hex!("02 01 02 ff ff"); let (rem, result) = >::from_der(input).expect("parsing failed"); assert_eq!(result, Some(2)); assert_eq!(rem, &[0xff, 0xff]); // non-fatal error let (rem, result) = >::from_der(input).expect("parsing failed"); assert!(result.is_none()); assert_eq!(rem, input); // fatal error (correct tag, but incomplete) let input = &hex!("02 03 02 01"); let res = >::from_der(input); assert_eq!(res, Err(nom::Err::Incomplete(Needed::new(1)))); } #[test] fn from_der_tagged_explicit() { let input = &hex!("a0 03 02 01 02"); let (rem, result) = TaggedExplicit::::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(0)); assert_eq!(result.as_ref(), &2); } #[test] fn from_der_tagged_explicit_with_class() { let input = &hex!("a0 03 02 01 02"); // Note: the strange notation (using braces) is required by the compiler to use // a constant instead of the numeric value. let (rem, result) = TaggedValue::::from_der(input) .expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(0)); assert_eq!(result.as_ref(), &2); } #[test] fn from_der_tagged_explicit_any_tag() { let input = &hex!("a0 03 02 01 02"); let (rem, result) = TaggedParser::::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(0)); assert_eq!(result.as_ref(), &2); } #[test] fn from_der_tagged_explicit_optional() { let input = &hex!("a0 03 02 01 02"); let (rem, result) = Option::>::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(result.is_some()); let tagged = result.unwrap(); assert_eq!(tagged.tag(), Tag(0)); assert_eq!(tagged.as_ref(), &2); let (rem, result) = Option::>::from_der(input).expect("parsing failed"); assert!(result.is_none()); assert_eq!(rem, input); // using OptTaggedExplicit let (rem, result) = OptTaggedExplicit::::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(result.is_some()); let tagged = result.unwrap(); assert_eq!(tagged.tag(), Tag(0)); assert_eq!(tagged.as_ref(), &2); // using OptTaggedParser let (rem, result) = OptTaggedParser::from(0) .parse_der(input, |_, data| Integer::from_der(data)) .expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result, Some(Integer::from(2))); } #[test] fn from_der_tagged_implicit() { let input = &hex!("81 04 70 61 73 73"); let (rem, result) = TaggedImplicit::<&str, Error, 1>::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(1)); assert_eq!(result.as_ref(), &"pass"); } #[test] fn from_der_tagged_implicit_with_class() { let input = &hex!("81 04 70 61 73 73"); // Note: the strange notation (using braces) is required by the compiler to use // a constant instead of the numeric value. let (rem, result) = TaggedValue::<&str, Error, Implicit, { Class::CONTEXT_SPECIFIC }, 1>::from_der(input) .expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(1)); assert_eq!(result.as_ref(), &"pass"); } #[test] fn from_der_tagged_implicit_any_tag() { let input = &hex!("81 04 70 61 73 73"); let (rem, result) = TaggedParser::::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(1)); assert_eq!(result.as_ref(), &"pass"); } #[test] fn from_der_tagged_implicit_optional() { let input = &hex!("81 04 70 61 73 73"); let (rem, result) = Option::>::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(result.is_some()); let tagged = result.unwrap(); assert_eq!(tagged.tag(), Tag(1)); assert_eq!(tagged.as_ref(), &"pass"); let (rem, result) = Option::>::from_der(input).expect("parsing failed"); assert!(result.is_none()); assert_eq!(rem, input); // using OptTaggedExplicit let (rem, result) = OptTaggedImplicit::<&str, Error, 1>::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert!(result.is_some()); let tagged = result.unwrap(); assert_eq!(tagged.tag(), Tag(1)); assert_eq!(tagged.as_ref(), &"pass"); } #[test] fn from_der_tagged_implicit_all() { let input = &hex!("81 04 70 61 73 73"); let (rem, result) = TaggedParser::::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(result.tag(), Tag(1)); assert_eq!(result.as_ref().as_ref(), "pass"); // try the API verifying class and tag let _ = TaggedParser::::parse_der(Class::ContextSpecific, Tag(1), input) .expect("parsing failed"); // test TagParser API let parser = TaggedParserBuilder::implicit() .with_class(Class::ContextSpecific) .with_tag(Tag(1)) .der_parser::(); let _ = parser(input).expect("parsing failed"); // try specifying the expected tag (correct tag) let _ = parse_der_tagged_implicit::<_, Ia5String, _>(1)(input).expect("parsing failed"); // try specifying the expected tag (incorrect tag) let _ = parse_der_tagged_implicit::<_, Ia5String, _>(2)(input) .expect_err("parsing should have failed"); } /// Generic tests on methods, and coverage tests #[test] fn from_der_tagged_optional_cov() { let p = |input| OptTaggedParser::from(1).parse_der::<_, Error, _>(input, |_, data| Ok((data, ()))); // empty input let input = &[]; let (_, r) = p(input).expect("parsing failed"); assert!(r.is_none()); // wrong tag let input = &hex!("a0 03 02 01 02"); let (_, r) = p(input).expect("parsing failed"); assert!(r.is_none()); // wrong class let input = &hex!("e1 03 02 01 02"); let r = p(input); assert!(r.is_err()); let p = OptTaggedParser::from(Tag(1)); let _ = format!("{:?}", p); } rusticata-asn1-rs-07e764c/tests/issue-18-constructed-bit-sequence.rs000066400000000000000000000012461476576401200253560ustar00rootroot00000000000000#![cfg(feature = "std")] use std::borrow::Cow; use asn1_rs::*; #[derive(DerSequence, Debug, PartialEq)] struct Person { name: String, age: u16, } #[test] fn issue_18_1() { // create a sequence from random data let seq = Sequence::new(Cow::Borrowed(&[2, 2, 18, 52, 2, 2, 86, 12])); // now serialize a [2] IMPLICIT Person type T2<'a> = TaggedValue, Error, Implicit, { Class::UNIVERSAL }, 2>; let tagged = T2::implicit(seq); let result = tagged.to_der_vec().expect("Could not serialize sequence"); let (_, header) = Header::from_der(&result).expect("could not parse serialized data"); assert!(header.is_constructed()); } rusticata-asn1-rs-07e764c/tests/issue-27-option-struct-derive.rs000066400000000000000000000023771476576401200245530ustar00rootroot00000000000000use asn1_rs::*; #[derive(DerSequence, Debug, PartialEq)] struct TestBool { a: u16, b: Option, c: u32, } #[test] fn issue_27_1() { let x = TestBool { a: 0x1234, b: None, c: 0x5678, }; let expected = &[48, 8, 2, 2, 18, 52, 2, 2, 86, 120]; let (_, val) = TestBool::from_der(expected).unwrap(); assert_eq!(val, x); } #[test] fn issue_27_2() { let x = TestBool { a: 0x1234, b: Some(true), c: 0x5678, }; let expected = &[48, 11, 2, 2, 18, 52, 1, 1, 255, 2, 2, 86, 120]; let (_, val) = TestBool::from_der(expected).unwrap(); assert_eq!(val, x); } #[derive(DerSequence, Debug, PartialEq)] struct TestInt { a: u16, b: Option, c: bool, } #[test] fn issue_27_3() { let x = TestInt { a: 0x1234, b: None, c: true, }; let expected = &[48, 7, 2, 2, 18, 52, 1, 1, 255]; let (_, val) = TestInt::from_der(expected).unwrap(); assert_eq!(val, x); } #[test] fn issue_27_4() { let x = TestInt { a: 0x1234, b: Some(0x5678), c: true, }; let expected = &[48, 11, 2, 2, 18, 52, 2, 2, 86, 120, 1, 1, 255]; let (_, val) = TestInt::from_der(expected).unwrap(); assert_eq!(val, x); } rusticata-asn1-rs-07e764c/tests/issue-43-encoding-large-tags.rs000066400000000000000000000007601476576401200242470ustar00rootroot00000000000000#![cfg(feature = "std")] use asn1_rs::{Any, FromDer, Integer, Tag, ToDer}; #[test] fn encode_large_tag() { const EXPECTED_TAG: u32 = 0x41424344; let data = Integer::from(1).to_der_vec().unwrap(); let any = &Any::from_tag_and_data(Tag::from(EXPECTED_TAG), &data); let tmp = any.to_der_vec().unwrap(); let expect = Tag::from(EXPECTED_TAG); let actual = Any::from_der(&tmp).unwrap().1.tag(); assert_eq!(expect, actual, "expected tag {expect}, found tag {actual}"); } rusticata-asn1-rs-07e764c/tests/krb5.rs000066400000000000000000000067471476576401200177410ustar00rootroot00000000000000#![cfg(feature = "std")] //! Test implementation for Kerberos v5 //! //! This is mostly used to verify that required types and functions are implemented, //! and that provided API is convenient. use asn1_rs::*; use hex_literal::hex; const PRINCIPAL_NAME: &[u8] = &hex!("30 81 11 a0 03 02 01 00 a1 0a 30 81 07 1b 05 4a 6f 6e 65 73"); /// PrincipalName ::= SEQUENCE { /// name-type [0] Int32, /// name-string [1] SEQUENCE OF KerberosString /// } #[derive(Debug, PartialEq, Eq)] pub struct PrincipalName { pub name_type: NameType, pub name_string: Vec, } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct NameType(pub i32); // KerberosString ::= GeneralString (IA5String) pub type KerberosString<'a> = GeneralString<'a>; pub type KerberosStringList<'a> = Vec>; impl Tagged for PrincipalName { const TAG: Tag = Tag::Sequence; } impl<'a> FromDer<'a> for PrincipalName { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { // XXX in the example above, PRINCIPAL_NAME does not respect DER constraints (length is using long form while < 127) let (rem, seq) = Sequence::from_ber(bytes)?; seq.and_then(|data| { let input = &data; let (i, t) = parse_der_tagged_explicit::<_, u32, _>(0)(input)?; let name_type = t.inner; let name_type = NameType(name_type as i32); let (_, t) = parse_der_tagged_explicit::<_, KerberosStringList, _>(1)(i)?; let name_string = t.inner.iter().map(|s| s.string()).collect(); Ok(( rem, PrincipalName { name_type, name_string, }, )) }) } } impl ToDer for PrincipalName { fn to_der_len(&self) -> Result { let sz = self.name_type.0.to_der_len()? + 2 /* tagged */; let sz = sz + self.name_string.to_der_len()? + 2 /* tagged */; Ok(sz) } fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult { let len = self.to_der_len()?; let header = Header::new(Class::Universal, true, Self::TAG, Length::Definite(len)); header.write_der_header(writer) } fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult { // build DER sequence content let sz1 = self .name_type .0 .explicit(Class::ContextSpecific, 0) .write_der(writer)?; let sz2 = self .name_string .iter() .map(|s| KerberosString::from(s.as_ref())) .collect::>() .explicit(Class::ContextSpecific, 1) .write_der(writer)?; Ok(sz1 + sz2) } } #[test] fn krb5_principalname() { let input = PRINCIPAL_NAME; let (rem, res) = PrincipalName::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); let expected = PrincipalName { name_type: NameType(0), name_string: vec!["Jones".to_string()], }; assert_eq!(res, expected); } #[test] fn to_der_krb5_principalname() { let principal = PrincipalName { name_type: NameType(0), name_string: vec!["Jones".to_string()], }; let v = PrincipalName::to_der_vec(&principal).expect("serialization failed"); std::fs::write("/tmp/out.bin", &v).unwrap(); let (_, principal2) = PrincipalName::from_der(&v).expect("parsing failed"); assert!(principal.eq(&principal2)); } rusticata-asn1-rs-07e764c/tests/run-pass/000077500000000000000000000000001476576401200202625ustar00rootroot00000000000000rusticata-asn1-rs-07e764c/tests/run-pass/custom_errors.rs000066400000000000000000000024171476576401200235420ustar00rootroot00000000000000use asn1_rs::{oid, Any, Error, FromDer, Oid, ParseResult, Sequence}; use hex_literal::hex; #[derive(Debug)] pub enum X509Error { NotYetImplemented, DerError(Error), } impl From for X509Error { fn from(e: Error) -> Self { X509Error::DerError(e) } } #[derive(Debug)] pub struct AttrValue<'a> { pub attr_type: Oid<'a>, pub attr_value: Any<'a>, } impl<'a> FromDer<'a, X509Error> for AttrValue<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, X509Error> { Sequence::from_der_and_then(bytes, |i| { let (i, attr_type) = Oid::from_der(i)?; let (i, attr_value) = Any::from_der(i)?; Ok(( i, Self { attr_type, attr_value, }, )) }) .map_err(nom::Err::convert) } } fn main() { let input = &hex!("30 14 06 03 55 04 0A 13 0D 4C 65 74 27 73 20 45 6E 63 72 79 70 74"); let (rem, v) = AttrValue::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(oid! {2.5.4.10}, v.attr_type); let s = v .attr_value .as_printablestring() .expect("could not extract printablestring"); assert_eq!("Let's Encrypt", s.as_ref()); } rusticata-asn1-rs-07e764c/tests/run-pass/der_alias.rs000066400000000000000000000004631476576401200225560ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, DerAlias)] pub struct T1<'a>(pub Any<'a>); fn main() { let input = &hex!("300b 020101 020102 3003020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1.0.tag(), Tag::Sequence); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_compound.rs000066400000000000000000000007541476576401200252040ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] pub struct T1 { a: u32, b: u16, c: T2, } #[derive(Debug, PartialEq, DerSequence)] pub struct T2 { a: u16, } fn main() { let input = &hex!("300b 020101 020102 3003020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!( t1, T1 { a: 1, b: 2, c: T2 { a: 3 } } ); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_default.rs000066400000000000000000000021521476576401200247760ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[optional] #[default(0)] a: u16, } #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T1 { #[tag_explicit(0)] #[optional] #[default(0)] a: u16, } fn main() { // optional value present let input1 = &hex!("3003 020103"); let (rem, t0) = T0::from_der(input1).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: 3 }); // optional value absent let input_empty = &hex!("3000"); let (rem, t1) = T0::from_der(input_empty).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T0 { a: 0 }); // optional value present let input1 = &hex!("3005 a003 020103"); let (rem, t0) = T1::from_der(input1).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T1 { a: 3 }); // optional value absent let input_empty = &hex!("3000"); let (rem, t1) = T1::from_der(input_empty).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: 0 }); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_lifetime.rs000066400000000000000000000011361476576401200251510ustar00rootroot00000000000000use asn1_rs::{DerSequence, FromDer}; use hex_literal::hex; // simple lifetime #[derive(Debug, PartialEq, DerSequence)] pub struct T1<'a> { a: u32, b: u16, c: u16, d: &'a str, } // lifetime with a constraint #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T2<'a> where 'a: 'static { a: u32, b: u16, c: u16, d: &'a str, } fn main() { let input = &hex!("300f0201 01020102 020103 0c0461626364"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: 1, b: 2, c: 3, d: "abcd" }); }rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_map_err.rs000066400000000000000000000042311476576401200247770ustar00rootroot00000000000000use asn1_rs::{oid, Any, DerSequence, Error, FromDer, Oid}; use hex_literal::hex; #[derive(Debug, PartialEq)] pub enum MyError { NotYetImplemented, } impl From for MyError { fn from(_: asn1_rs::Error) -> Self { MyError::NotYetImplemented } } // no custom error nor map_err // just for reference #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { pub a: u32, } // map_err without custom error // especially useful if subparser does not return an `Error` #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T1 { #[map_err(|_| Error::BerTypeError)] pub a: u32, } // custom error, no mapping (just using Into) #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] #[error(MyError)] pub struct T2 { pub a: u32, } // custom error and error mapping #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] #[error(MyError)] pub struct T3 { #[map_err(MyError::from)] pub a: u32, } // similar to T1: subparser returns an error of type MyError, // which is mapped to `Error` #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T4 { #[map_err(|_| Error::BerTypeError)] pub a: T2, } // check that if subparser returns MyError, and this struct also // does, then no annotation is required #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] #[error(MyError)] pub struct T5 { pub a: T3, } #[derive(Debug)] pub enum X509Error { NotYetImplemented, DerError(Error), } impl From for X509Error { fn from(e: Error) -> Self { X509Error::DerError(e) } } #[derive(Debug, DerSequence)] // #[debug_derive] #[error(X509Error)] pub struct AttrValue<'a> { pub attr_type: Oid<'a>, pub attr_value: Any<'a>, } fn main() { let input = &hex!("30 14 06 03 55 04 0A 13 0D 4C 65 74 27 73 20 45 6E 63 72 79 70 74"); let (rem, v) = AttrValue::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(oid! {2.5.4.10}, v.attr_type); let s = v .attr_value .as_printablestring() .expect("could not extract printablestring"); assert_eq!("Let's Encrypt", s.as_ref()); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_optional.rs000066400000000000000000000031771476576401200252070ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; // XXX limitation: // when OPTIONAL only (and not tagged), parser is eager: // if there are several values of the same type, the first one // goes to the OPTIONAL value (and will fail if the next value is not present and mandatory) #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { a: u16, #[optional] b: Option, } #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T1 { #[tag_explicit(0)] #[optional] a: Option, } fn main() { // optional value present let input0 = &hex!("3006 020103 020103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: 3, b: Some(3) }); // optional value absent let input1 = &hex!("3003 020103"); let (rem, t0) = T0::from_der(input1).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: 3, b: None }); // optional value present but wrong tag let input_wrong_tag = &hex!("3008 a103020103 020103"); T0::from_der(input_wrong_tag).expect_err("parsing should fail"); // optional value present but invalid length let input_wrong_len0 = &hex!("3008 a002020103 020103"); T0::from_der(input_wrong_len0).expect_err("parsing should fail"); let input_wrong_len1 = &hex!("3008 a003020403 020103"); T0::from_der(input_wrong_len1).expect_err("parsing should fail"); // test empty input let input_empty = &hex!("3000"); let (rem, t1) = T1::from_der(input_empty).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: None }); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_simple.rs000066400000000000000000000005231476576401200246430ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] pub struct T1 { a: u32, b: u16, c: u16, } fn main() { let input = &hex!("30090201 01020102 020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: 1, b: 2, c: 3 }); }rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_tagged.rs000066400000000000000000000032151476576401200246060ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; // tagged without parsing annotations #[derive(Debug, PartialEq, DerSequence)] pub struct T1 { a: u32, b: u16, c: TaggedExplicit, } #[derive(Debug, PartialEq, DerSequence)] pub struct T2 { a: u16, } // test with EXPLICIT Vec #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T3 { a: u32, b: u16, c: TaggedExplicit, Error, 0>, } // test with IMPLICIT Vec #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T4 { a: u32, b: u16, c: TaggedImplicit, Error, 0>, } // test with [0] IMPLICIT xx OPTIONAL #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T5 { a: u32, b: u16, // c: OptTaggedImplicit, Error, 0>, c0: OptTaggedImplicit, Error, 0>, // c0: Option, Error, Implicit, {Class::CONTEXT_SPECIFIC}, 0>>, c1: OptTaggedImplicit, Error, 1>, // c1: Option, Error, Implicit, {Class::CONTEXT_SPECIFIC}, 1>>, } fn main() { let input = &hex!("300d 020101 020102 a0053003020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); let c = TaggedValue::explicit(T2 { a: 3 }); assert_eq!(t1, T1 { a: 1, b: 2, c }); let input = &hex!("300d 020101 020102 a1053003020103"); let (rem, t5) = T5::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); let c1 = Some(TaggedValue::implicit(vec![T2 { a: 3 }])); assert_eq!( t5, T5 { a: 1, b: 2, c0: None, c1 } ); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_tagged_explicit.rs000066400000000000000000000062121476576401200265070ustar00rootroot00000000000000fn test_tag_explicit() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T6 { #[tag_explicit(0)] a: u16, } let input0 = &hex!("3005 a003020103"); let (rem, t6) = T6::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t6, T6 { a: 3 }); let input1 = &hex!("3005 a103020103"); T6::from_der(input1).expect_err("parsing tag 1 should fail"); } fn test_tag_explicit_optional() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[tag_explicit(0)] #[optional] a: Option, b: u16, } #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T1 { #[tag_explicit(0)] #[optional] a: Option, } // optional value present let input0 = &hex!("3008 a003020103 020103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: Some(3), b: 3 }); // optional value absent let input1 = &hex!("3003 020103"); let (rem, t0) = T0::from_der(input1).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: None, b: 3 }); // optional value present but wrong tag let input_wrong_tag = &hex!("3008 a103020103 020103"); T0::from_der(input_wrong_tag).expect_err("parsing should fail"); // optional value present but invalid length let input_wrong_len0 = &hex!("3008 a002020103 020103"); T0::from_der(input_wrong_len0).expect_err("parsing should fail"); let input_wrong_len1 = &hex!("3008 a003020403 020103"); T0::from_der(input_wrong_len1).expect_err("parsing should fail"); // test empty input let input_empty = &hex!("3000"); let (rem, t1) = T1::from_der(input_empty).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: None }); } fn test_tag_explicit_application() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T6 { #[tag_explicit(APPLICATION 0)] a: u16, } let input0 = &hex!("3005 6003020103"); let (rem, t6) = T6::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t6, T6 { a: 3 }); let input1 = &hex!("3005 6103020103"); T6::from_der(input1).expect_err("parsing tag 1 should fail"); } fn test_tag_explicit_private() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T6 { #[tag_explicit(PRIVATE 0)] a: u16, } let input0 = &hex!("3005 e003020103"); let (rem, t6) = T6::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t6, T6 { a: 3 }); let input1 = &hex!("3005 e103020103"); T6::from_der(input1).expect_err("parsing tag 1 should fail"); } fn main() { test_tag_explicit(); test_tag_explicit_optional(); test_tag_explicit_application(); test_tag_explicit_private(); } rusticata-asn1-rs-07e764c/tests/run-pass/der_sequence_tagged_implicit.rs000066400000000000000000000061421476576401200265020ustar00rootroot00000000000000fn test_tag_implicit() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[tag_implicit(0)] a: u16, } let input0 = &hex!("3003 800103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: 3 }); let input1 = &hex!("3003 810103"); T0::from_der(input1).expect_err("parsing tag 1 should fail"); } fn test_tag_implicit_optional() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[tag_implicit(0)] #[optional] a: Option, b: u16, } #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T1 { #[tag_implicit(0)] #[optional] a: Option, } // optional value present let input0 = &hex!("3006 800103 020103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: Some(3), b: 3 }); // optional value absent let input1 = &hex!("3003 020103"); let (rem, t0) = T0::from_der(input1).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: None, b: 3 }); // optional value present but wrong tag let input_wrong_tag = &hex!("3006 810103 020103"); T0::from_der(input_wrong_tag).expect_err("parsing should fail"); // optional value present but invalid length let input_wrong_len0 = &hex!("3006 800203 020103"); T0::from_der(input_wrong_len0).expect_err("parsing should fail"); let input_wrong_len1 = &hex!("3006 800403 020103"); T0::from_der(input_wrong_len1).expect_err("parsing should fail"); // test empty input let input_empty = &hex!("3000"); let (rem, t1) = T1::from_der(input_empty).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: None }); } fn test_tag_implicit_application() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[tag_implicit(APPLICATION 0)] a: u16, } let input0 = &hex!("3003 400103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: 3 }); let input1 = &hex!("3003 410103"); T0::from_der(input1).expect_err("parsing tag 1 should fail"); } fn test_tag_implicit_private() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence)] // #[debug_derive] pub struct T0 { #[tag_implicit(PRIVATE 0)] a: u16, } let input0 = &hex!("3003 c00103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: 3 }); let input1 = &hex!("3003 c10103"); T0::from_der(input1).expect_err("parsing tag 1 should fail"); } fn main() { test_tag_implicit(); test_tag_implicit_optional(); test_tag_implicit_application(); test_tag_implicit_private(); } rusticata-asn1-rs-07e764c/tests/run-pass/der_set_compound.rs000066400000000000000000000007421476576401200241640ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSet)] pub struct T1 { a: u32, b: u16, c: T2, } #[derive(Debug, PartialEq, DerSet)] pub struct T2 { a: u16, } fn main() { let input = &hex!("310b 020101 020102 3103020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!( t1, T1 { a: 1, b: 2, c: T2 { a: 3 } } ); } rusticata-asn1-rs-07e764c/tests/run-pass/der_set_simple.rs000066400000000000000000000005161476576401200236300ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSet)] pub struct T1 { a: u32, b: u16, c: u16, } fn main() { let input = &hex!("31090201 01020102 020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: 1, b: 2, c: 3 }); }rusticata-asn1-rs-07e764c/tests/run-pass/der_set_tagged_explicit.rs000066400000000000000000000061611476576401200254750ustar00rootroot00000000000000fn test_tag_explicit() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSet)] // #[debug_derive] pub struct T6 { #[tag_explicit(0)] a: u16, } let input0 = &hex!("3105 a003020103"); let (rem, t6) = T6::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t6, T6 { a: 3 }); let input1 = &hex!("3105 a103020103"); T6::from_der(input1).expect_err("parsing tag 1 should fail"); } fn test_tag_explicit_optional() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSet)] // #[debug_derive] pub struct T0 { #[tag_explicit(0)] #[optional] a: Option, b: u16, } #[derive(Debug, PartialEq, DerSet)] // #[debug_derive] pub struct T1 { #[tag_explicit(0)] #[optional] a: Option, } // optional value present let input0 = &hex!("3108 a003020103 020103"); let (rem, t0) = T0::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: Some(3), b: 3 }); // optional value absent let input1 = &hex!("3103 020103"); let (rem, t0) = T0::from_der(input1).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t0, T0 { a: None, b: 3 }); // optional value present but wrong tag let input_wrong_tag = &hex!("3108 a103020103 020103"); T0::from_der(input_wrong_tag).expect_err("parsing should fail"); // optional value present but invalid length let input_wrong_len0 = &hex!("3108 a002020103 020103"); T0::from_der(input_wrong_len0).expect_err("parsing should fail"); let input_wrong_len1 = &hex!("3108 a003020403 020103"); T0::from_der(input_wrong_len1).expect_err("parsing should fail"); // test empty input let input_empty = &hex!("3100"); let (rem, t1) = T1::from_der(input_empty).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: None }); } fn test_tag_explicit_application() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSet)] // #[debug_derive] pub struct T6 { #[tag_explicit(APPLICATION 0)] a: u16, } let input0 = &hex!("3105 6003020103"); let (rem, t6) = T6::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t6, T6 { a: 3 }); let input1 = &hex!("3105 6103020103"); T6::from_der(input1).expect_err("parsing tag 1 should fail"); } fn test_tag_explicit_private() { use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSet)] // #[debug_derive] pub struct T6 { #[tag_explicit(PRIVATE 0)] a: u16, } let input0 = &hex!("3105 e003020103"); let (rem, t6) = T6::from_der(input0).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t6, T6 { a: 3 }); let input1 = &hex!("3105 e103020103"); T6::from_der(input1).expect_err("parsing tag 1 should fail"); } fn main() { test_tag_explicit(); test_tag_explicit_optional(); test_tag_explicit_application(); test_tag_explicit_private(); } rusticata-asn1-rs-07e764c/tests/run-pass/der_structured.rs000066400000000000000000000015621476576401200236720ustar00rootroot00000000000000use asn1_rs::{DerSequence, FromDer}; use hex_literal::hex; #[derive(Clone, Debug, PartialEq, DerSequence)] pub struct T1 { a: u32, b: u16, c: u16, } // sequence composed of other objects #[derive(Debug, PartialEq, DerSequence)] pub struct T2 { a: u32, b: T1, } // sequence composed of a sequence of objects #[derive(Debug, PartialEq, DerSequence)] pub struct T3 { a: Vec, } fn main() { let input = &hex!("30090201 01020102 020103"); let mut v = vec![0x30]; let sz = 2 * input.len() as u8; v.push(sz + 2); v.extend_from_slice(&[0x30]); v.push(sz); v.extend_from_slice(input); v.extend_from_slice(input); let (rem, t3) = T3::from_der(&v).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t3.a.len(), 2); let t1 = T1 { a: 1, b: 2, c: 3 }; assert_eq!(t3, T3{ a: vec![t1.clone(), t1]}); } rusticata-asn1-rs-07e764c/tests/run-pass/toder_sequence_lifetime.rs000066400000000000000000000012621476576401200255140ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence, ToDerSequence)] //#[debug_derive] pub struct T1<'a> { a: u32, b: u16, c: u16, d: &'a str, } #[cfg(feature = "std")] fn main() { let input = &hex!("300f0201 01020102 020103 0c0461626364"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!( t1, T1 { a: 1, b: 2, c: 3, d: "abcd" } ); // serialize back data let output = t1.to_der_vec().expect("serialization failed"); assert_eq!(&input[..], output); } #[cfg(not(feature = "std"))] fn main() {} rusticata-asn1-rs-07e764c/tests/run-pass/toder_sequence_simple.rs000066400000000000000000000010701476576401200252040ustar00rootroot00000000000000use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence, ToDerSequence)] //#[debug_derive] pub struct T1 { a: u32, b: u16, c: u16, } #[cfg(feature = "std")] fn main() { let input = &hex!("30090201 01020102 020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: 1, b: 2, c: 3 }); // serialize back data let output = t1.to_der_vec().expect("serialization failed"); assert_eq!(&input[..], output); } #[cfg(not(feature = "std"))] fn main() {} rusticata-asn1-rs-07e764c/tests/to_der.rs000066400000000000000000000457371476576401200203540ustar00rootroot00000000000000#![cfg(feature = "std")] use asn1_rs::*; use hex_literal::hex; // use nom::HexDisplay; use std::collections::BTreeSet; use std::convert::{TryFrom, TryInto}; use std::iter::FromIterator; macro_rules! test_simple_string { ($t:ty, $s:expr) => { let t = <$t>::from($s); let v = t.to_der_vec().expect("serialization failed"); assert_eq!(v[0] as u32, t.tag().0); assert_eq!(v[1] as usize, t.as_ref().len()); assert_eq!(&v[2..], $s.as_bytes()); let (_, t2) = <$t>::from_der(&v).expect("decoding serialized object failed"); assert!(t.eq(&t2)); }; } macro_rules! test_string_invalid_charset { ($t:ty, $s:expr) => { <$t>::test_valid_charset($s.as_bytes()).expect_err("should reject charset"); }; } #[test] fn to_der_length() { // indefinite length let length = Length::Indefinite; let v = length.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x80]); // definite, short form let length = Length::Definite(3); let v = length.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x03]); // definite, long form let length = Length::Definite(250); let v = length.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x81, 0xfa]); } #[test] fn to_der_length_long() { let s = core::str::from_utf8(&[0x41; 256]).unwrap(); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(&v[..4], &[0x0c, 0x82, 0x01, 0x00]); assert_eq!(&v[4..], s.as_bytes()); } #[test] fn to_der_tag() { // short tag, UNIVERSAL let v = (Class::Universal, false, Tag(0x1a)) .to_der_vec() .expect("serialization failed"); assert_eq!(&v, &[0x1a]); // short tag, APPLICATION let v = (Class::Application, false, Tag(0x1a)) .to_der_vec() .expect("serialization failed"); assert_eq!(&v, &[0x1a | (0b01 << 6)]); // short tag, constructed let v = (Class::Universal, true, Tag(0x10)) .to_der_vec() .expect("serialization failed"); assert_eq!(&v, &[0x30]); // long tag, UNIVERSAL let v = (Class::Universal, false, Tag(0x1a1a)) .to_der_vec() .expect("serialization failed"); assert_eq!(&v, &[0b1_1111, 0xb4, 0x1a]); } #[test] fn to_der_header() { // simple header let header = Header::new_simple(Tag::Integer); let v = header.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x2, 0x0]); // indefinite length let header = Header::new(Class::Universal, false, Tag::Integer, Length::Indefinite); let v = header.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x2, 0x80]); } #[test] fn to_der_any() { let header = Header::new_simple(Tag::Integer); let any = Any::new(header, &hex!("02")); assert_eq!(any.to_der_len(), Ok(3)); let v = any.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x02, 0x01, 0x02]); } #[test] fn to_der_any_raw() { let header = Header::new(Class::Universal, false, Tag::Integer, Length::Definite(3)); let any = Any::new(header, &hex!("02")); // to_vec should compute the length let v = any.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x02, 0x01, 0x02]); // to_vec_raw will use the header as provided let v = any.to_der_vec_raw().expect("serialization failed"); assert_eq!(&v, &[0x02, 0x03, 0x02]); } #[test] fn to_der_bitstring() { let bitstring = BitString::new(6, &hex!("6e 5d c0")); let v = bitstring.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("03 04 06 6e 5d c0")); let (_, result) = BitString::from_der(&v).expect("parsing failed"); assert!(bitstring.eq(&result)); } #[test] fn to_der_bmpstring() { let bmpstring = BmpString::new("User"); assert_eq!(bmpstring.to_der_len(), Ok(10)); let v = bmpstring.to_der_vec().expect("serialization failed"); let expected = &hex!("1e 08 00 55 00 73 00 65 00 72"); assert_eq!(&v, expected); assert!(BmpString::test_valid_charset(&v[2..]).is_ok()); let (_, result) = BmpString::from_der(&v).expect("parsing failed"); assert!(bmpstring.eq(&result)); // for coverage let b1 = BmpString::from("s"); let s = b1.string(); let b2 = BmpString::from(s); assert_eq!(b1, b2); // long string let sz = 256; let s = str::repeat("a", sz); let bmpstring = BmpString::new(&s); assert_eq!(bmpstring.to_der_len(), Ok(4 + 2 * s.len())); let _v = bmpstring.to_der_vec().expect("serialization failed"); } #[test] fn to_der_bool() { let v = Boolean::new(0xff) .to_der_vec() .expect("serialization failed"); assert_eq!(&v, &[0x01, 0x01, 0xff]); // let v = false.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x01, 0x01, 0x00]); // let v = true.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x01, 0x01, 0xff]); // raw value (not 0 of 0xff) let v = Boolean::new(0x8a) .to_der_vec_raw() .expect("serialization failed"); assert_eq!(&v, &[0x01, 0x01, 0x8a]); } #[test] fn to_der_enumerated() { let v = Enumerated(2).to_der_vec().expect("serialization failed"); assert_eq!(Enumerated(2).to_der_len(), Ok(3)); assert_eq!(&v, &[0x0a, 0x01, 0x02]); // let (_, result) = Enumerated::from_der(&v).expect("parsing failed"); assert_eq!(result, Enumerated(2)); } #[test] fn to_der_generalizedtime() { // date without millisecond let dt = ASN1DateTime::new(1999, 12, 31, 23, 59, 59, None, ASN1TimeZone::Z); let time = GeneralizedTime::new(dt); let v = time.to_der_vec().expect("serialization failed"); assert_eq!(&v[..2], &hex!("18 0f")); assert_eq!(&v[2..], b"19991231235959Z"); let (_, time2) = GeneralizedTime::from_der(&v).expect("decoding serialized object failed"); assert!(time.eq(&time2)); assert_eq!(time.to_der_len(), Ok(0x11)); // // date with millisecond let dt = ASN1DateTime::new(1999, 12, 31, 23, 59, 59, Some(123), ASN1TimeZone::Z); let time = GeneralizedTime::new(dt); let v = time.to_der_vec().expect("serialization failed"); assert_eq!(&v[..2], &hex!("18 13")); assert_eq!(&v[2..], b"19991231235959.123Z"); let (_, time2) = GeneralizedTime::from_der(&v).expect("decoding serialized object failed"); assert!(time.eq(&time2)); } #[test] fn to_der_graphicstring() { test_simple_string!(GraphicString, "123456"); test_string_invalid_charset!(GraphicString, "é23456"); } fn encode_decode_assert_int(t: T, expected: &[u8]) where T: ToDer + std::fmt::Debug + Eq, for<'a> T: TryFrom, Error = Error>, { let v = t.to_der_vec().expect("serialization failed"); assert_eq!(&v, expected); let (_, obj) = Integer::from_der(&v).expect("decoding serialized object failed"); let t2: T = obj.try_into().unwrap(); assert_eq!(t, t2); } #[test] fn to_der_integer() { let int = Integer::new(&hex!("02")); let v = int.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x02, 0x01, 0x02]); // from_u32 let int = Integer::from_u32(2); let v = int.to_der_vec().expect("serialization failed"); assert_eq!(&v, &[0x02, 0x01, 0x02]); // impl ToDer for primitive types encode_decode_assert_int(2u32, &[0x02, 0x01, 0x02]); // signed i32 (> 0) encode_decode_assert_int(4, &[0x02, 0x01, 0x04]); // signed i32 (< 0) encode_decode_assert_int(-4, &[0x02, 0x01, 0xfc]); // negative number encode_decode_assert_int(-1i8, &[0x02, 0x01, 0xff]); } #[test] fn to_der_null() { let bytes: &[u8] = &hex!("05 00"); let s = Null::new(); assert_eq!(s.to_der_len(), Ok(2)); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(&v, bytes); // unit assert_eq!(().to_der_len(), Ok(2)); let (_, s2) = <()>::from_der(&v).expect("decoding serialized object failed"); assert!(().eq(&s2)); let v2 = ().to_der_vec().expect("serialization failed"); assert_eq!(&v2, bytes); // invalid null encodings let bytes: &[u8] = &hex!("05 01 00"); let _ = Null::from_ber(bytes).expect_err("should fail"); let _ = <()>::from_ber(bytes).expect_err("should fail"); } #[test] fn to_der_numericstring() { test_simple_string!(NumericString, "123456"); test_string_invalid_charset!(NumericString, "abcdef"); test_string_invalid_charset!(NumericString, "1a"); } #[test] fn to_der_objectdescriptor() { test_simple_string!(ObjectDescriptor, "abcdef"); test_string_invalid_charset!(ObjectDescriptor, "abcdéf"); } #[test] fn to_der_octetstring() { let bytes: &[u8] = &hex!("01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f"); let s = OctetString::from(bytes); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(s.to_der_len(), Ok(bytes.len() + 2)); assert_eq!(&v[..2], &hex!("04 0f")); assert_eq!(&v[2..], bytes); let (_, s2) = OctetString::from_der(&v).expect("decoding serialized object failed"); assert!(s.eq(&s2)); // let v = bytes.to_der_vec().expect("serialization failed"); assert_eq!(bytes.to_der_len(), Ok(bytes.len() + 2)); assert_eq!(&v[..2], &hex!("04 0f")); assert_eq!(&v[2..], bytes); let (_, s2) = OctetString::from_der(&v).expect("decoding serialized object failed"); assert!(s.eq(&s2)); } #[test] fn to_der_real_binary() { // base = 2, value = 4 let r = Real::binary(2.0, 2, 1); let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 03 80 02 01")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!((r.f64() - result.f64()).abs() < f64::EPSILON); // // base = 2, value = 0.5 let r = Real::binary(0.5, 2, 0); let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 03 80 ff 01")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!((r.f64() - result.f64()).abs() < f64::EPSILON); // // base = 2, value = 3.25, but change encoding base (8) let r = Real::binary(3.25, 2, 0).with_enc_base(8); let v = r.to_der_vec().expect("serialization failed"); // note: this encoding has a scale factor (not DER compliant) assert_eq!(&v, &hex!("09 03 94 ff 0d")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!((r.f64() - result.f64()).abs() < f64::EPSILON); // // base = 2, value = 0.00390625, but change encoding base (16) let r = Real::binary(0.00390625, 2, 0).with_enc_base(16); let v = r.to_der_vec().expect("serialization failed"); // note: this encoding has a scale factor (not DER compliant) assert_eq!(&v, &hex!("09 03 a0 fe 01")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!((r.f64() - result.f64()).abs() < f64::EPSILON); // // 2 octets for exponent, negative exponent and abs(exponent) is all 1's and fills the whole octet(s) let r = Real::binary(3.0, 2, -1020); let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 04 81 fc 04 03")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!((r.f64() - result.f64()).abs() < f64::EPSILON); // // 3 octets for exponent, and // check that first 9 bits for exponent are not all 1's let r = Real::binary(1.0, 2, 262140); let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 05 82 03 ff fc 01")); let (_, result) = Real::from_der(&v).expect("parsing failed"); // XXX value cannot be represented as f64 (inf) assert!(result.f64().is_infinite()); // // >3 octets for exponent, and // mantissa < 0 let r = Real::binary(-1.0, 2, 76354972); let v = r.to_der_vec().expect("serialization failed"); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert_eq!(&v, &hex!("09 07 c3 04 04 8d 15 9c 01")); // XXX value cannot be represented as f64 (-inf) assert!(result.f64().is_infinite()); } #[test] fn to_der_real_special() { // ZERO let r = Real::Zero; let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 00")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!(r.eq(&result)); // INFINITY let r = Real::Infinity; let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 01 40")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!(r.eq(&result)); // MINUS INFINITY let r = Real::NegInfinity; let v = r.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("09 01 41")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!(r.eq(&result)); } #[test] fn to_der_real_string() { // non-zero value, base 10 let r = Real::new(1.2345); let v = r.to_der_vec().expect("serialization failed"); // assert_eq!(&v, &hex!("09 00")); let (_, result) = Real::from_der(&v).expect("parsing failed"); assert!(r.eq(&result)); } #[test] fn to_der_sequence() { let it = [2, 3, 4].iter(); let seq = Sequence::from_iter_to_der(it).unwrap(); let v = seq.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("30 09 02 01 02 02 01 03 02 01 04")); let (_, seq2) = Sequence::from_der(&v).expect("decoding serialized object failed"); assert_eq!(seq, seq2); // Vec::ToDer let v = vec![2, 3, 4].to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("30 09 02 01 02 02 01 03 02 01 04")); } #[test] fn to_der_sequenceof() { let seq = SequenceOf::from_iter([2, 3, 4]); let v = seq.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("30 09 02 01 02 02 01 03 02 01 04")); let (_, seq2) = SequenceOf::from_der(&v).expect("decoding serialized object failed"); assert_eq!(seq, seq2); // Vec::ToDer let v = vec![2, 3, 4].to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("30 09 02 01 02 02 01 03 02 01 04")); } #[test] fn to_der_set() { let it = [2u8, 3, 4].iter(); let set = Set::from_iter_to_der(it).unwrap(); let v = set.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("31 09 02 01 02 02 01 03 02 01 04")); // let (_, set2) = Set::from_der(&v).expect("decoding serialized object failed"); // assert_eq!(set, set2); // BTreeSet::ToDer let set2 = BTreeSet::from_iter(vec![2, 3, 4]); let v = set2.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("31 09 02 01 02 02 01 03 02 01 04")); } #[test] fn to_der_set_of() { let seq = SetOf::from_iter([2, 3, 4]); let v = seq.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("31 09 02 01 02 02 01 03 02 01 04")); let (_, seq2) = SetOf::from_der(&v).expect("decoding serialized object failed"); assert_eq!(seq, seq2); } #[test] fn to_der_str() { let s = "abcdef"; assert_eq!(s.to_der_len(), Ok(2 + s.len())); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(&v[..2], &hex!("0c 06")); assert_eq!(&v[2..], b"abcdef"); let (_, s2) = Utf8String::from_der(&v).expect("decoding serialized object failed"); assert!(s.eq(s2.as_ref())); // long string let sz = 256; let s = str::repeat("a", sz); let s = s.as_str(); assert_eq!(s.to_der_len(), Ok(4 + sz)); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(v.len(), 4 + sz); } #[test] fn to_der_string() { let s = "abcdef".to_string(); assert_eq!(s.to_der_len(), Ok(2 + s.len())); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(&v[..2], &hex!("0c 06")); assert_eq!(&v[2..], b"abcdef"); let (_, s2) = Utf8String::from_der(&v).expect("decoding serialized object failed"); assert!(s.eq(s2.as_ref())); // long string let sz = 256; let s = str::repeat("a", sz); assert_eq!(s.to_der_len(), Ok(4 + sz)); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(v.len(), 4 + sz); } #[test] fn to_der_tagged_explicit() { let tagged = TaggedParser::new_explicit(Class::ContextSpecific, 1, 2u32); let v = tagged.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("a1 03 02 01 02")); let (_, t2) = TaggedParser::::from_der(&v).expect("decoding serialized object failed"); assert!(tagged.eq(&t2)); // TaggedValue API let tagged = TaggedValue::explicit(2u32); let v = tagged.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("a1 03 02 01 02")); let (_, t2) = TaggedExplicit::::from_der(&v).expect("decoding serialized object failed"); assert!(tagged.eq(&t2)); } #[test] fn to_der_tagged_implicit() { let tagged = TaggedParser::new_implicit(Class::ContextSpecific, false, 1, 2u32); let v = tagged.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("81 01 02")); let (_, t2) = TaggedParser::::from_der(&v).expect("decoding serialized object failed"); assert!(tagged.eq(&t2)); // TaggedValue API let tagged = TaggedValue::implicit(2u32); let v = tagged.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("81 01 02")); let (_, t2) = TaggedImplicit::::from_der(&v).expect("decoding serialized object failed"); assert!(tagged.eq(&t2)); } #[test] fn to_der_tagged_implicit_optional() { // TaggedValue API let tagged = Some(TaggedValue::implicit(Integer::from(2))); let v = tagged.to_der_vec().expect("serialization failed"); assert_eq!(&v, &hex!("81 01 02")); let (_, t2) = OptTaggedImplicit::::from_der(&v) .expect("decoding serialized object failed"); assert!(tagged.eq(&t2)); } #[test] fn to_der_teletexstring() { test_simple_string!(TeletexString, "abcdef"); } #[test] fn to_der_utctime() { let dt = ASN1DateTime::new(99, 12, 31, 23, 59, 59, None, ASN1TimeZone::Z); let time = UtcTime::new(dt); let v = time.to_der_vec().expect("serialization failed"); assert_eq!(&v[..2], &hex!("17 0d")); assert_eq!(&v[2..], b"991231235959Z"); let (_, time2) = UtcTime::from_der(&v).expect("decoding serialized object failed"); assert!(time.eq(&time2)); } #[test] fn to_der_universalstring() { const S: &str = "abcdef"; let s = UniversalString::from(S); assert_eq!(s.to_der_len(), Ok(2 + 4 * S.len())); let v = s.to_der_vec().expect("serialization failed"); assert_eq!( &v, &hex!("1c 18 00000061 00000062 00000063 00000064 00000065 00000066") ); let (_, s2) = UniversalString::from_der(&v).expect("decoding serialized object failed"); assert!(s.eq(&s2)); // long string let sz = 256; let s = str::repeat("a", sz); let s = UniversalString::from(s); assert_eq!(s.to_der_len(), Ok(4 + 4 * sz)); let v = s.to_der_vec().expect("serialization failed"); assert_eq!(v.len(), 4 + 4 * sz); } #[test] fn to_der_utf8string() { test_simple_string!(Utf8String, "abcdef"); } #[test] fn to_der_visiblestring() { test_simple_string!(VisibleString, "abcdef"); test_string_invalid_charset!(VisibleString, "abcdéf"); } #[test] fn to_der_videotexstring() { test_simple_string!(VideotexString, "abcdef"); } rusticata-asn1-rs-07e764c/tests/to_static.rs000066400000000000000000000025531476576401200210560ustar00rootroot00000000000000use std::borrow::Cow; use asn1_rs::*; #[derive(ToStatic)] pub struct Unit; #[test] fn derive_unit_tostatic() { let unit = Unit; let _static = unit.to_static(); assert_static_lifetime(&unit); } #[derive(ToStatic)] //#[debug_derive] pub struct Unnamed<'a>(pub Cow<'a, str>); #[test] fn derive_unnamed_tostatic() { let s = Cow::Borrowed("test"); let unnamed = Unnamed(s); let _static = unnamed.to_static(); assert!(matches! { _static.0, Cow::Owned(_) }); } #[derive(ToStatic)] //#[debug_derive] pub struct Named<'a> { cow: Cow<'a, str>, } #[derive(ToStatic)] //#[debug_derive] pub struct Embed<'a> { a: Cow<'a, str>, n: Named<'a>, } #[test] fn derive_named_tostatic() { let s = Cow::Borrowed("test"); let named1 = Named { cow: s }; let _static1 = named1.to_static(); assert_static_lifetime(&_static1); assert!(matches! { _static1.cow, Cow::Owned(_) }); let s2 = Cow::Borrowed("test2"); let named2 = Embed { a: s2, n: named1 }; let _static2 = named2.to_static(); assert_static_lifetime(&_static2); assert!(matches! { _static2.a, Cow::Owned(_) }); assert!(matches! { _static2.n.cow, Cow::Owned(_) }); } #[derive(ToStatic)] //#[debug_derive] pub enum MyEnum0 { Variant0, Variant1(u32), Variant2 { a: u32, b: u32 }, } fn assert_static_lifetime(_arg: &T) where T: 'static, { } rusticata-asn1-rs-07e764c/tests/toder_sequence_lifetime.rs000066400000000000000000000012421476576401200237420ustar00rootroot00000000000000#![cfg(feature = "std")] use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence, ToDerSequence)] #[debug_derive] pub struct T1<'a> { a: u32, b: u16, c: u16, d: &'a str, } #[test] fn toder_sequence_lifetime() { let input = &hex!("300f0201 01020102 020103 0c0461626364"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!( t1, T1 { a: 1, b: 2, c: 3, d: "abcd" } ); // serialize back data let output = t1.to_der_vec().expect("serialization failed"); assert_eq!(&input[..], output); } rusticata-asn1-rs-07e764c/tests/toder_sequence_simple.rs000066400000000000000000000010371476576401200234370ustar00rootroot00000000000000#![cfg(feature = "std")] use asn1_rs::*; use hex_literal::hex; #[derive(Debug, PartialEq, DerSequence, ToDerSequence)] #[debug_derive] pub struct T1 { a: u32, b: u16, c: u16, } #[test] fn toder_sequence() { let input = &hex!("30090201 01020102 020103"); let (rem, t1) = T1::from_der(input).expect("parsing failed"); assert!(rem.is_empty()); assert_eq!(t1, T1 { a: 1, b: 2, c: 3 }); // serialize back data let output = t1.to_der_vec().expect("serialization failed"); assert_eq!(&input[..], output); } rusticata-asn1-rs-07e764c/tests/x509.rs000066400000000000000000000111721476576401200175670ustar00rootroot00000000000000//! Test implementation for X.509 //! //! This is mostly used to verify that required types and functions are implemented, //! and that provided API is convenient. use asn1_rs::{ nom, Any, CheckDerConstraints, Choice, Error, FromBer, FromDer, Oid, ParseResult, Sequence, SetOf, Tag, Tagged, }; use hex_literal::hex; use nom::sequence::pair; use std::convert::{TryFrom, TryInto}; const DN: &[u8] = &hex!( " 30 45 31 0b 30 09 06 03 55 04 06 13 02 46 52 31 13 30 11 06 03 55 04 08 0c 0a 53 6f 6d 65 2d 53 74 61 74 65 31 21 30 1f 06 03 55 04 0a 0c 18 49 6e 74 65 72 6e 65 74 20 57 69 64 67 69 74 73 20 50 74 79 20 4c 74 64 " ); // Name ::= CHOICE { -- only one possibility for now -- // rdnSequence RDNSequence } // RDNSequence ::= SEQUENCE OF RelativeDistinguishedName #[derive(Debug)] pub struct Name<'a> { pub rdn_sequence: Vec>, } impl<'a> FromDer<'a> for Name<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, rdn_sequence) = >::from_der(bytes)?; let dn = Name { rdn_sequence }; Ok((rem, dn)) } } // RelativeDistinguishedName ::= // SET SIZE (1..MAX) OF AttributeTypeAndValue #[derive(Debug)] pub struct RelativeDistinguishedName<'a> { pub v: Vec>, } impl<'a> FromDer<'a> for RelativeDistinguishedName<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, set) = SetOf::::from_der(bytes)?; let v: Vec<_> = set.into(); if v.is_empty() { return Err(nom::Err::Failure(Error::InvalidLength)); } Ok((rem, RelativeDistinguishedName { v })) } } // AttributeTypeAndValue ::= SEQUENCE { // type AttributeType, // value AttributeValue } #[derive(Debug)] pub struct AttributeTypeAndValue<'a> { pub oid: Oid<'a>, pub value: AttributeValue<'a>, } impl<'a> FromBer<'a> for AttributeTypeAndValue<'a> { fn from_ber(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, seq) = Sequence::from_der(bytes)?; let (_, (oid, value)) = seq.parse_into(|i| pair(Oid::from_der, AttributeValue::from_der)(i))?; let attr = AttributeTypeAndValue { oid, value }; Ok((rem, attr)) } } impl<'a> FromDer<'a> for AttributeTypeAndValue<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, seq) = Sequence::from_der(bytes)?; let (_, (oid, value)) = seq.parse_into(|i| pair(Oid::from_der, AttributeValue::from_der)(i))?; let attr = AttributeTypeAndValue { oid, value }; Ok((rem, attr)) } } impl CheckDerConstraints for AttributeTypeAndValue<'_> { fn check_constraints(any: &Any) -> asn1_rs::Result<()> { any.tag().assert_eq(Sequence::TAG)?; Ok(()) } } // AttributeType ::= OBJECT IDENTIFIER // AttributeValue ::= ANY -- DEFINED BY AttributeType #[derive(Debug)] pub enum AttributeValue<'a> { DirectoryString(DirectoryString), Other(Any<'a>), } impl<'a> FromDer<'a> for AttributeValue<'a> { fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> { let (rem, any) = Any::from_der(bytes)?; let ds = if DirectoryString::can_decode(any.tag()) { AttributeValue::DirectoryString(any.try_into()?) } else { AttributeValue::Other(any) }; Ok((rem, ds)) } } // DirectoryString ::= CHOICE { // teletexString TeletexString (SIZE (1..MAX)), // printableString PrintableString (SIZE (1..MAX)), // universalString UniversalString (SIZE (1..MAX)), // utf8String UTF8String (SIZE (1..MAX)), // bmpString BMPString (SIZE (1..MAX)) } #[derive(Debug)] pub enum DirectoryString { Printable(String), Utf8(String), } impl Choice for DirectoryString { fn can_decode(tag: Tag) -> bool { matches!(tag, Tag::PrintableString | Tag::Utf8String) } } impl<'a> TryFrom> for DirectoryString { type Error = Error; fn try_from(any: Any<'a>) -> Result { match any.tag() { Tag::PrintableString => { let s = any.printablestring()?; Ok(DirectoryString::Printable(s.string())) } Tag::Utf8String => { let s = any.string()?; Ok(DirectoryString::Utf8(s)) } _ => Err(Error::InvalidTag), } } } #[test] fn x509_decode_dn() { let (rem, dn) = Name::from_der(DN).expect("parsing failed"); assert!(rem.is_empty()); // dbg!(&dn); assert_eq!(dn.rdn_sequence.len(), 3); }