chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

1
vendor/asn1-rs/.cargo-checksum.json vendored Normal file

File diff suppressed because one or more lines are too long

182
vendor/asn1-rs/CHANGELOG.md vendored Normal file
View File

@@ -0,0 +1,182 @@
# 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<Any>::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<T>` or `BTreeSet<T>`) 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

666
vendor/asn1-rs/Cargo.lock generated vendored Normal file
View File

@@ -0,0 +1,666 @@
# 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3109e49b1e4909e9db6515a30c633684d68cdeaa252f215214cb4fa1a5bfee2c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"synstructure",
]
[[package]]
name = "asn1-rs-impl"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b18050c2cd6fe86c3a76584ef5e0baf286d038cda203eb6223df2cc413565f7"
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",
]

203
vendor/asn1-rs/Cargo.toml vendored Normal file
View File

@@ -0,0 +1,203 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2018"
rust-version = "1.63"
name = "asn1-rs"
version = "0.7.1"
authors = ["Pierre Chifflier <chifflier@wzdftpd.net>"]
build = false
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",
]
autolib = false
autobins = false
autoexamples = false
autotests = false
autobenches = false
description = "Parser/encoder for ASN.1 BER/DER data"
homepage = "https://github.com/rusticata/asn1-rs"
readme = "README.md"
keywords = [
"BER",
"DER",
"ASN1",
"parser",
"nom",
]
categories = ["parser-implementations"]
license = "MIT OR Apache-2.0"
repository = "https://github.com/rusticata/asn1-rs.git"
[package.metadata.cargo_check_external_types]
allowed_external_types = [
"nom",
"nom::*",
"asn1_rs_derive",
"asn1_rs_derive::*",
]
[package.metadata.docs.rs]
all-features = true
rustdoc-args = [
"--cfg",
"docsrs",
]
[features]
bigint = ["num-bigint"]
bits = ["bitvec"]
datetime = ["time"]
debug = [
"std",
"colored",
]
default = ["std"]
serialize = ["cookie-factory"]
std = []
trace = ["debug"]
[lib]
name = "asn1_rs"
path = "src/lib.rs"
[[example]]
name = "dump-der"
path = "examples/dump-der.rs"
[[test]]
name = "ber"
path = "tests/ber.rs"
[[test]]
name = "compile_tests"
path = "tests/compile_tests.rs"
[[test]]
name = "cov"
path = "tests/cov.rs"
[[test]]
name = "der"
path = "tests/der.rs"
[[test]]
name = "issue-18-constructed-bit-sequence"
path = "tests/issue-18-constructed-bit-sequence.rs"
[[test]]
name = "issue-27-option-struct-derive"
path = "tests/issue-27-option-struct-derive.rs"
[[test]]
name = "issue-43-encoding-large-tags"
path = "tests/issue-43-encoding-large-tags.rs"
[[test]]
name = "krb5"
path = "tests/krb5.rs"
[[test]]
name = "to_der"
path = "tests/to_der.rs"
[[test]]
name = "to_static"
path = "tests/to_static.rs"
[[test]]
name = "toder_sequence_lifetime"
path = "tests/toder_sequence_lifetime.rs"
[[test]]
name = "toder_sequence_simple"
path = "tests/toder_sequence_simple.rs"
[[test]]
name = "x509"
path = "tests/x509.rs"
[dependencies.asn1-rs-derive]
version = "0.6"
[dependencies.asn1-rs-impl]
version = "0.2"
[dependencies.bitvec]
version = "1.0"
optional = true
[dependencies.colored]
version = "3.0"
optional = true
[dependencies.cookie-factory]
version = "0.3.0"
optional = true
[dependencies.displaydoc]
version = "0.2.2"
[dependencies.nom]
version = "7.0"
features = ["std"]
default-features = false
[dependencies.num-bigint]
version = "0.4"
optional = true
[dependencies.num-traits]
version = "0.2.14"
[dependencies.rusticata-macros]
version = "4.0"
[dependencies.thiserror]
version = "2.0.0"
[dependencies.time]
version = "0.3"
features = [
"macros",
"parsing",
"formatting",
]
optional = true
[dev-dependencies.colored]
version = "3.0"
[dev-dependencies.hex-literal]
version = "0.4"
[dev-dependencies.pem]
version = "3.0"
[dev-dependencies.trybuild]
version = "1.0"

201
vendor/asn1-rs/LICENSE-APACHE vendored Normal file
View File

@@ -0,0 +1,201 @@
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.

25
vendor/asn1-rs/LICENSE-MIT vendored Normal file
View File

@@ -0,0 +1,25 @@
Copyright (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.

179
vendor/asn1-rs/README.md vendored Normal file
View File

@@ -0,0 +1,179 @@
<!-- cargo-sync-readme start -->
[![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<u8>` 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"
<!-- cargo-sync-readme end -->
## 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.

39
vendor/asn1-rs/doc/DEBUG.md vendored Normal file
View File

@@ -0,0 +1,39 @@
# 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
```

354
vendor/asn1-rs/doc/DERIVE.md vendored Normal file
View File

@@ -0,0 +1,354 @@
# 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<Vec<S2>, Error, 0>,
}
# let parser = |input| -> Result<(), Error> {
let (rem, result) = S::from_ber(input)?;
// Get a reference on c (type is &Vec<S2>)
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<u16>,
// 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<u16>,
// 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<u16>,
}
# 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<T>`, 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<BTreeSet<S2>, Error, 0>,
}
# let parser = |input| -> Result<(), Error> {
let (rem, result) = S::from_ber(input)?;
// Get a reference on c (type is &BTreeSet<S2>)
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<Error>`, 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<asn1_rs::Error> 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<asn1_rs::Error> 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

238
vendor/asn1-rs/doc/RECIPES.md vendored Normal file
View File

@@ -0,0 +1,238 @@
# 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) = <u32>::from_der(input)?;
# Ok(()) };
```
Note: this crates makes extensive use of types annotation and turbofish operator, for example `<Type>::from_der()` or `TaggedExplicit::<u32, Error, 0>::from_der()`.
See table B-3 in <https://doc.rust-lang.org/book/appendix-02-operators.html> 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<T>` or `Vec<T>`:
```rust
# use asn1_rs::*;
# let parser = |input| -> Result<(), Error> {
let (rem, result) = SequenceOf::<u32>::from_der(input)?;
# Ok(()) };
```
or
```rust
# use asn1_rs::*;
# let parser = |input| -> Result<(), Error> {
let (rem, result) = <Vec<u32>>::from_der(input)?;
# Ok(()) };
```
`SET OF T` can be parsed using either `SetOf<T>`, `BTreeSet<T>` or `HashSet<T>`.
## `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::<u32, Error, 0>::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::<u32, Error, Explicit, {Class::CONTEXT_SPECIFIC}, 0>::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::<Explicit, u32>::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<TaggedExplicit<...>>` can be used:
```rust
# use asn1_rs::*;
# let parser = |input| -> Result<(), Error> {
let (rem, result) = Option::<TaggedExplicit::<u32, Error, 0>>::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::<u32, Error, 0>::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::<u32, Error, 0>::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::<u32, Error, Implicit, { Class::CONTEXT_SPECIFIC }, 1>::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::<Implicit, u32>::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<TaggedImplicit<...>>` can be used:
```rust
# use asn1_rs::*;
# let parser = |input| -> Result<(), Error> {
let (rem, result) = Option::<TaggedImplicit::<u32, Error, 0>>::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::<u32, Error, 0>::from_der(input)?;
# Ok(()) };
```

239
vendor/asn1-rs/examples/dump-der.rs vendored Normal file
View File

@@ -0,0 +1,239 @@
use 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!("... <continued>");
}
}
fn main() -> std::result::Result<(), Box<dyn Error>> {
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<Any>, 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::<Any, asn1_rs::Error>() {
print_der_result_any(item, depth + 1, ctx);
}
}
Tag::Sequence => {
let seq = any.sequence().unwrap();
for item in seq.der_iter::<Any, asn1_rs::Error>() {
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(),
}
}

493
vendor/asn1-rs/src/asn1_types/any.rs vendored Normal file
View File

@@ -0,0 +1,493 @@
use 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<T>(&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<F, T, E>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<F, T, E>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<T>(&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<Oid<'a>> {
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<Oid<'a>> {
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<T>` from this object.
pub fn as_optional<'b, T>(&'b self) -> Result<Option<T>>
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<T, E, const CLASS: u8, const TAG: u32>(
&self,
) -> Result<TaggedValue<T, E, Explicit, CLASS, TAG>, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
TryFrom::try_from(self)
}
/// Attempt to create a tagged value (IMPLICIT) from this object.
pub fn as_tagged_implicit<T, E, const CLASS: u8, const TAG: u32>(
&self,
) -> Result<TaggedValue<T, E, Implicit, CLASS, TAG>, E>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
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<String> {
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<Any> {
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<Any> {
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<usize> {
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<usize> {
// 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<usize> {
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<usize> {
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::<Integer>().expect("parse_ber failed");
assert_eq!(r.as_u32(), Ok(1));
let (_, r) = any.parse_der::<Integer>().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!(<Any as DynTagged>::tag(&any), any.tag());
let int = any.integer().unwrap();
assert_eq!(int.as_u16(), Ok(1));
}
}

View File

@@ -0,0 +1,157 @@
use 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<u8, Msb0>> {
BitSlice::<_, Msb0>::try_from_slice(&self.data).ok()
}
}
impl AsRef<[u8]> for BitString<'_> {
fn as_ref(&self) -> &[u8] {
&self.data
}
}
impl<'a> TryFrom<Any<'a>> for BitString<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<BitString<'a>> {
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<BitString<'a>> {
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<usize> {
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<usize> {
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<usize> {
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));
}
}

147
vendor/asn1-rs/src/asn1_types/boolean.rs vendored Normal file
View File

@@ -0,0 +1,147 @@
use 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<Any<'a>> for Boolean {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Boolean> {
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<Boolean> {
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<usize> {
// 3 = 1 (tag) + 1 (length) + 1 (value)
Ok(3)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
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<usize> {
let sz = writer.write(&[Self::TAG.0 as u8, 0x01, self.value])?;
Ok(sz)
}
}
impl<'a> TryFrom<Any<'a>> for bool {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<bool> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for bool {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<bool> {
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<usize> {
// 3 = 1 (tag) + 1 (length) + 1 (value)
Ok(3)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[Self::TAG.0 as u8, 0x01]).map_err(Into::into)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let b = if *self { 0xff } else { 0x00 };
writer.write(&[b]).map_err(Into::into)
}
}

21
vendor/asn1-rs/src/asn1_types/choice.rs vendored Normal file
View File

@@ -0,0 +1,21 @@
use 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<T> 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> {}

View File

@@ -0,0 +1,125 @@
use crate::*;
use core::convert::TryFrom;
#[derive(Debug, PartialEq, Eq)]
pub struct EmbeddedPdv<'a> {
pub identification: PdvIdentification<'a>,
pub data_value_descriptor: Option<ObjectDescriptor<'a>>,
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<Any<'a>> for EmbeddedPdv<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
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<Self> {
let data = any.data;
// AUTOMATIC TAGS means all values will be tagged (IMPLICIT)
// [0] -> identification
let (rem, seq0) =
TaggedParser::<Explicit, Any>::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::<Implicit, &[u8]>::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<'_> {}

View File

@@ -0,0 +1,61 @@
use 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<Any<'a>> for EndOfContent {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<EndOfContent> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for EndOfContent {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<EndOfContent> {
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<usize> {
// Ok(2)
// }
// fn write_der_header(&self, writer: &mut dyn std::io::Write) -> crate::SerializeResult<usize> {
// 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<usize> {
// Ok(0)
// }
// }

View File

@@ -0,0 +1,72 @@
use 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<Any<'a>> for Enumerated {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Enumerated> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Enumerated {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Enumerated> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_primitive()?;
let res_u64 = bytes_to_u64(any.data)?;
if res_u64 > (<u32>::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<usize> {
Integer::from(self.0).to_der_len()
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
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<usize> {
let int = Integer::from(self.0);
int.write_der_content(writer)
}
}

View File

@@ -0,0 +1,303 @@
use 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<Self> {
// 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<OffsetDateTime> {
self.0.to_datetime()
}
}
impl<'a> TryFrom<Any<'a>> for GeneralizedTime {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<GeneralizedTime> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for GeneralizedTime {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<GeneralizedTime> {
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<usize> {
// 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<usize> {
// 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<usize> {
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)
}
}

727
vendor/asn1-rs/src/asn1_types/integer.rs vendored Normal file
View File

@@ -0,0 +1,727 @@
use 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<const N: usize>(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<const N: usize>(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<Any<'a>> for $int {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for $int {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
$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<usize> {
let int = Integer::from(*self);
int.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der(writer)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_content(writer)
}
}
};
}
macro_rules! impl_uint {
($ty:ty) => {
impl<'a> TryFrom<Any<'a>> for $ty {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for $ty {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
$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<usize> {
let int = Integer::from(*self);
int.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der(writer)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
let int = Integer::from(*self);
int.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
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<u16, _>, 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<BigUint> {
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<const N: usize>(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<const N: usize>(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<Integer<'_>> for $ty {
type Error = Error;
fn try_from(value: Integer<'_>) -> Result<Self> {
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<Any<'a>> for Integer<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Integer<'a>> {
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<Integer<'a>> {
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<usize> {
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<usize> {
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<usize> {
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)));
}
}

26
vendor/asn1-rs/src/asn1_types/mod.rs vendored Normal file
View File

@@ -0,0 +1,26 @@
mod 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::*,
};

105
vendor/asn1-rs/src/asn1_types/null.rs vendored Normal file
View File

@@ -0,0 +1,105 @@
use 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<Any<'a>> for Null {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Null> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Null {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Null> {
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<usize> {
Ok(2)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[0x05, 0x00]).map_err(Into::into)
}
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
Ok(0)
}
}
impl<'a> TryFrom<Any<'a>> 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<usize> {
Ok(2)
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
writer.write(&[0x05, 0x00]).map_err(Into::into)
}
fn write_der_content(&self, _writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
Ok(0)
}
}

View File

@@ -0,0 +1,18 @@
use 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(())
}
}

View File

@@ -0,0 +1,157 @@
use 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<Any<'a>> for OctetString<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<OctetString<'a>> {
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<OctetString<'a>> {
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<usize> {
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<usize> {
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<usize> {
writer.write(&self.data).map_err(Into::into)
}
}
impl<'a> TryFrom<Any<'a>> 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<usize> {
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<usize> {
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<usize> {
writer.write(self).map_err(Into::into)
}
}

528
vendor/asn1-rs/src/asn1_types/oid.rs vendored Normal file
View File

@@ -0,0 +1,528 @@
use 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<Any<'a>> for Oid<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
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<Self> {
// 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<usize> {
// 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<usize> {
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<usize> {
writer.write(&self.asn1).map_err(Into::into)
}
}
fn encode_relative(ids: &'_ [u64]) -> impl Iterator<Item = u8> + '_ {
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<Oid<'static>, 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<u8> = [(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<Oid<'static>, OidParseError> {
if s.is_empty() {
return Err(OidParseError::TooShort);
}
let asn1_encoded: Vec<u8> = 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<String> = 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<String> = 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<Item = BigUint> + 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<impl FusedIterator<Item = u64> + 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<usize, Output = Self> + From<u8> {}
impl<N> Repr for N where N: Num + Shl<usize, Output = N> + From<u8> {}
struct SubIdentifierIterator<'a, N: Repr> {
oid: &'a Oid<'a>,
pos: usize,
first: bool,
n: PhantomData<&'a N>,
}
impl<N: Repr> Iterator for SubIdentifierIterator<'_, N> {
type Item = N;
fn next(&mut self) -> Option<Self::Item> {
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 = <N as Zero>::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<N: Repr> FusedIterator for SubIdentifierIterator<'_, N> {}
impl<N: Repr> 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(")?;
<Oid as fmt::Display>::fmt(self, f)?;
f.write_str(")")
}
}
impl FromStr for Oid<'_> {
type Err = OidParseError;
fn from_str(s: &str) -> core::result::Result<Self, Self::Err> {
let v: core::result::Result<Vec<_>, _> = s.split('.').map(|c| c.parse::<u64>()).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});
}
}

View File

@@ -0,0 +1,123 @@
use crate::*;
// note: we cannot implement `TryFrom<Any<'a>> with generic errors for Option<T>`,
// because this conflicts with generic `T` implementation in
// `src/traits.rs`, since `T` always satisfies `T: Into<Option<T>>`
//
// for the same reason, we cannot use a generic error type here
impl<'a, T> FromBer<'a> for Option<T>
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<Any<'a>> {
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<T>
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<Any<'a>> {
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<T> CheckDerConstraints for Option<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
T::check_constraints(any)
}
}
impl<T> DynTagged for Option<T>
where
T: DynTagged,
{
fn tag(&self) -> Tag {
if self.is_some() {
self.tag()
} else {
Tag(0)
}
}
}
#[cfg(feature = "std")]
impl<T> ToDer for Option<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
match self {
None => Ok(0),
Some(t) => t.to_der_len(),
}
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
match self {
None => Ok(0),
Some(t) => t.write_der_header(writer),
}
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
match self {
None => Ok(0),
Some(t) => t.write_der_content(writer),
}
}
}

466
vendor/asn1-rs/src/asn1_types/real.rs vendored Normal file
View File

@@ -0,0 +1,466 @@
use 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<Any<'a>> for Real {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for Real {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<Self> {
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::<u32>() {
Err(_) => Err(any.tag().invalid_value("Invalid float string encoding")),
Ok(v) => Ok(Real::new(v.into())),
}
}
0x2 /* NR2 */ | 0x3 /* NR3 */=> {
match s.parse::<f64>() {
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<usize> {
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<usize> {
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<usize> {
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<f32> for Real {
fn from(f: f32) -> Self {
Real::new(f.into())
}
}
impl From<f64> for Real {
fn from(f: f64) -> Self {
Real::new(f)
}
}
impl From<Real> for f32 {
fn from(r: Real) -> Self {
r.f32()
}
}
impl From<Real> 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)
}

View File

@@ -0,0 +1,27 @@
use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged};
use core::convert::{TryFrom, TryInto};
impl<'a> TryFrom<Any<'a>> for f32 {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<f32> {
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;
}

View File

@@ -0,0 +1,27 @@
use crate::{Any, CheckDerConstraints, DerAutoDerive, Error, Real, Result, Tag, Tagged};
use core::convert::{TryFrom, TryInto};
impl<'a> TryFrom<Any<'a>> for f64 {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<f64> {
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;
}

View File

@@ -0,0 +1,400 @@
use 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<T>`)
///
/// 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<Sequence<'a>> {
/// 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::<u32, Error>() {
/// // item has type `Result<u32>`, 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<U, F, E>(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<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
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<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
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<F, T, E>(&'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<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E>
where
F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<T, E>(&'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<T, E>(&'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<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
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<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
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<T, U, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromBer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SequenceIterator::<T, BerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 = SequenceIterator::<T, BerParser, E>::new(&data)
.collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
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<T, U, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromDer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 = SequenceIterator::<T, DerParser, E>::new(&data)
.collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
Ok(v2)
}
}
}
pub fn into_der_sequence_of_ref<T, E>(self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
match self.content {
Cow::Borrowed(bytes) => SequenceIterator::<T, DerParser, E>::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<T, U> ToStatic for Vec<T>
where
T: ToStatic<Owned = U>,
U: 'static,
{
type Owned = Vec<U>;
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<Any<'a>> for Sequence<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Sequence<'a>> {
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<Sequence<'a>> {
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<usize> {
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<usize> {
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<usize> {
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<T, IT>(it: IT) -> SerializeResult<Self>
where
IT: Iterator<Item = T>,
T: ToDer,
T: Tagged,
{
let mut v = Vec::new();
for item in it {
let item_v = <T as ToDer>::to_der_vec(&item)?;
v.extend_from_slice(&item_v);
}
Ok(Sequence {
content: Cow::Owned(v),
})
}
}

View File

@@ -0,0 +1,106 @@
use 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::<Integer, DerParser>::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<T>,
_f: PhantomData<F>,
_e: PhantomData<E>,
}
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<Error>,
{
type Item = Result<T, E>;
fn next(&mut self) -> Option<Self::Item> {
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<Error>,
{
type Item = Result<T, E>;
fn next(&mut self) -> Option<Self::Item> {
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()))
}
}
}
}

View File

@@ -0,0 +1,197 @@
use 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<Target = [T]>` and `DerefMut<Target = [T]>`, 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<u32>`, since parsing the serialized bytes could fail
/// sum += *item;
/// }
/// assert_eq!(sum, 9);
///
/// ```
#[derive(Debug, PartialEq)]
pub struct SequenceOf<T> {
pub(crate) items: Vec<T>,
}
impl<T> SequenceOf<T> {
/// Builds a `SEQUENCE OF` from the provided content
#[inline]
pub const fn new(items: Vec<T>) -> Self {
SequenceOf { items }
}
/// Converts `self` into a vector without clones or allocation.
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.items
}
/// Appends an element to the back of a collection
#[inline]
pub fn push(&mut self, item: T) {
self.items.push(item)
}
}
impl<T> AsRef<[T]> for SequenceOf<T> {
fn as_ref(&self) -> &[T] {
&self.items
}
}
impl<T> AsMut<[T]> for SequenceOf<T> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.items
}
}
impl<T> Deref for SequenceOf<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl<T> DerefMut for SequenceOf<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
impl<T> From<SequenceOf<T>> for Vec<T> {
fn from(seq: SequenceOf<T>) -> Self {
seq.items
}
}
impl<T> FromIterator<T> for SequenceOf<T> {
fn from_iter<IT: IntoIterator<Item = T>>(iter: IT) -> Self {
let items = Vec::from_iter(iter);
SequenceOf::new(items)
}
}
impl<'a, T> TryFrom<Any<'a>> for SequenceOf<T>
where
T: FromBer<'a>,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
if !any.header.is_constructed() {
return Err(Error::ConstructExpected);
}
let items = SequenceIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?;
Ok(SequenceOf::new(items))
}
}
impl<T> CheckDerConstraints for SequenceOf<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SequenceIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for SequenceOf<T>
where
T: FromDer<'a, E>,
E: From<Error> + Display + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"SequenceOf::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), parse_der_any, bytes)
.map_err(Err::convert)?;
any.header
.assert_tag(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
let items = SequenceIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<Vec<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, SequenceOf::new(items)))
},
bytes,
)
}
}
impl<T> Tagged for SequenceOf<T> {
const TAG: Tag = Tag::Sequence;
}
#[cfg(feature = "std")]
impl<T> ToDer for SequenceOf<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.items.to_der_len()
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
self.items.write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
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]);
}
}

View File

@@ -0,0 +1,165 @@
use 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<T>
// where
// T: TryFrom<&'b Any<'a>>,
// for<'e> <T as TryFrom<&'b Any<'a>>>::Error: From<Error>,
// T: FromBer<'a, <T as TryFrom<&'b Any<'a>>>::Error>,
// // T: FromBer<'a, E>,
// // E: From<Error>,
// {
// type Error = <T as TryFrom<&'b Any<'a>>>::Error;
// fn try_from(any: &'b Any<'a>) -> Result<Vec<T>, Self::Error> {
// any.tag().assert_eq(Self::TAG)?;
// any.header.assert_constructed()?;
// let v = SequenceIterator::<T, BerParser, Self::Error>::new(any.data)
// .collect::<Result<Vec<T>, Self::Error>>()?;
// Ok(v)
// }
// }
// // XXX this compiles but requires bound TryFrom :/
// impl<'a, 'b, T> TryFrom<&'b Any<'a>> for Vec<T>
// where
// T: TryFrom<&'b Any<'a>>,
// <T as TryFrom<&'b Any<'a>>>::Error: From<Error>,
// T: FromBer<'a, <T as TryFrom<&'b Any<'a>>>::Error>,
// // T: FromBer<'a, E>,
// // E: From<Error>,
// {
// type Error = <T as TryFrom<&'b Any<'a>>>::Error;
// fn try_from(any: &'b Any<'a>) -> Result<Vec<T>, Self::Error> {
// any.tag().assert_eq(Self::TAG)?;
// any.header.assert_constructed()?;
// let v = SequenceIterator::<T, BerParser, Self::Error>::new(any.data)
// .collect::<Result<Vec<T>, Self::Error>>()?;
// Ok(v)
// }
// }
impl<'a, T> TryFrom<Any<'a>> for Vec<T>
where
T: FromBer<'a>,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
trace_generic(
core::any::type_name::<Self>(),
"T::from(Any)",
|any| {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
let res_items: Result<Vec<T>> =
SetIterator::<T, BerParser>::new(any.data).collect();
if res_items.is_err() {
debug_eprintln!(
core::any::type_name::<T>(),
"≠ {}",
"Conversion from Any failed".red()
);
}
res_items
},
any,
)
}
}
impl<T> CheckDerConstraints for Vec<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SequenceIterator::<Any, DerParser>::new(any.data) {
let item = item?;
<T as CheckDerConstraints>::check_constraints(&item)?;
}
Ok(())
}
}
impl<T> Tagged for Vec<T> {
const TAG: Tag = Tag::Sequence;
}
// impl<'a, T> FromBer<'a> for Vec<T>
// where
// T: FromBer<'a>,
// {
// fn from_ber(bytes: &'a [u8]) -> ParseResult<Self> {
// let (rem, any) = Any::from_ber(bytes)?;
// any.header.assert_tag(Self::TAG)?;
// let v = SequenceIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?;
// Ok((rem, v))
// }
// }
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for Vec<T>
where
T: FromDer<'a, E>,
E: From<Error> + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"Sequence::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), parse_der_any, bytes)
.map_err(Err::convert)?;
any.header
.assert_tag(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
let v = SequenceIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<Vec<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, v))
},
bytes,
)
}
}
#[cfg(feature = "std")]
impl<T> ToDer for Vec<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
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<usize> {
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<usize> {
let mut sz = 0;
for t in self.iter() {
sz += t.write_der(writer)?;
}
Ok(sz)
}
}

387
vendor/asn1-rs/src/asn1_types/set.rs vendored Normal file
View File

@@ -0,0 +1,387 @@
use 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<T>` or `HashSet<T>`)
///
/// 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<Set<'a>> {
/// 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::<u32, Error>() {
/// // item has type `Result<u32>`, 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<U, F, E>(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<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
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<U, F, E>(bytes: &'a [u8], op: F) -> ParseResult<'a, U, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, U, E>,
E: From<Error>,
{
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<F, T, E>(&'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<F, T, E>(self, mut f: F) -> ParseResult<'a, T, E>
where
F: FnMut(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<T, E>(&'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<T, E>(&'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<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromBer<'a, E>,
E: From<Error>,
{
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<T, E>(&'a self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
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<T, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromBer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SetIterator::<T, BerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 =
SetIterator::<T, BerParser, E>::new(&data).collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
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<T, E>(self) -> Result<Vec<T>, E>
where
for<'b> T: FromDer<'b, E>,
E: From<Error>,
T: ToStatic<Owned = T>,
{
match self.content {
Cow::Borrowed(bytes) => SetIterator::<T, DerParser, E>::new(bytes).collect(),
Cow::Owned(data) => {
let v1 =
SetIterator::<T, DerParser, E>::new(&data).collect::<Result<Vec<T>, E>>()?;
let v2 = v1.iter().map(|t| t.to_static()).collect::<Vec<_>>();
Ok(v2)
}
}
}
pub fn into_der_set_of_ref<T, E>(self) -> Result<Vec<T>, E>
where
T: FromDer<'a, E>,
E: From<Error>,
{
match self.content {
Cow::Borrowed(bytes) => SetIterator::<T, DerParser, E>::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<Any<'a>> for Set<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Set<'a>> {
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<Set<'a>> {
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<usize> {
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<usize> {
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<usize> {
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<T, IT>(it: IT) -> SerializeResult<Self>
where
IT: Iterator<Item = T>,
T: ToDer,
T: Tagged,
{
let mut v = Vec::new();
for item in it {
let item_v = <T as ToDer>::to_der_vec(&item)?;
v.extend_from_slice(&item_v);
}
Ok(Set {
content: Cow::Owned(v),
})
}
}

View File

@@ -0,0 +1,142 @@
use crate::*;
use alloc::collections::BTreeSet;
use core::{convert::TryFrom, fmt::Debug};
use self::debug::{trace, trace_generic};
impl<T> Tagged for BTreeSet<T> {
const TAG: Tag = Tag::Set;
}
impl<'a, T> TryFrom<Any<'a>> for BTreeSet<T>
where
T: FromBer<'a>,
T: Ord,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
trace_generic(
core::any::type_name::<Self>(),
"BTreeSet::from_der",
|any| {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
let items =
SetIterator::<T, BerParser>::new(any.data).collect::<Result<BTreeSet<T>>>()?;
Ok(items)
},
any,
)
}
}
impl<T> CheckDerConstraints for BTreeSet<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SetIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for BTreeSet<T>
where
T: FromDer<'a, E>,
T: Ord,
E: From<Error> + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"BTreeSet::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), 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::<T, DerParser, E>::new(any.data)
.collect::<Result<BTreeSet<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, items))
},
bytes,
)
}
}
#[cfg(feature = "std")]
impl<T> ToDer for BTreeSet<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
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<usize> {
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<usize> {
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");
<BTreeSet<u32>>::check_constraints(&any).unwrap();
let h = <BTreeSet<u32>>::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::<u32>::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::<u32>::from_der(&v).unwrap();
assert!(h.iter().eq(h2.iter()));
}
}

View File

@@ -0,0 +1,136 @@
#![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<T> Tagged for HashSet<T> {
const TAG: Tag = Tag::Set;
}
impl<'a, T> TryFrom<Any<'a>> for HashSet<T>
where
T: FromBer<'a>,
T: Hash + Eq,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<HashSet<T>>>()?;
Ok(items)
}
}
impl<T> CheckDerConstraints for HashSet<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SetIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for HashSet<T>
where
T: FromDer<'a, E>,
T: Hash + Eq,
E: From<Error> + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"BTreeSet::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), 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::<T, DerParser, E>::new(any.data)
.collect::<Result<HashSet<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, items))
},
bytes,
)
}
}
impl<T> ToDer for HashSet<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
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<usize> {
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<usize> {
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");
<HashSet<u32>>::check_constraints(&any).unwrap();
let h = <HashSet<u32>>::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::<u32>::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::<u32>::from_der(&v).unwrap();
assert!(h.iter().eq(h2.iter()));
}
}

View File

@@ -0,0 +1,22 @@
pub 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::<Integer, DerParser>::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>;

View File

@@ -0,0 +1,183 @@
use 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<Target = [T]>` and `DerefMut<Target = [T]>`, 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<u32>`, since parsing the serialized bytes could fail
/// sum += *item;
/// }
/// assert_eq!(sum, 9);
///
/// ```
#[derive(Debug, PartialEq)]
pub struct SetOf<T> {
items: Vec<T>,
}
impl<T> SetOf<T> {
/// Builds a `SET OF` from the provided content
#[inline]
pub const fn new(items: Vec<T>) -> Self {
SetOf { items }
}
/// Converts `self` into a vector without clones or allocation.
#[inline]
pub fn into_vec(self) -> Vec<T> {
self.items
}
/// Appends an element to the back of a collection
#[inline]
pub fn push(&mut self, item: T) {
self.items.push(item)
}
}
impl<T> AsRef<[T]> for SetOf<T> {
fn as_ref(&self) -> &[T] {
&self.items
}
}
impl<T> AsMut<[T]> for SetOf<T> {
fn as_mut(&mut self) -> &mut [T] {
&mut self.items
}
}
impl<T> Deref for SetOf<T> {
type Target = [T];
fn deref(&self) -> &Self::Target {
&self.items
}
}
impl<T> DerefMut for SetOf<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.items
}
}
impl<T> From<SetOf<T>> for Vec<T> {
fn from(set: SetOf<T>) -> Self {
set.items
}
}
impl<T> FromIterator<T> for SetOf<T> {
fn from_iter<IT: IntoIterator<Item = T>>(iter: IT) -> Self {
let items = Vec::from_iter(iter);
SetOf::new(items)
}
}
impl<'a, T> TryFrom<Any<'a>> for SetOf<T>
where
T: FromBer<'a>,
{
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self> {
any.tag().assert_eq(Self::TAG)?;
if !any.header.is_constructed() {
return Err(Error::ConstructExpected);
}
let items = SetIterator::<T, BerParser>::new(any.data).collect::<Result<Vec<T>>>()?;
Ok(SetOf::new(items))
}
}
impl<T> CheckDerConstraints for SetOf<T>
where
T: CheckDerConstraints,
{
fn check_constraints(any: &Any) -> Result<()> {
any.tag().assert_eq(Self::TAG)?;
any.header.assert_constructed()?;
for item in SetIterator::<Any, DerParser>::new(any.data) {
let item = item?;
T::check_constraints(&item)?;
}
Ok(())
}
}
/// manual impl of FromDer, so we do not need to require `TryFrom<Any> + CheckDerConstraints`
impl<'a, T, E> FromDer<'a, E> for SetOf<T>
where
T: FromDer<'a, E>,
E: From<Error> + Display + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
trace_generic(
core::any::type_name::<Self>(),
"SetOf::from_der",
|bytes| {
let (rem, any) = trace(core::any::type_name::<Self>(), Any::from_der, bytes)
.map_err(Err::convert)?;
any.header
.assert_tag(Self::TAG)
.map_err(|e| Err::Error(e.into()))?;
let items = SetIterator::<T, DerParser, E>::new(any.data)
.collect::<Result<Vec<T>, E>>()
.map_err(Err::Error)?;
Ok((rem, SetOf::new(items)))
},
bytes,
)
}
}
impl<T> Tagged for SetOf<T> {
const TAG: Tag = Tag::Set;
}
#[cfg(feature = "std")]
impl<T> ToDer for SetOf<T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.items.to_der_len()
}
fn write_der_header(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// 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<usize> {
self.items.write_der_content(writer)
}
}

170
vendor/asn1-rs/src/asn1_types/strings.rs vendored Normal file
View File

@@ -0,0 +1,170 @@
mod 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<str> 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<String> 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<usize> {
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<usize> {
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<usize> {
writer.write(self.data.as_bytes()).map_err(Into::into)
}
}
};
($name:ident) => {
asn1_string!(IMPL $name, stringify!($name));
};
}

View File

@@ -0,0 +1,143 @@
// 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<str> 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<String> 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<BmpString<'a>> {
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::<Vec<_>>();
let s = String::from_utf16(v)?;
let data = Cow::Owned(s);
Ok(BmpString { data })
}
}
impl<'a> core::convert::TryFrom<Any<'a>> for BmpString<'a> {
type Error = Error;
#[inline]
fn try_from(any: Any<'a>) -> Result<BmpString<'a>> {
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<usize> {
// 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<usize> {
// 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<usize> {
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)
}
}

View File

@@ -0,0 +1,15 @@
use 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(())
}
}

View File

@@ -0,0 +1,15 @@
use 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(())
}
}

View File

@@ -0,0 +1,15 @@
use 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(())
}
}

View File

@@ -0,0 +1,19 @@
use 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(())
}
}

View File

@@ -0,0 +1,36 @@
use 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(())
}
}

View File

@@ -0,0 +1,67 @@
use crate::*;
use alloc::borrow::Cow;
use core::convert::TryFrom;
impl<'a> TryFrom<Any<'a>> 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<usize> {
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<usize> {
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<usize> {
writer.write(self.as_bytes()).map_err(Into::into)
}
}

View File

@@ -0,0 +1,65 @@
use crate::*;
#[cfg(not(feature = "std"))]
use alloc::string::String;
use core::convert::TryFrom;
impl<'a> TryFrom<Any<'a>> for String {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<String> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for String {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<String> {
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<usize> {
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<usize> {
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<usize> {
writer.write(self.as_ref()).map_err(Into::into)
}
}

View File

@@ -0,0 +1,19 @@
use 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(())
}
}

View File

@@ -0,0 +1,139 @@
// 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<str> 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<String> for UniversalString<'_> {
fn from(s: String) -> Self {
Self {
data: Cow::Owned(s),
}
}
}
impl<'a> TryFrom<Any<'a>> for UniversalString<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<UniversalString<'a>> {
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<UniversalString<'a>> {
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::<Option<Vec<_>>>()
.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<usize> {
// 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<usize> {
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<usize> {
self.data
.chars()
.try_for_each(|c| writer.write(&(c as u32).to_be_bytes()[..]).map(|_| ()))?;
Ok(self.data.len() * 4)
}
}

View File

@@ -0,0 +1,14 @@
use 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(())
}
}

View File

@@ -0,0 +1,20 @@
use 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(())
}
}

View File

@@ -0,0 +1,19 @@
use 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(())
}
}

128
vendor/asn1-rs/src/asn1_types/tagged.rs vendored Normal file
View File

@@ -0,0 +1,128 @@
use 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::<Integer, Error, Explicit, {Class::APPLICATION}, 0>::from_ber(bytes)
/// .unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
#[derive(Debug, PartialEq, Eq)]
pub struct TaggedValue<T, E, TagKind, const CLASS: u8, const TAG: u32> {
pub(crate) inner: T,
tag_kind: PhantomData<TagKind>,
_e: PhantomData<E>,
}
impl<T, E, TagKind, const CLASS: u8, const TAG: u32> TaggedValue<T, E, TagKind, CLASS, TAG> {
/// 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<T, E, const CLASS: u8, const TAG: u32> TaggedValue<T, E, Explicit, CLASS, TAG> {
/// 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<T, E, const CLASS: u8, const TAG: u32> TaggedValue<T, E, Implicit, CLASS, TAG> {
/// 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<T, E, TagKind, const CLASS: u8, const TAG: u32> AsRef<T>
for TaggedValue<T, E, TagKind, CLASS, TAG>
{
fn as_ref(&self) -> &T {
&self.inner
}
}
impl<T, E, TagKind, const CLASS: u8, const TAG: u32> Tagged
for TaggedValue<T, E, TagKind, CLASS, TAG>
{
const TAG: Tag = Tag(TAG);
}

View File

@@ -0,0 +1,42 @@
use 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::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
pub type ApplicationExplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Explicit, { Class::APPLICATION }, TAG>;
/// 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::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8)));
/// ```
pub type ApplicationImplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Implicit, { Class::APPLICATION }, TAG>;

View File

@@ -0,0 +1,110 @@
use 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::<u32>();
///
/// 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<TagKind, E = Error> {
class: Class,
tag: Tag,
tag_kind: PhantomData<TagKind>,
_e: PhantomData<E>,
}
impl<TagKind, E> Default for TaggedParserBuilder<TagKind, E> {
fn default() -> Self {
Self::new()
}
}
impl<TagKind, E> TaggedParserBuilder<TagKind, E> {
/// Create a default `TaggedParserBuilder` builder
///
/// `TagKind` must be specified as either [`Explicit`] or [`Implicit`]
///
/// ```
/// use asn1_rs::{Explicit, TaggedParserBuilder};
///
/// let builder = TaggedParserBuilder::<Explicit>::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<E> TaggedParserBuilder<Explicit, E> {
/// Create a `TagParser` builder for `EXPLICIT` tagged values
pub const fn explicit() -> Self {
TaggedParserBuilder::new()
}
}
impl<E> TaggedParserBuilder<Implicit, E> {
/// Create a `TagParser` builder for `IMPLICIT` tagged values
pub const fn implicit() -> Self {
TaggedParserBuilder::new()
}
}
impl<TagKind, E> TaggedParserBuilder<TagKind, E> {
/// 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<Error>,
{
move |bytes: &[u8]| TaggedParser::<TagKind, T, E>::parse_ber(self.class, self.tag, bytes)
}
}
impl<TagKind, E> TaggedParserBuilder<TagKind, E> {
/// 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<Error>,
{
move |bytes: &[u8]| TaggedParser::<TagKind, T, E>::parse_der(self.class, self.tag, bytes)
}
}

View File

@@ -0,0 +1,262 @@
use crate::*;
use core::convert::TryFrom;
use core::marker::PhantomData;
impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom<Any<'a>>
for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: FromBer<'a, E>,
E: From<Error>,
{
type Error = E;
fn try_from(any: Any<'a>) -> Result<Self, E> {
Self::try_from(&any)
}
}
impl<'a, 'b, T, E, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>>
for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: FromBer<'a, E>,
E: From<Error>,
{
type Error = E;
fn try_from(any: &'b Any<'a>) -> Result<Self, E> {
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<T, E, Explicit, CLASS, TAG>
where
T: FromDer<'a, E>,
E: From<Error>,
{
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<T, E, const CLASS: u8, const TAG: u32> CheckDerConstraints
for TaggedValue<T, E, Explicit, CLASS, TAG>
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<T, E, const CLASS: u8, const TAG: u32> ToDer for TaggedValue<T, E, Explicit, CLASS, TAG>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
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<usize> {
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<usize> {
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::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
pub type TaggedExplicit<T, E, const TAG: u32> = TaggedValue<T, E, Explicit, CONTEXT_SPECIFIC, TAG>;
// 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<F>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<F>(
class: Class,
tag: u32,
bytes: &'a [u8],
op: F,
) -> ParseResult<'a, T, E>
where
F: FnOnce(&'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<Error>,
{
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<Error>,
{
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<T> 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<T> ToDer for TaggedParser<'_, Explicit, T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
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<usize> {
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<usize> {
self.inner.write_der(writer)
}
}

View File

@@ -0,0 +1,101 @@
use 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<Tag>,
TaggedParser<'a, Explicit, T, E>: FromDer<'a, E>,
E: From<Error>,
{
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<Error>,
IntoTag: Into<Tag>,
{
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<Tag>,
// T: TryFrom<Any<'a>, Error = Error> + Tagged,
TaggedParser<'a, Implicit, T, E>: FromDer<'a, E>,
E: From<Error>,
{
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<Error>,
IntoTag: Into<Tag>,
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<Error>,
{
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))
}
}

View File

@@ -0,0 +1,287 @@
use crate::*;
use core::convert::TryFrom;
use core::marker::PhantomData;
impl<'a, T, E, const CLASS: u8, const TAG: u32> TryFrom<Any<'a>>
for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
type Error = E;
fn try_from(any: Any<'a>) -> Result<Self, E> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b, E, T, const CLASS: u8, const TAG: u32> TryFrom<&'b Any<'a>>
for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
type Error = E;
fn try_from(any: &'b Any<'a>) -> Result<Self, E> {
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<T, E, Implicit, CLASS, TAG>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
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<T, E, const CLASS: u8, const TAG: u32> CheckDerConstraints
for TaggedValue<T, E, Implicit, CLASS, TAG>
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<T, E, const CLASS: u8, const TAG: u32> ToDer for TaggedValue<T, E, Implicit, CLASS, TAG>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.inner.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
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<usize> {
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<usize> {
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::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2)));
/// ```
pub type TaggedImplicit<T, E, const TAG: u32> = TaggedValue<T, E, Implicit, CONTEXT_SPECIFIC, TAG>;
impl<'a, T, E> FromBer<'a, E> for TaggedParser<'a, Implicit, T, E>
where
T: TryFrom<Any<'a>, Error = E>,
T: Tagged,
E: From<Error>,
{
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<T, E> 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<Any<'a>, Error = E>,
T: CheckDerConstraints,
T: Tagged,
E: From<Error>,
{
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<T> 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<T> ToDer for TaggedParser<'_, Implicit, T>
where
T: ToDer,
{
fn to_der_len(&self) -> Result<usize> {
self.inner.to_der_len()
}
fn write_der(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
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<usize> {
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<usize> {
self.inner.write_der_content(writer)
}
}

View File

@@ -0,0 +1,239 @@
use 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<T>, E>
where
F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<T>, E>
where
F: Fn(Header, &'a [u8]) -> ParseResult<'a, T, E>,
E: From<Error>,
{
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<Tag> 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<u32> 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::<Integer, Error, 0>::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::<Integer, Error, 0>::from_ber(&[]).unwrap();
/// assert_eq!(tagged, None);
/// let (_, tagged) = OptTaggedExplicit::<Integer, Error, 1>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, None);
/// ```
pub type OptTaggedExplicit<T, E, const TAG: u32> = Option<TaggedExplicit<T, E, TAG>>;
/// 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::<Integer, Error, 0>::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::<Integer, Error, 0>::from_ber(&[]).unwrap();
/// assert_eq!(tagged, None);
/// ```
pub type OptTaggedImplicit<T, E, const TAG: u32> = Option<TaggedImplicit<T, E, TAG>>;

View File

@@ -0,0 +1,78 @@
use 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<TagKind>,
pub(crate) _e: PhantomData<E>,
}
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<TagKind, T, E> AsRef<T> 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<Error>,
{
pub fn parse_ber(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, t) = TaggedParser::<TagKind, T, E>::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<Error>,
{
pub fn parse_der(class: Class, tag: Tag, bytes: &'a [u8]) -> ParseResult<'a, Self, E> {
let (rem, t) = TaggedParser::<TagKind, T, E>::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<TagKind, T, E> DynTagged for TaggedParser<'_, TagKind, T, E> {
fn tag(&self) -> Tag {
self.tag()
}
}

View File

@@ -0,0 +1,42 @@
use 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::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::explicit(Integer::from(2)));
/// ```
pub type PrivateExplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Explicit, { Class::PRIVATE }, TAG>;
/// 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::<Integer, Error, 0>::from_ber(bytes).unwrap();
/// assert_eq!(tagged, TaggedValue::implicit(Integer::from(2_u8)));
/// ```
pub type PrivateImplicit<T, E, const TAG: u32> =
TaggedValue<T, E, Implicit, { Class::PRIVATE }, TAG>;

221
vendor/asn1-rs/src/asn1_types/utctime.rs vendored Normal file
View File

@@ -0,0 +1,221 @@
use 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<Self> {
// 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<OffsetDateTime> {
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<OffsetDateTime> {
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<i64> {
let dt = self.0.to_datetime()?;
Ok(dt.unix_timestamp())
}
}
impl<'a> TryFrom<Any<'a>> for UtcTime {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<UtcTime> {
TryFrom::try_from(&any)
}
}
impl<'a, 'b> TryFrom<&'b Any<'a>> for UtcTime {
type Error = Error;
fn try_from(any: &'b Any<'a>) -> Result<UtcTime> {
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<usize> {
// 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<usize> {
// 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<usize> {
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)
}
}

3
vendor/asn1-rs/src/ber/mod.rs vendored Normal file
View File

@@ -0,0 +1,3 @@
mod parser;
pub use parser::*;

169
vendor/asn1-rs/src/ber/parser.rs vendored Normal file
View File

@@ -0,0 +1,169 @@
use 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<u64, Error> {
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)));
}
}
);
);

94
vendor/asn1-rs/src/class.rs vendored Normal file
View File

@@ -0,0 +1,94 @@
use 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<u8> for Class {
type Error = BerClassFromIntError;
#[inline]
fn try_from(value: u8) -> Result<Self, Self::Error> {
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());
}
}

43
vendor/asn1-rs/src/const_int.rs vendored Normal file
View File

@@ -0,0 +1,43 @@
use 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,
}
}
}

109
vendor/asn1-rs/src/datetime.rs vendored Normal file
View File

@@ -0,0 +1,109 @@
use 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<u16>,
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<u16>,
tz: ASN1TimeZone,
) -> Self {
ASN1DateTime {
year,
month,
day,
hour,
minute,
second,
millisecond,
tz,
}
}
#[cfg(feature = "datetime")]
fn to_time_datetime(
&self,
) -> core::result::Result<OffsetDateTime, time::error::ComponentRange> {
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<OffsetDateTime> {
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<u8> {
if hi.is_ascii_digit() && lo.is_ascii_digit() {
Ok((hi - b'0') * 10 + (lo - b'0'))
} else {
Err(tag.invalid_value("expected digit"))
}
}

264
vendor/asn1-rs/src/debug.rs vendored Normal file
View File

@@ -0,0 +1,264 @@
#![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!("... <continued>");
}
}
#[cfg(not(feature = "debug"))]
#[inline]
pub fn trace_generic<F, I, O, E>(_msg: &str, _fname: &str, f: F, input: I) -> Result<O, E>
where
F: Fn(I) -> Result<O, E>,
{
f(input)
}
#[cfg(feature = "debug")]
pub fn trace_generic<F, I, O, E>(msg: &str, fname: &str, f: F, input: I) -> Result<O, E>
where
F: Fn(I) -> Result<O, E>,
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::<T>()
);
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!(<Vec<u16>>::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) = <Vec<u32>>::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 _ = <SequenceOf<u32>>::from_ber(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <Vec<u32>>::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!(<bool>::from_der(&hex!("01 01 ff")).is_ok());
eprintln!("** second test fails when parsing ANY (eof)**");
assert!(<bool>::from_der(&hex!("01 02 ff")).is_err());
eprintln!("** second test fails when checking DER constraints**");
assert!(<bool>::from_der(&hex!("01 01 f0")).is_err());
eprintln!("** second test fails during TryFrom**");
assert!(<bool>::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) = <Vec<u32>>::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 _ = <Vec<bool>>::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 _ = <Vec<bool>>::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 _ = <SequenceOf<u32>>::from_der(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <Vec<u32>>::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 _ = <BTreeSet<bool>>::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 _ = <SetOf<u32>>::from_der(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <BTreeSet<u32>>::from_der(input).expect_err("parsing should fail");
eprintln!("--");
let _ = <HashSet<u32>>::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");
}
}

409
vendor/asn1-rs/src/derive.rs vendored Normal file
View File

@@ -0,0 +1,409 @@
/// # 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<Any>`](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:
/// <pre>
/// S ::= SEQUENCE {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// 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<Any>`](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:
/// <pre>
/// S ::= SEQUENCE {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// 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<Any>`](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:
/// <pre>
/// S ::= SET {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// 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<Any>`](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:
/// <pre>
/// S ::= SEt {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// 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<Any>`](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<Any>` trait.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 object:
/// <pre>
/// MyInt ::= INTEGER(0..2^32)
/// </pre>
///
/// 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<Any>`](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<Any>` and `FromDer` traits.
///
/// See [`derive`](crate::doc::derive) documentation for more examples and documentation.
///
/// ## Examples
///
/// To parse the following ASN.1 object:
/// <pre>
/// MyInt ::= INTEGER(0..2^32)
/// </pre>
///
/// 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:
/// <pre>
/// S ::= SEQUENCE {
/// a INTEGER(0..2^32),
/// b INTEGER(0..2^16),
/// c INTEGER(0..2^16),
/// }
/// </pre>
///
/// 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;

11
vendor/asn1-rs/src/doc/mod.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
//! 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 {}

204
vendor/asn1-rs/src/error.rs vendored Normal file
View File

@@ -0,0 +1,204 @@
#![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<Tag>, actual: Tag },
/// Unexpected Class (expected: {expected:?}, actual: {actual:?})
UnexpectedClass {
expected: Option<Class>,
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<Class>, actual: Class) -> Self {
Self::UnexpectedClass { expected, actual }
}
/// Build an error from the provided unexpected tag
#[inline]
pub const fn unexpected_tag(expected: Option<Tag>, 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<Error> for nom::Err<Error> {
fn from(e: Error) -> Self {
nom::Err::Error(e)
}
}
impl From<str::Utf8Error> for Error {
fn from(_: str::Utf8Error) -> Self {
Error::StringInvalidCharset
}
}
impl From<string::FromUtf8Error> for Error {
fn from(_: string::FromUtf8Error) -> Self {
Error::StringInvalidCharset
}
}
impl From<string::FromUtf16Error> for Error {
fn from(_: string::FromUtf16Error) -> Self {
Error::StringInvalidCharset
}
}
impl From<nom::Err<Error>> for Error {
fn from(e: nom::Err<Error>) -> Self {
match e {
nom::Err::Incomplete(n) => Self::Incomplete(n),
nom::Err::Error(e) | nom::Err::Failure(e) => e,
}
}
}
impl<I, E> FromExternalError<I, E> 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, F>(e: nom::Err<E>) -> F
where
F: From<E> + From<Error>,
{
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<T, E = Error> = core::result::Result<T, E>;
/// 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<T> = std::result::Result<T, SerializeError>;

504
vendor/asn1-rs/src/header.rs vendored Normal file
View File

@@ -0,0 +1,504 @@
use 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<Cow<'a, [u8]>>,
}
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<Cow<'a, [u8]>>) -> 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<Tag> 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<Cow<'static, [u8]>> =
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
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<usize> {
Ok(0)
}
fn write_der_raw(&self, writer: &mut dyn std::io::Write) -> SerializeResult<usize> {
// 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<Header<'a>> 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..]);
}
}

180
vendor/asn1-rs/src/length.rs vendored Normal file
View File

@@ -0,0 +1,180 @@
use 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<usize> {
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<usize> for Length {
fn from(l: usize) -> Self {
Length::Definite(l)
}
}
impl ops::Add<Length> 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<usize> 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<usize> 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<usize> {
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<usize> {
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<usize> {
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);
}
}

218
vendor/asn1-rs/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,218 @@
//! [![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<u8>` 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;

76
vendor/asn1-rs/src/tag.rs vendored Normal file
View File

@@ -0,0 +1,76 @@
use 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<u32> for Tag {
fn from(v: u32) -> Self {
Tag(v)
}
}

90
vendor/asn1-rs/src/tostatic.rs vendored Normal file
View File

@@ -0,0 +1,90 @@
use 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) -> <Self as ToStatic>::Owned {
Cow::Owned(self.to_string())
}
}
impl ToStatic for Cow<'_, [u8]> {
type Owned = Cow<'static, [u8]>;
fn to_static(&self) -> <Self as ToStatic>::Owned {
Cow::Owned(self.to_vec())
}
}
macro_rules! impl_tostatic_primitive {
($t:ty) => {
impl ToStatic for $t {
type Owned = $t;
fn to_static(&self) -> <Self as ToStatic>::Owned {
self.clone()
}
}
};
($t:ty => $to:ty, $closure:expr) => {
impl ToStatic for $t {
type Owned = $to;
fn to_static(&self) -> <Self as ToStatic>::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<T> ToStatic for &'_ T
where
T: ToStatic,
{
type Owned = T::Owned;
fn to_static(&self) -> Self::Owned {
(*self).to_static()
}
}
impl<T> ToStatic for Option<T>
where
T: ToStatic,
{
type Owned = Option<T::Owned>;
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<T> ToStatic for Box<T>
where
T: ToStatic,
{
type Owned = Box<T::Owned>;
fn to_static(&self) -> Self::Owned {
Box::new(self.as_ref().to_static())
}
}
};

354
vendor/asn1-rs/src/traits.rs vendored Normal file
View File

@@ -0,0 +1,354 @@
use 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<T> Tagged for &'_ T
where
T: Tagged,
{
const TAG: Tag = T::TAG;
}
pub trait DynTagged {
fn tag(&self) -> Tag;
}
impl<T> 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<Any>`] 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<Any<'a>> for MyType {
/// type Error = asn1_rs::Error;
///
/// fn try_from(any: Any<'a>) -> Result<MyType> {
/// 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<Any<'a>, Error = E>,
E: From<Error>,
{
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<Any>`] + [`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<Any>`] 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<Any<'a>> for MyType {
/// type Error = asn1_rs::Error;
///
/// fn try_from(any: Any<'a>) -> Result<MyType> {
/// 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<Any>`] 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<T>`),
/// because the default implementation adds a constraint on `T` to implement also `TryFrom<Any>`
/// 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<Any<'a>, Error = E>,
T: CheckDerConstraints,
T: DerAutoDerive,
E: From<Error> + Display + Debug,
{
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, T, E> {
trace_generic(
core::any::type_name::<T>(),
"T::from_der",
|bytes| {
let (i, any) = trace(core::any::type_name::<T>(), parse_der_any, bytes)
.map_err(nom::Err::convert)?;
<T as CheckDerConstraints>::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<usize>;
/// Write the DER encoded representation to a newly allocated `Vec<u8>`.
fn to_der_vec(&self) -> SerializeResult<Vec<u8>> {
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<Vec<u8>> {
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<usize> {
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<usize>;
/// Attempt to write the DER content (all except header) to this writer.
fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize>;
/// 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<usize> {
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<usize> {
(*self).to_der_len()
}
fn write_der_header(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
(*self).write_der_header(writer)
}
fn write_der_content(&self, writer: &mut dyn Write) -> SerializeResult<usize> {
(*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::*;

518
vendor/asn1-rs/tests/ber.rs vendored Normal file
View File

@@ -0,0 +1,518 @@
use 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) = <f32>::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) = <f64>::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) = <Vec<u32>>::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) = <Sequence>::from_ber(input).expect("parsing failed");
let v = result
.ber_sequence_of::<u32, _>()
.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::<Result<Vec<u32>>>()
.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) = <Set>::from_ber(input).expect("parsing failed");
let v = result.ber_set_of::<u32, _>().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::<Result<Vec<u32>>>()
.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::<u32, Error>();
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::<u32>::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::<u32>::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::<TaggedExplicit<u32, Error, 0>>::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::<TaggedExplicit<u32, Error, 1>>::from_ber(input).expect("parsing failed");
assert!(result.is_none());
assert_eq!(rem, input);
// using OptTaggedExplicit
let (rem, result) =
OptTaggedExplicit::<u32, Error, 0>::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, &[]);
}

6
vendor/asn1-rs/tests/compile_tests.rs vendored Normal file
View File

@@ -0,0 +1,6 @@
#[test]
fn compile_fail() {
let t = trybuild::TestCases::new();
t.pass("tests/run-pass/*.rs");
t.compile_fail("tests/compile-fail/*.rs");
}

101
vendor/asn1-rs/tests/cov.rs vendored Normal file
View File

@@ -0,0 +1,101 @@
#![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 = <asn1_rs::Error as ParseError<_>>::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!();
}
}

661
vendor/asn1-rs/tests/der.rs vendored Normal file
View File

@@ -0,0 +1,661 @@
use 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) = <bool>::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) = <Option<Enumerated>>::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) = <f32>::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) = <f64>::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) = <Vec<u32>>::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::<Result<Vec<u32>>>()
.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::<u32, Error>();
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) = <BTreeSet<u32>>::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) = <Set>::from_der(input).expect("parsing failed");
let v = result.der_set_of::<u32, _>().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::<Result<Vec<u32>>>()
.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) = <Option<u8>>::from_der(input).expect("parsing failed");
assert_eq!(result, Some(2));
assert_eq!(rem, &[0xff, 0xff]);
// non-fatal error
let (rem, result) = <Option<Ia5String>>::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 = <Option<u8>>::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::<u32, Error, 0>::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::<u32, Error, Explicit, { Class::CONTEXT_SPECIFIC }, 0>::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::<Explicit, u32>::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::<TaggedExplicit<u32, Error, 0>>::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::<TaggedExplicit<u32, Error, 1>>::from_der(input).expect("parsing failed");
assert!(result.is_none());
assert_eq!(rem, input);
// using OptTaggedExplicit
let (rem, result) =
OptTaggedExplicit::<u32, Error, 0>::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::<Implicit, &str>::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::<TaggedImplicit<&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");
let (rem, result) =
Option::<TaggedImplicit<&str, Error, 0>>::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::<Implicit, Ia5String>::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::<Implicit, Ia5String>::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::<Ia5String>();
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);
}

View File

@@ -0,0 +1,26 @@
#![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<Sequence<'a>, 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());
}

View File

@@ -0,0 +1,71 @@
use asn1_rs::*;
#[derive(DerSequence, Debug, PartialEq)]
struct TestBool {
a: u16,
b: Option<bool>,
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<u32>,
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);
}

View File

@@ -0,0 +1,16 @@
#![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}");
}

110
vendor/asn1-rs/tests/krb5.rs vendored Normal file
View File

@@ -0,0 +1,110 @@
#![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<String>,
}
#[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<KerberosString<'a>>;
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<usize> {
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<usize> {
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<usize> {
// 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::<Vec<_>>()
.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));
}

549
vendor/asn1-rs/tests/to_der.rs vendored Normal file
View File

@@ -0,0 +1,549 @@
#![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: T, expected: &[u8])
where
T: ToDer + std::fmt::Debug + Eq,
for<'a> T: TryFrom<Integer<'a>, 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<T>::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<T>::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<T>::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::<Explicit, u32>::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::<u32, Error, 1>::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::<Implicit, u32>::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::<u32, Error, 1>::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::<Integer, Error, 1>::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");
}

72
vendor/asn1-rs/tests/to_static.rs vendored Normal file
View File

@@ -0,0 +1,72 @@
use 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<T>(_arg: &T)
where
T: 'static,
{
}

View File

@@ -0,0 +1,32 @@
#![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);
}

View File

@@ -0,0 +1,23 @@
#![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);
}

158
vendor/asn1-rs/tests/x509.rs vendored Normal file
View File

@@ -0,0 +1,158 @@
//! 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<RelativeDistinguishedName<'a>>,
}
impl<'a> FromDer<'a> for Name<'a> {
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> {
let (rem, rdn_sequence) = <Vec<RelativeDistinguishedName>>::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<AttributeTypeAndValue<'a>>,
}
impl<'a> FromDer<'a> for RelativeDistinguishedName<'a> {
fn from_der(bytes: &'a [u8]) -> ParseResult<'a, Self> {
let (rem, set) = SetOf::<AttributeTypeAndValue>::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<Any<'a>> for DirectoryString {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self, Self::Error> {
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);
}