asn1-0.17.0/.cargo_vcs_info.json0000644000000001360000000000100117630ustar { "git": { "sha1": "bc49fc61004c640054d34aeff5185ec6546330ca" }, "path_in_vcs": "" }asn1-0.17.0/.clusterfuzzlite/Dockerfile000064400000000000000000000001741046102023000161030ustar 00000000000000FROM gcr.io/oss-fuzz-base/base-builder-rust:v1 COPY . $SRC/rust-asn1 WORKDIR rust-asn1 COPY .clusterfuzzlite/build.sh $SRC/ asn1-0.17.0/.clusterfuzzlite/build.sh000075500000000000000000000004061046102023000155450ustar 00000000000000#!/bin/bash -eu cargo fuzz build -O --debug-assertions FUZZ_TARGET_OUTPUT_DIR=fuzz/target/x86_64-unknown-linux-gnu/release for f in fuzz/fuzz_targets/*.rs do FUZZ_TARGET_NAME=$(basename ${f%.*}) cp $FUZZ_TARGET_OUTPUT_DIR/$FUZZ_TARGET_NAME $OUT/ done asn1-0.17.0/.clusterfuzzlite/project.yaml000064400000000000000000000000171046102023000164370ustar 00000000000000language: rust asn1-0.17.0/.github/dependabot.yml000064400000000000000000000004331046102023000147430ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily allow: # Also update indirect dependencies - dependency-type: all - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" asn1-0.17.0/.github/workflows/ci.yml000064400000000000000000000110601046102023000152640ustar 00000000000000on: pull_request: {} push: branches: main name: Continuous integration permissions: contents: read jobs: lint: runs-on: ubuntu-latest strategy: matrix: RUST: - nightly steps: - uses: actions/checkout@v4.1.7 with: persist-credentials: false - uses: dtolnay/rust-toolchain@1482605bfc5719782e1267fd0c0cc350fe7646b8 with: toolchain: ${{ matrix.RUST }} components: rustfmt, clippy - uses: actions/cache@v4.0.2 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-${{ matrix.RUST }}-cargo-2-${{ hashFiles('**/Cargo.toml') }} - run: cargo fmt --all -- --check - run: cargo clippy --all-targets -- -D warnings - run: cargo clippy --all-targets --all-features -- -D warnings ci: runs-on: ubuntu-latest strategy: matrix: RUST: # MSRV - VERSION: "1.59.0" FLAGS: "" - VERSION: stable FLAGS: "" - VERSION: stable FLAGS: "--no-default-features --features std" - VERSION: stable FLAGS: "--no-default-features" SKIP_TESTS: true - VERSION: beta FLAGS: "" - VERSION: beta FLAGS: "--no-default-features --features std" - VERSION: nightly FLAGS: "" - VERSION: nightly FLAGS: "--no-default-features --features std" - VERSION: nightly FLAGS: "-Z minimal-versions" steps: - uses: actions/checkout@v4.1.7 with: persist-credentials: false - uses: dtolnay/rust-toolchain@1482605bfc5719782e1267fd0c0cc350fe7646b8 with: toolchain: ${{ matrix.RUST.VERSION }} - uses: actions/cache@v4.0.2 with: path: | ~/.cargo/bin/ ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-${{ matrix.RUST.VERSION }}-cargo-2-${{ hashFiles('**/Cargo.toml') }} - run: cargo check --tests ${{ matrix.RUST.FLAGS }} - run: cargo test ${{ matrix.RUST.FLAGS }} if: "${{ !matrix.RUST.SKIP_TESTS }}" coverage: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4.1.7 with: persist-credentials: false - uses: dtolnay/rust-toolchain@1482605bfc5719782e1267fd0c0cc350fe7646b8 with: toolchain: 1.76.0 components: llvm-tools-preview - run: sudo apt update && sudo apt-get install -y lcov - uses: actions/cache@v4.0.2 id: cargo-cache with: path: | ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-5-${{ hashFiles('**/Cargo.toml') }} - run: cargo test env: RUSTFLAGS: "-Cinstrument-coverage" LLVM_PROFILE_FILE: "rust-cov/cov-%m-%p.profraw" - name: Compute coverage HTML run: | set -xe LIBDIR=$(rustc --print target-libdir) $LIBDIR/../bin/llvm-profdata merge -sparse rust-cov/*.profraw -o cargo-test-rust-cov.profdata $LIBDIR/../bin/llvm-cov export \ $( \ for file in \ $( \ RUSTFLAGS="-Cinstrument-coverage" \ cargo test --tests --no-run --message-format=json \ | jq -r "select(.profile.test == true) | .filenames[]" \ ); \ do \ printf "%s %s " -object $file; \ done \ ) \ -instr-profile=cargo-test-rust-cov.profdata \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/rustc/' \ --ignore-filename-regex='/.rustup/toolchains/' --format=lcov > cargo-test.lcov genhtml cargo-test.lcov -o coverage-html | tee output.log - uses: actions/upload-artifact@v4.3.4 with: name: coverage-html path: coverage-html - name: Check coverage run: | import re with open("output.log") as f: [line] = [l for l in f if l.startswith(" lines......:")] coverage = float(re.search("([\d\.]+)%", line).group(1)) if coverage < 100: raise ValueError(f"Coverage too low: {coverage}") shell: python asn1-0.17.0/.github/workflows/fuzz-build.yml000064400000000000000000000010231046102023000167620ustar 00000000000000name: ClusterFuzzLite continuous builds on: push: branches: - main permissions: read-all jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: sanitizer: - address steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: language: rust sanitizer: ${{ matrix.sanitizer }} upload-build: true asn1-0.17.0/.github/workflows/fuzz-cron.yml000064400000000000000000000026731046102023000166400ustar 00000000000000name: ClusterFuzzLite batch fuzzing on: workflow_dispatch: schedule: # Run daily - cron: "0 */24 * * *" push: branches: - main permissions: read-all jobs: cron: runs-on: ubuntu-latest strategy: fail-fast: false matrix: sanitizer: - address steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: language: rust sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 3600 mode: 'batch' parallel-fuzzing: true sanitizer: ${{ matrix.sanitizer }} coverage: runs-on: ubuntu-latest steps: - name: Build Fuzzers id: build uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: language: rust sanitizer: coverage - name: Run Fuzzers id: run uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 600 mode: 'coverage' sanitizer: 'coverage' asn1-0.17.0/.github/workflows/fuzz-pr.yml000064400000000000000000000017341046102023000163150ustar 00000000000000name: ClusterFuzzLite PR fuzzing on: pull_request: {} permissions: read-all jobs: pr: runs-on: ubuntu-latest concurrency: group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} cancel-in-progress: true strategy: fail-fast: false matrix: sanitizer: - address steps: - name: Build Fuzzers (${{ matrix.sanitizer }}) id: build uses: google/clusterfuzzlite/actions/build_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: language: rust github-token: ${{ secrets.GITHUB_TOKEN }} sanitizer: ${{ matrix.sanitizer }} - name: Run Fuzzers (${{ matrix.sanitizer }}) id: run uses: google/clusterfuzzlite/actions/run_fuzzers@884713a6c30a92e5e8544c39945cd7cb630abcd1 with: github-token: ${{ secrets.GITHUB_TOKEN }} fuzz-seconds: 30 mode: 'code-change' sanitizer: ${{ matrix.sanitizer }} asn1-0.17.0/.gitignore000064400000000000000000000000221046102023000125350ustar 00000000000000target Cargo.lock asn1-0.17.0/Cargo.lock0000644000000030030000000000100077320ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "asn1" version = "0.17.0" dependencies = [ "asn1_derive", "libc", ] [[package]] name = "asn1_derive" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3df30ecdcaf8338675a1413460a1b11df89789e1fcc6a10dc52f6e38b6982aa2" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "proc-macro2" version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] [[package]] name = "syn" version = "2.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" asn1-0.17.0/Cargo.toml0000644000000023750000000000100077700ustar # 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 = "2021" rust-version = "1.59.0" name = "asn1" version = "0.17.0" authors = ["Alex Gaynor "] build = false autobins = false autoexamples = false autotests = false autobenches = false description = "ASN.1 (DER) parser and writer for Rust." readme = "README.md" keywords = ["asn1"] license = "BSD-3-Clause" repository = "https://github.com/alex/rust-asn1" [lib] name = "asn1" path = "src/lib.rs" [[example]] name = "no_std" path = "examples/no_std.rs" [[test]] name = "derive_test" path = "tests/derive_test.rs" [[test]] name = "oid_tests" path = "tests/oid_tests.rs" [[test]] name = "roundtrip_tests" path = "tests/roundtrip_tests.rs" [dependencies.asn1_derive] version = "0.17.0" [dev-dependencies.libc] version = "0.2.11" [features] default = ["std"] std = [] asn1-0.17.0/Cargo.toml.orig000064400000000000000000000007641046102023000134510ustar 00000000000000[package] name = "asn1" version = "0.17.0" authors = ["Alex Gaynor "] repository = "https://github.com/alex/rust-asn1" keywords = ["asn1"] license = "BSD-3-Clause" description = "ASN.1 (DER) parser and writer for Rust." edition = "2021" # This specifies the MSRV rust-version = "1.59.0" [features] default = ["std"] std = [] [dependencies] asn1_derive = { path = "asn1_derive/", version = "0.17.0" } [dev-dependencies] libc = "0.2.11" [workspace] members = ["asn1_derive"] asn1-0.17.0/LICENSE000064400000000000000000000030041046102023000115550ustar 00000000000000Copyright (c) Alex Gaynor and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of rust-asn1 nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. asn1-0.17.0/README.md000064400000000000000000000012611046102023000120320ustar 00000000000000# rust-asn1 [![Dependency Status][deps-rs-image]][deps-rs-link] [![Documentation][docs-rs-image]][docs-rs-link] This is a Rust library for parsing and generating ASN.1 data (DER only). ## Installation Add `asn1` to the `[dependencies]` section of your `Cargo.toml`: ```toml [dependencies] asn1 = "0.17" ``` Builds on Rust 1.59.0 and newer. `rust-asn1` is compatible with `#![no_std]` environments: ```toml asn1 = { version = "0.17", default-features = false } ``` [deps-rs-image]: https://deps.rs/repo/github/alex/rust-asn1/status.svg [deps-rs-link]: https://deps.rs/repo/github/alex/rust-asn1 [docs-rs-image]: https://docs.rs/asn1/badge.svg [docs-rs-link]: https://docs.rs/asn1/ asn1-0.17.0/clippy.toml000064400000000000000000000003571046102023000127550ustar 00000000000000# `ParseError` is larger (136 bytes) than the default clippy limit (128 bytes) # for error variants; this allows it to pass while catching any future # unintended growth. Note that the limit below is exclusive. large-error-threshold = 137 asn1-0.17.0/examples/no_std.rs000064400000000000000000000020111046102023000142170ustar 00000000000000#![no_std] fn main() { let data = b"\x30\x06\x02\x01\x01\x02\x01\x03"; let result: asn1::ParseResult<_> = asn1::parse(data, |d| { d.read_element::()? .parse(|d| Ok((d.read_element::()?, d.read_element::()?))) }); // Using libc::printf because println! isn't no_std! match result { Ok((r, s)) => unsafe { libc::printf(b"r=%ld, s=%ld\n\x00".as_ptr() as *const libc::c_char, r, s) }, Err(_) => unsafe { libc::printf("Error\n\x00".as_ptr() as *const libc::c_char) }, }; let computed = asn1::write(|w| { w.write_element(&asn1::SequenceWriter::new(&|w: &mut asn1::Writer| { w.write_element(&1i64)?; w.write_element(&3i64)?; Ok(()) })) }) .unwrap(); unsafe { libc::printf( "Original length: %ld\nComputed length: %ld\n\x00".as_ptr() as *const libc::c_char, data.len() as i64, computed.len() as i64, ); } } asn1-0.17.0/src/base128.rs000064400000000000000000000043141046102023000130570ustar 00000000000000use crate::parser::{ParseError, ParseErrorKind, ParseResult}; pub(crate) fn read_base128_int(mut data: &[u8]) -> ParseResult<(u32, &[u8])> { let mut ret = 0u32; for i in 0..5 { let b = match data.first() { Some(b) => *b, None => return Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), }; data = &data[1..]; if ret > u32::MAX >> 7 { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } ret <<= 7; ret |= u32::from(b & 0x7f); // Integers must be minimally encoded. `i == 0 && 0x80` would mean // that the first byte had a value of 0, which is non-minimal. if i == 0 && b == 0x80 { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } if b & 0x80 == 0 { return Ok((ret, data)); } } Err(ParseError::new(ParseErrorKind::InvalidValue)) } pub(crate) fn base128_length(n: u32) -> usize { // Equivalent to: let bits = if n != 0 { 32 - n.leading_zeros() } else { 1 }; let bits = u32::BITS - (n | 1).leading_zeros(); let bytes = (bits + 6) / 7; bytes as usize } pub(crate) fn write_base128_int(mut data: &mut [u8], n: u32) -> Option { let length = base128_length(n); if data.len() < length { return None; } if n == 0 { data[0] = 0; return Some(1); } for i in (0..length).rev() { let mut o = (n >> (i * 7)) as u8; o &= 0x7f; if i != 0 { o |= 0x80; } data[0] = o; data = &mut data[1..]; } Some(length) } #[cfg(test)] mod tests { use super::{read_base128_int, write_base128_int}; #[test] fn test_read_overflow() { let buf = [0x90, 0x80, 0x80, 0x80, 0x0]; let result = read_base128_int(&buf); assert!(result.is_err()); } #[test] fn test_roundtrip() { for i in [0, 10, u32::MAX] { let mut buf = [0; 16]; let length = write_base128_int(&mut buf, i).unwrap(); let (val, remainder) = read_base128_int(&buf[..length]).unwrap(); assert_eq!(i, val); assert!(remainder.is_empty()); } } } asn1-0.17.0/src/bit_string.rs000064400000000000000000000074421046102023000140630ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::vec::Vec; /// Represents an ASN.1 `BIT STRING` whose contents is borrowed. #[derive(Debug, PartialEq, Clone, Hash, Eq)] pub struct BitString<'a> { data: &'a [u8], padding_bits: u8, } impl<'a> BitString<'a> { pub fn new(data: &'a [u8], padding_bits: u8) -> Option> { if padding_bits > 7 || (data.is_empty() && padding_bits != 0) { return None; } if padding_bits > 0 && data[data.len() - 1] & ((1 << padding_bits) - 1) != 0 { return None; } Some(BitString { data, padding_bits }) } /// Returns a sequence of bytes representing the data in the `BIT STRING`. Padding bits will /// always be 0. pub fn as_bytes(&self) -> &'a [u8] { self.data } /// Returns the number of padding bits. Will always be in [0, 8). pub fn padding_bits(&self) -> u8 { self.padding_bits } /// Returns whether the requested bit is set. Padding bits will always return false and /// asking for bits that exceed the length of the bit string will also return false. pub fn has_bit_set(&self, n: usize) -> bool { let idx = n / 8; let v = 1 << (7 - (n & 0x07)); if self.data.len() < (idx + 1) { false } else { self.data[idx] & v != 0 } } } /// Represents an ASN.1 `BIT STRING` whose contents owned. When used to parse /// data this will allocate. #[derive(Debug, PartialEq, Clone, Hash, Eq)] pub struct OwnedBitString { data: Vec, padding_bits: u8, } impl OwnedBitString { pub fn new(data: Vec, padding_bits: u8) -> Option { BitString::new(&data, padding_bits)?; Some(OwnedBitString { data, padding_bits }) } pub fn as_bitstring(&self) -> BitString<'_> { BitString::new(&self.data, self.padding_bits).unwrap() } } #[cfg(test)] mod tests { use crate::{BitString, OwnedBitString}; use alloc::vec; #[test] fn test_bitstring_new() { assert_eq!(BitString::new(b"abc", 8), None); assert_eq!(BitString::new(b"", 2), None); assert_eq!(BitString::new(b"\xff", 1), None); assert!(BitString::new(b"\xff", 0).is_some()); assert!(BitString::new(b"\xfe", 1).is_some()); } #[test] fn test_owned_bitstring_new() { assert_eq!(OwnedBitString::new(vec![b'a', b'b', b'c'], 8), None); assert_eq!(OwnedBitString::new(vec![], 2), None); assert_eq!(OwnedBitString::new(vec![0xff], 1), None); assert!(OwnedBitString::new(vec![0xff], 0).is_some()); assert!(OwnedBitString::new(vec![0xfe], 1).is_some()); } #[test] fn test_bitstring_as_bytes() { let bs = BitString::new(b"\xfe", 1).unwrap(); assert_eq!(bs.as_bytes(), b"\xfe"); } #[test] fn test_bitstring_padding_bits() { let bs = BitString::new(b"\xfe", 1).unwrap(); assert_eq!(bs.padding_bits(), 1); let bs = BitString::new(b"\xe0", 5).unwrap(); assert_eq!(bs.padding_bits(), 5); } #[test] fn test_bitstring_has_bit_set() { let bs = BitString::new(b"\x80", 0).unwrap(); assert!(bs.has_bit_set(0)); assert!(!bs.has_bit_set(1)); assert!(!bs.has_bit_set(7)); // An arbitrary bit much bigger than the underlying size of the bitfield assert!(!bs.has_bit_set(50)); let bs = BitString::new(b"\xc0", 4).unwrap(); // padding bits should always return false when asking if the bit is set assert!(bs.has_bit_set(0)); assert!(bs.has_bit_set(1)); assert!(!bs.has_bit_set(2)); assert!(!bs.has_bit_set(3)); assert!(!bs.has_bit_set(4)); assert!(!bs.has_bit_set(5)); assert!(!bs.has_bit_set(6)); assert!(!bs.has_bit_set(7)); } } asn1-0.17.0/src/lib.rs000064400000000000000000000165271046102023000124710ustar 00000000000000#![cfg_attr(not(feature = "std"), no_std)] #![forbid(unsafe_code)] #![deny(rust_2018_idioms)] //! This crate provides you with the ability to generate and parse ASN.1 //! encoded data. More precisely, it provides you with the ability to generate //! and parse data encoded with ASN.1's DER (Distinguished Encoding Rules) //! encoding. It does not support BER (Basic Encoding Rules), CER (Canonical //! Encoding Rules), XER (XML Encoding Rules), CXER (Canonical XML Encoding //! Rules), or any other alphabet soup encodings -- and it never will. //! //! If you wanted to parse an ASN.1 structure like this: //! ```text //! Signature ::= SEQUENCE { //! r INTEGER, //! s INTEGER //! } //! ``` //! //! Then you'd write the following code: //! ``` //! # let data = b""; //! let result: asn1::ParseResult<_> = asn1::parse(data, |d| { //! return d.read_element::()?.parse(|d| { //! let r = d.read_element::()?; //! let s = d.read_element::()?; //! return Ok((r, s)); //! }) //! }); //! ``` //! //! In general everything about parsing is driven by providing different type //! parameters to `Parser.read_element`. Some types implement the //! `Asn1Readable` trait directly on a basic type, as seen with `u64` or //! `&[u8]` (`OCTET STRING`), while others use wrapper types which simply //! provide ASN.1 encoding and decoding for some other type (`PrintableString` //! or `UtcTime`). There are also types such as `Implicit` and `Explicit` for //! handling tagged values, `Choice1`, `Choice2`, and `Choice3` available for //! choices, and `Option` for handling `OPTIONAL` values. //! //! To serialize DER for the `Sequence` structure, you'd write the following: //! ``` //! # let r = 0u64; //! # let s = 0u64; //! let result = asn1::write(|w| { //! w.write_element(&asn1::SequenceWriter::new(&|w| { //! w.write_element(&r)?; //! w.write_element(&s)?; //! Ok(()) //! })) //! }); //! ``` //! //! # Derive //! //! When built with the `derive` feature (enabled by default), these can also //! be expressed as Rust structs: //! ``` //! #[derive(asn1::Asn1Read, asn1::Asn1Write)] //! struct Signature { //! r: u64, //! s: u64, //! } //! //! # let data = b""; //! # let r = 0u64; //! # let s = 0u64; //! let sig = asn1::parse_single::(data); //! let result = asn1::write_single(&Signature{r, s}); //! ``` //! //! Fields may be marked as `EXPLICIT` or `IMPLICIT` either by struct members //! having the types [`Explicit`] and [`Implicit`] or via the use of //! `#[explicit]` and `#[implicit]` annotations: //! ``` //! #[derive(asn1::Asn1Read, asn1::Asn1Write)] //! struct SomeSequence<'a> { //! #[implicit(0)] //! a: Option<&'a [u8]>, //! #[explicit(1)] //! b: Option, //! } //! ``` //! //! Fields can also be annotated with `#[default(VALUE)]` to indicate ASN.1 //! `OPTIONAL DEFAULT` values. In this case, the field's type should be `T`, //! and not `Option`. //! //! These derives may also be used with `enum`s to generate `CHOICE` //! implementations. //! ``` //! #[derive(asn1::Asn1Read, asn1::Asn1Write)] //! enum Time { //! UTCTime(asn1::UtcTime), //! GeneralizedTime(asn1::GeneralizedTime) //! } //! ``` //! //! All variants must have a single un-named field. //! //! ## DEFINED BY //! //! rust-asn1 also provides utilities for more easily handling the case of //! `ANY DEFINED BY` in an ASN.1 structure. For example, given the following //! ASN.1; //! //! ```text //! MySequence ::= SEQUENCE { //! contentType OBJECT IDENTIFIER, //! content ANY DEFINED BY contentType //! } //!``` //! //! This can be represented by: //! //! ``` //! # const SOME_OID_CONSTANT: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); //! #[derive(asn1::Asn1Read, asn1::Asn1Write)] //! struct MySequence { //! content_type: asn1::DefinedByMarker, //! #[defined_by(content_type)] //! content: Content, //! } //! //! #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite)] //! enum Content { //! #[defined_by(SOME_OID_CONSTANT)] //! SomeVariant(i32), //! } //! ``` //! //! # Design philosophy //! //! As we have designed the `asn1` crate, we value the following things, in //! this order: //! //! - **Security** //! - **Correctness** //! - **Performance** //! - **Ergonomics** extern crate alloc; mod base128; mod bit_string; mod object_identifier; mod parser; mod tag; mod types; mod writer; pub use crate::bit_string::{BitString, OwnedBitString}; pub use crate::object_identifier::ObjectIdentifier; pub use crate::parser::{ parse, parse_single, strip_tlv, ParseError, ParseErrorKind, ParseLocation, ParseResult, Parser, }; pub use crate::tag::Tag; pub use crate::types::{ Asn1DefinedByReadable, Asn1DefinedByWritable, Asn1Readable, Asn1Writable, BMPString, BigInt, BigUint, Choice1, Choice2, Choice3, DateTime, DefinedByMarker, Enumerated, Explicit, GeneralizedTime, IA5String, Implicit, Null, OctetStringEncoded, OwnedBigInt, OwnedBigUint, PrintableString, Sequence, SequenceOf, SequenceOfWriter, SequenceWriter, SetOf, SetOfWriter, SimpleAsn1Readable, SimpleAsn1Writable, Tlv, UniversalString, UtcTime, Utf8String, VisibleString, }; pub use crate::writer::{write, write_single, WriteBuf, WriteError, WriteResult, Writer}; pub use asn1_derive::{oid, Asn1DefinedByRead, Asn1DefinedByWrite, Asn1Read, Asn1Write}; /// Decodes an `OPTIONAL` ASN.1 value which has a `DEFAULT`. Generaly called /// immediately after [`Parser::read_element`]. pub fn from_optional_default(v: Option, default: T) -> ParseResult { match v { Some(v) if v == default => Err(ParseError::new(ParseErrorKind::EncodedDefault)), Some(v) => Ok(v), None => Ok(default), } } /// Prepares an `OPTIONAL` ASN.1 value which has a `DEFAULT` for writing. /// Generally called immediately before [`Writer::write_element`]. pub fn to_optional_default<'a, T: PartialEq>(v: &'a T, default: &'a T) -> Option<&'a T> { if v == default { None } else { Some(v) } } /// This API is public so that it may be used from macros, but should not be /// considered a part of the supported API surface. #[doc(hidden)] pub const fn implicit_tag(tag: u32, inner_tag: Tag) -> Tag { Tag::new( tag, tag::TagClass::ContextSpecific, inner_tag.is_constructed(), ) } /// This API is public so that it may be used from macros, but should not be /// considered a part of the supported API surface. #[doc(hidden)] pub const fn explicit_tag(tag: u32) -> Tag { Tag::new(tag, tag::TagClass::ContextSpecific, true) } /// This API is public so that it may be used from macros, but should not be /// considered a part of the supported API surface. #[doc(hidden)] pub fn read_defined_by<'a, T: Asn1Readable<'a>, U: Asn1DefinedByReadable<'a, T>>( v: T, p: &mut Parser<'a>, ) -> ParseResult { U::parse(v, p) } /// This API is public so that it may be used from macros, but should not be /// considered a part of the supported API surface. #[doc(hidden)] pub fn write_defined_by>( v: &U, w: &mut Writer<'_>, ) -> WriteResult { v.write(w) } /// This API is public so that it may be used from macros, but should not be /// considered a part of the supported API surface. #[doc(hidden)] pub fn writable_defined_by_item>(v: &U) -> &T { v.item() } asn1-0.17.0/src/object_identifier.rs000064400000000000000000000157311046102023000153670ustar 00000000000000use crate::base128; use crate::parser::{ParseError, ParseErrorKind, ParseResult}; use alloc::fmt; const MAX_OID_LENGTH: usize = 63; /// Represents an ASN.1 `OBJECT IDENTIFIER`. `ObjectIdentifier`s are opaque, /// the only thing may be done with them is test if they are equal to another /// `ObjectIdentifier`. The generally recommended practice for handling them /// is to create some `ObjectIdentifier` constants with `asn1::oid!()` and /// then compare them with `ObjectIdentifier`s you get from parsing. /// /// `asn1::oid!()` takes a series of arcs, for example: `asn1::oid!(1, 2, 3)`. /// /// rust-asn1 stores `ObjectIdentifier`s in a fixed-size buffer, therefore /// they are limited to OID values whose DER encoding fits into that buffer. /// This buffer is sufficiently large to fit all known publically known OIDs, /// so this should not affect most people. #[derive(PartialEq, Eq, Clone, Hash)] pub struct ObjectIdentifier { // Store the OID as DER encoded. der_encoded: [u8; MAX_OID_LENGTH], der_encoded_len: u8, } impl ObjectIdentifier { /// Parses an OID from a dotted string, e.g. `"1.2.840.113549"`. pub fn from_string(oid: &str) -> Option { let mut parts = oid.split('.'); let first = parts.next()?.parse::().ok()?; let second = parts.next()?.parse::().ok()?; if first > 2 || (first < 2 && second >= 40) { return None; } let mut der_data = [0; MAX_OID_LENGTH]; let mut der_data_len = 0; der_data_len += base128::write_base128_int(&mut der_data[der_data_len..], 40 * first + second)?; for part in parts { der_data_len += base128::write_base128_int( &mut der_data[der_data_len..], part.parse::().ok()?, )?; } Some(ObjectIdentifier { der_encoded: der_data, der_encoded_len: der_data_len as u8, }) } /// Creates an `ObjectIdentifier` from its DER representation. This does /// not perform any allocations or copies. pub fn from_der(data: &[u8]) -> ParseResult { if data.is_empty() { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } else if data.len() > MAX_OID_LENGTH { return Err(ParseError::new(ParseErrorKind::OidTooLong)); } let mut parsed = (0, data); while !parsed.1.is_empty() { // `base128::read_base128_int` can return a `ShortData` error, but // in context here that means `InvalidValue`. parsed = base128::read_base128_int(parsed.1) .map_err(|_| ParseError::new(ParseErrorKind::InvalidValue))?; } let mut storage = [0; MAX_OID_LENGTH]; storage[..data.len()].copy_from_slice(data); Ok(ObjectIdentifier { der_encoded: storage, der_encoded_len: data.len() as u8, }) } /// Creates an `ObjectIdentifier` from its DER representation. Does not /// check that the DER is valid. Intended only for use from the `oid!()` /// macro. Do not use yourself! #[doc(hidden)] pub const fn from_der_unchecked(data: [u8; MAX_OID_LENGTH], data_len: u8) -> ObjectIdentifier { ObjectIdentifier { der_encoded: data, der_encoded_len: data_len, } } pub(crate) fn as_der(&self) -> &[u8] { &self.der_encoded[..self.der_encoded_len as usize] } } struct OidFormatter<'a>(&'a ObjectIdentifier); impl fmt::Debug for OidFormatter<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut parsed = (0, self.0.as_der()); parsed = base128::read_base128_int(parsed.1).unwrap(); if parsed.0 < 80 { write!(f, "{}.{}", parsed.0 / 40, parsed.0 % 40)?; } else { write!(f, "2.{}", parsed.0 - 80)?; } while !parsed.1.is_empty() { parsed = base128::read_base128_int(parsed.1).unwrap(); write!(f, ".{}", parsed.0)?; } Ok(()) } } impl fmt::Debug for ObjectIdentifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ObjectIdentifier") .field("oid", &OidFormatter(self)) .finish() } } impl fmt::Display for ObjectIdentifier { /// Converts an `ObjectIdentifier` to a dotted string, e.g. /// "1.2.840.113549". fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Debug::fmt(&OidFormatter(self), f) } } #[cfg(test)] mod tests { use super::MAX_OID_LENGTH; use crate::{ObjectIdentifier, ParseError, ParseErrorKind}; use alloc::format; #[cfg(not(feature = "std"))] use alloc::string::ToString; #[test] fn test_object_identifier_from_string() { for val in &[ "", "1", "3.10", "1.50", "2.12.a3.4", "a.4", "1.a", ".2.5", "2..5", "2.5.", "1.3.6.1.4.1.1248.1.1.2.1.3.21.69.112.115.111.110.32.83.116.121.108.117.115.32.80.114.111.32.52.57.48.48.123.124412.31.213321.123.110.32.83.116.121.108.117.115.32.80.114.111.32.52.57.48.48.123.124412.31.213321.123", ] { assert_eq!(ObjectIdentifier::from_string(val), None); } for val in &[ "2.5", "2.5.2", "1.2.840.113549", "1.2.3.4", "1.2.840.133549.1.1.5", "2.100.3", ] { assert!(ObjectIdentifier::from_string(val).is_some()); } } #[test] fn test_from_der() { assert_eq!(ObjectIdentifier::from_der(b"\x06\x40\x2b\x06\x01\x04\x01\x89\x60\x01\x01\x02\x01\x03\x15\x45\x70\x73\x6f\x6e\x20\x53\x74\x79\x6c\x75\x73\x20\x50\x72\x6f\x20\x34\x39\x30\x30\x7b\x87\xcb\x7c\x1f\x8d\x82\x49\x7b\x2b\x06\x01\x04\x01\x89\x60\x01\x01\x02\x01\x03\x15\x45\x70\x73\x6f\x6e\x20"), Err(ParseError::new(ParseErrorKind::OidTooLong))); } #[test] fn test_from_der_unchecked() { for (dotted_string, der) in &[("2.5", b"\x55" as &[u8]), ("2.100.3", b"\x81\x34\x03")] { let mut data = [0; MAX_OID_LENGTH]; data[..der.len()].copy_from_slice(der); assert_eq!( ObjectIdentifier::from_string(dotted_string).unwrap(), ObjectIdentifier::from_der_unchecked(data, der.len() as u8) ); } } #[test] fn test_debug() { let oid = ObjectIdentifier::from_string("1.2.3.4").unwrap(); assert_eq!(format!("{:?}", oid), "ObjectIdentifier { oid: 1.2.3.4 }"); } #[test] fn test_to_string() { for val in &[ "0.4", "2.5", "2.5.2", "1.2.840.113549", "1.2.3.4", "1.2.840.133549.1.1.5", "2.100.3", "2.1.750304883", ] { assert_eq!( &ObjectIdentifier::from_string(val).unwrap().to_string(), val ); } } } asn1-0.17.0/src/parser.rs000064400000000000000000001770621046102023000132210ustar 00000000000000use crate::types::{Asn1Readable, Tlv}; use crate::Tag; use core::fmt; #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum ParseErrorKind { /// Something about the value was invalid. InvalidValue, /// Something about the tag was invalid. This refers to a syntax error, /// not a tag's value being unexpected. InvalidTag, /// Something about the length was invalid. This can mean either a invalid /// encoding, or that a TLV was longer than 4GB, which is the maximum /// length that rust-asn1 supports. InvalidLength, /// A container's size was invalid. This typically indicates an empty /// or oversized structure. InvalidSize { min: usize, max: usize, actual: usize, }, /// An unexpected tag was encountered. UnexpectedTag { actual: Tag }, /// There was not enough data available to complete parsing. `needed` /// indicates the amount of data required to advance the parse. /// /// Note that providing `needed` additional bytes of data does not ensure /// that `parse` will succeed -- it is the amount of data required to /// satisfy the `read` operation that failed, and there may be subsequent /// `read` operations that require additional data. ShortData { needed: usize }, /// An internal computation would have overflowed. IntegerOverflow, /// There was extraneous data in the input. ExtraData, /// Elements of a set were not lexicographically sorted. InvalidSetOrdering, /// An OPTIONAL DEFAULT was written with a default value. EncodedDefault, /// OID value is longer than the maximum size rust-asn1 can store. This is /// a limitation of rust-asn1. OidTooLong, /// A `DEFINED BY` value received an value for which there was no known /// variant. UnknownDefinedBy, } #[derive(Debug, PartialEq, Eq)] #[doc(hidden)] pub enum ParseLocation { Field(&'static str), Index(usize), } /// `ParseError` are returned when there is an error parsing the ASN.1 data. #[derive(PartialEq, Eq)] pub struct ParseError { kind: ParseErrorKind, parse_locations: [Option; 4], parse_depth: u8, } impl ParseError { pub fn new(kind: ParseErrorKind) -> ParseError { ParseError { kind, parse_locations: [None, None, None, None], parse_depth: 0, } } pub fn kind(&self) -> ParseErrorKind { self.kind } #[doc(hidden)] #[must_use] pub fn add_location(mut self, loc: ParseLocation) -> Self { if (self.parse_depth as usize) < self.parse_locations.len() { self.parse_locations[self.parse_depth as usize] = Some(loc); self.parse_depth += 1; } self } } #[cfg(feature = "std")] impl std::error::Error for ParseError {} // Wraps an `Option`, but `fmt::Debug` will only render `Some` values and // panics on others. struct SomeFmtOption(Option); impl fmt::Debug for SomeFmtOption { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.as_ref().unwrap().fmt(f) } } impl fmt::Debug for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("ParseError"); f.field("kind", &self.kind); if self.parse_depth > 0 { let mut locations = [ SomeFmtOption(None), SomeFmtOption(None), SomeFmtOption(None), SomeFmtOption(None), SomeFmtOption(None), SomeFmtOption(None), SomeFmtOption(None), SomeFmtOption(None), ]; for (i, location) in self.parse_locations[..self.parse_depth as usize] .iter() .rev() .enumerate() { locations[i] = match location.as_ref().unwrap() { ParseLocation::Field(f) => SomeFmtOption(Some(f as &dyn fmt::Debug)), ParseLocation::Index(i) => SomeFmtOption(Some(i as &dyn fmt::Debug)), } } f.field("location", &&locations[..self.parse_depth as usize]); } f.finish() } } impl fmt::Display for ParseError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "ASN.1 parsing error: ")?; match self.kind { ParseErrorKind::InvalidValue => write!(f, "invalid value"), ParseErrorKind::InvalidTag => write!(f, "invalid tag"), ParseErrorKind::InvalidLength => write!(f, "invalid length"), ParseErrorKind::InvalidSize { min, max, actual } => { write!( f, "invalid container size (expected between {} and {}, got {})", min, max, actual ) } ParseErrorKind::UnexpectedTag { actual } => { write!(f, "unexpected tag (got {:?})", actual) } ParseErrorKind::ShortData { needed } => { write!( f, "short data (needed at least {} additional bytes)", needed ) } ParseErrorKind::IntegerOverflow => write!(f, "integer overflow"), ParseErrorKind::ExtraData => write!(f, "extra data"), ParseErrorKind::InvalidSetOrdering => write!(f, "SET value was ordered incorrectly"), ParseErrorKind::EncodedDefault => write!(f, "DEFAULT value was explicitly encoded"), ParseErrorKind::OidTooLong => write!( f, "OBJECT IDENTIFIER was too large to be stored in rust-asn1's buffer" ), ParseErrorKind::UnknownDefinedBy => write!(f, "DEFINED BY with unknown value"), } } } /// The result of a `parse`. Either a successful value or a `ParseError`. pub type ParseResult = Result; /// Parse takes a sequence of bytes of DER encoded ASN.1 data, constructs a /// parser, and invokes a callback to read elements from the ASN.1 parser. pub fn parse<'a, T, E: From, F: FnOnce(&mut Parser<'a>) -> Result>( data: &'a [u8], f: F, ) -> Result { let mut p = Parser::new(data); let result = f(&mut p)?; p.finish()?; Ok(result) } /// Parses a single top-level ASN.1 element from `data` (does not allow /// trailing data). Most often this will be used where `T` is a type with /// `#[derive(asn1::Asn1Read)]`. pub fn parse_single<'a, T: Asn1Readable<'a>>(data: &'a [u8]) -> ParseResult { parse(data, Parser::read_element::) } /// Attempts to parse the `Tlv` at the start of `data` (allows trailing data). /// If successful, the `Tlv` and the trailing data after it are returned, if /// unsuccessful a `ParseError` is returned. /// /// This can be useful where you have a file or stream format that relies on /// ASN.1 TLVs for framing. /// /// When parsing a stream, if an error is returned, if its `kind` is /// `ParseErrorKind::ShortData`, this indicates that `data` did not contain /// sufficient data to parse an entire `Tlv`, and thus adding more data may /// resolve this. All other errors are "fatal" and cannot be resolved with /// additional data. pub fn strip_tlv(data: &[u8]) -> ParseResult<(Tlv<'_>, &[u8])> { let mut p = Parser::new(data); let tlv = p.read_element::>()?; Ok((tlv, p.data)) } /// Encapsulates an ongoing parse. For almost all use-cases the correct /// entry-point is [`parse`] or [`parse_single`]. pub struct Parser<'a> { data: &'a [u8], } impl<'a> Parser<'a> { #[inline] pub(crate) fn new(data: &'a [u8]) -> Parser<'a> { Parser { data } } #[inline] fn finish(self) -> ParseResult<()> { if !self.is_empty() { return Err(ParseError::new(ParseErrorKind::ExtraData)); } Ok(()) } pub(crate) fn clone_internal(&self) -> Parser<'a> { Parser::new(self.data) } pub(crate) fn peek_tag(&mut self) -> Option { let (tag, _) = Tag::from_bytes(self.data).ok()?; Some(tag) } pub(crate) fn read_tag(&mut self) -> ParseResult { let (tag, data) = Tag::from_bytes(self.data)?; self.data = data; Ok(tag) } #[inline] fn read_u8(&mut self) -> ParseResult { Ok(self.read_bytes(1)?[0]) } #[inline] fn read_bytes(&mut self, length: usize) -> ParseResult<&'a [u8]> { if length > self.data.len() { return Err(ParseError::new(ParseErrorKind::ShortData { needed: length - self.data.len(), })); } let (result, data) = self.data.split_at(length); self.data = data; Ok(result) } fn read_length(&mut self) -> ParseResult { match self.read_u8()? { n if (n & 0x80) == 0 => Ok(usize::from(n)), 0x81 => { let length = usize::from(self.read_u8()?); // Do not allow values <0x80 to be encoded using the long form if length < 0x80 { return Err(ParseError::new(ParseErrorKind::InvalidLength)); } Ok(length) } 0x82 => { let length_bytes = self.read_bytes(2)?; let length = usize::from(length_bytes[0]) << 8 | usize::from(length_bytes[1]); // Enforce that we're not using long form for values <0x80, // and that the first byte of the length is not zero (i.e. // that we're minimally encoded) if length < 0x100 { return Err(ParseError::new(ParseErrorKind::InvalidLength)); } Ok(length) } 0x83 => { let length_bytes = self.read_bytes(3)?; let length = usize::from(length_bytes[0]) << 16 | usize::from(length_bytes[1]) << 8 | usize::from(length_bytes[2]); // Same thing as the 0x82 case if length < 0x10000 { return Err(ParseError::new(ParseErrorKind::InvalidLength)); } Ok(length) } 0x84 => { let length_bytes = self.read_bytes(4)?; let length = usize::from(length_bytes[0]) << 24 | usize::from(length_bytes[1]) << 16 | usize::from(length_bytes[2]) << 8 | usize::from(length_bytes[3]); // Same thing as the 0x82 case if length < 0x1000000 { return Err(ParseError::new(ParseErrorKind::InvalidLength)); } Ok(length) } // We only support four-byte lengths _ => Err(ParseError::new(ParseErrorKind::InvalidLength)), } } #[inline] pub(crate) fn read_tlv(&mut self) -> ParseResult> { let initial_data = self.data; let tag = self.read_tag()?; let length = self.read_length()?; let data = self.read_bytes(length)?; let full_data = &initial_data[..initial_data.len() - self.data.len()]; Ok(Tlv { tag, data, full_data, }) } /// Tests whether there is any data remaining in the Parser. Generally /// useful when parsing a `SEQUENCE OF`. #[inline] pub fn is_empty(&self) -> bool { self.data.is_empty() } /// Reads a single ASN.1 element from the parser. Which type you are reading is determined by /// the type parameter `T`. #[inline] pub fn read_element>(&mut self) -> ParseResult { T::parse(self) } } #[cfg(test)] mod tests { use super::Parser; use crate::tag::TagClass; use crate::types::Asn1Readable; use crate::{ BMPString, BigInt, BigUint, BitString, Choice1, Choice2, Choice3, DateTime, Enumerated, Explicit, GeneralizedTime, IA5String, Implicit, ObjectIdentifier, OctetStringEncoded, OwnedBigInt, OwnedBigUint, OwnedBitString, ParseError, ParseErrorKind, ParseLocation, ParseResult, PrintableString, Sequence, SequenceOf, SetOf, Tag, Tlv, UniversalString, UtcTime, Utf8String, VisibleString, }; #[cfg(not(feature = "std"))] use alloc::boxed::Box; use alloc::{format, vec}; use core::fmt; #[test] fn test_lifetimes() { // Explicit 'static OCTET_STRING let result = crate::parse(b"\x04\x01\x00", |p| p.read_element::<&'static [u8]>()).unwrap(); assert_eq!(result, b"\x00"); // Explicit 'static SEQUENCE containing an explicit 'static OCTET_STRING let result = crate::parse(b"\x30\x03\x04\x01\x00", |p| { p.read_element::>()? .parse(|p| p.read_element::<&'static [u8]>()) }) .unwrap(); assert_eq!(result, b"\x00"); // Automatic 'static OCTET_STRING let result = crate::parse(b"\x04\x01\x00", |p| p.read_element::<&[u8]>()).unwrap(); assert_eq!(result, b"\x00"); // Automatic 'static SEQUENCE containing an automatic 'static // OCTET_STRING let result = crate::parse(b"\x30\x03\x04\x01\x00", |p| { p.read_element::>()? .parse(|p| p.read_element::<&[u8]>()) }) .unwrap(); assert_eq!(result, b"\x00"); // BIT_STRING let result = crate::parse::<_, ParseError, _>(b"\x03\x02\x00\x00", |p| { Ok(p.read_element::>()?.as_bytes()) }) .unwrap(); assert_eq!(result, b"\x00"); } #[test] fn test_parse_error_debug() { for (e, expected) in &[ ( ParseError::new(ParseErrorKind::InvalidValue), "ParseError { kind: InvalidValue }", ), ( ParseError::new(ParseErrorKind::InvalidValue) .add_location(ParseLocation::Field("Abc::123")), "ParseError { kind: InvalidValue, location: [\"Abc::123\"] }", ), ( ParseError::new(ParseErrorKind::InvalidValue) .add_location(ParseLocation::Index(12)) .add_location(ParseLocation::Field("Abc::123")), "ParseError { kind: InvalidValue, location: [\"Abc::123\", 12] }", ), ] { assert_eq!(&format!("{:?}", e), expected); } } #[test] fn test_parse_error_display() { for (e, expected) in &[ ( ParseError::new(ParseErrorKind::InvalidValue), "ASN.1 parsing error: invalid value", ), ( ParseError::new(ParseErrorKind::InvalidTag), "ASN.1 parsing error: invalid tag" ), ( ParseError::new(ParseErrorKind::InvalidLength), "ASN.1 parsing error: invalid length" ), ( ParseError::new(ParseErrorKind::InvalidSize { min: 1, max: 5, actual: 0 }), "ASN.1 parsing error: invalid container size (expected between 1 and 5, got 0)", ), ( ParseError::new(ParseErrorKind::IntegerOverflow), "ASN.1 parsing error: integer overflow" ), ( ParseError::new(ParseErrorKind::ExtraData), "ASN.1 parsing error: extra data" ), ( ParseError::new(ParseErrorKind::InvalidSetOrdering), "ASN.1 parsing error: SET value was ordered incorrectly" ), ( ParseError::new(ParseErrorKind::EncodedDefault), "ASN.1 parsing error: DEFAULT value was explicitly encoded" ), ( ParseError::new(ParseErrorKind::OidTooLong), "ASN.1 parsing error: OBJECT IDENTIFIER was too large to be stored in rust-asn1's buffer" ), ( ParseError::new(ParseErrorKind::UnknownDefinedBy), "ASN.1 parsing error: DEFINED BY with unknown value" ), ( ParseError::new(ParseErrorKind::ShortData{needed: 7}) .add_location(ParseLocation::Field("Abc::123")), "ASN.1 parsing error: short data (needed at least 7 additional bytes)", ), ( ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(12), }) .add_location(ParseLocation::Index(12)) .add_location(ParseLocation::Field("Abc::123")), "ASN.1 parsing error: unexpected tag (got Tag { value: 12, constructed: false, class: Universal })", ), ] { assert_eq!(&format!("{}", e), expected); } } #[test] fn test_parse_error_kind() { let e = ParseError::new(ParseErrorKind::EncodedDefault); assert_eq!(e.kind(), ParseErrorKind::EncodedDefault); } #[test] fn test_strip_tlv() { for (der_bytes, expected) in [ ( b"" as &[u8], Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), ), ( b"\x04", Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), ), ( b"\x04\x82", Err(ParseError::new(ParseErrorKind::ShortData { needed: 2 })), ), ( b"\x04\x03", Err(ParseError::new(ParseErrorKind::ShortData { needed: 3 })), ), ( b"\x04\x03ab", Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), ), ( b"\x04\x03abc", Ok(( Tlv { tag: Tag::primitive(0x04), data: b"abc", full_data: b"\x04\x03abc", }, b"" as &[u8], )), ), ( b"\x04\x03abc\x00\x00\x00", Ok(( Tlv { tag: Tag::primitive(0x04), data: b"abc", full_data: b"\x04\x03abc", }, b"\x00\x00\x00", )), ), ] { let result = crate::strip_tlv(der_bytes); assert_eq!(result, expected); } } fn assert_parses_cb< 'a, T: fmt::Debug + PartialEq, E: From + fmt::Debug + PartialEq, F: Fn(&mut Parser<'a>) -> Result, >( data: &[(Result, &'a [u8])], f: F, ) { for (expected, der_bytes) in data { let result = crate::parse(der_bytes, &f); assert_eq!(&result, expected); } } fn assert_parses<'a, T>(data: &[(ParseResult, &'a [u8])]) where T: Asn1Readable<'a> + fmt::Debug + PartialEq, { assert_parses_cb(data, |p| p.read_element::()); } #[test] fn test_parse_extra_data() { let result = crate::parse(b"\x00", |_| Ok(())); assert_eq!(result, Err(ParseError::new(ParseErrorKind::ExtraData))); } #[test] fn test_errors() { #[derive(Debug, PartialEq, Eq)] enum E { X(u64), P(ParseError), } impl From for E { fn from(e: ParseError) -> E { E::P(e) } } assert_parses_cb( &[ (Ok(8), b"\x02\x01\x08"), ( Err(E::P(ParseError::new(ParseErrorKind::ShortData { needed: 1, }))), b"\x02\x01", ), (Err(E::X(7)), b"\x02\x01\x07"), ], |p| { let val = p.read_element::()?; if val % 2 == 0 { Ok(val) } else { Err(E::X(val)) } }, ); } #[test] fn test_parse_tlv() { assert_parses::>(&[ ( Ok(Tlv { tag: Tag::primitive(0x4), data: b"abc", full_data: b"\x04\x03abc", }), b"\x04\x03abc", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 2 })), b"\x04\x03a", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x04", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), // Long form tags ( Ok(Tlv { tag: Tag::new(31, TagClass::Universal, false), data: b"", full_data: b"\x1f\x1f\x00", }), b"\x1f\x1f\x00", ), ( Ok(Tlv { tag: Tag::new(128, TagClass::Universal, false), data: b"", full_data: b"\x1f\x81\x00\x00", }), b"\x1f\x81\x00\x00", ), ( Ok(Tlv { tag: Tag::new(0x4001, TagClass::Universal, false), data: b"", full_data: b"\x1f\x81\x80\x01\x00", }), b"\x1f\x81\x80\x01\x00", ), ( Ok(Tlv { tag: Tag::new(0x01, TagClass::Application, false), data: b"", full_data: b"\x41\x00", }), b"\x41\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x1f", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\xff", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x1f\x85", ), // Overflow u32 for the tag number. ( Err(ParseError::new(ParseErrorKind::InvalidTag)), b"\x1f\x88\x80\x80\x80\x80\x00", ), // Long form tag for value that fits in a short form ( Err(ParseError::new(ParseErrorKind::InvalidTag)), b"\x1f\x1e\x00", ), ( // base128 integer with leading 0 Err(ParseError::new(ParseErrorKind::InvalidTag)), b"\xff\x80\x84\x01\x01\xa9", ), ]); } #[test] fn test_parse_null() { assert_parses::<()>(&[ (Ok(()), b"\x05\x00"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x05\x01\x00", ), ]); } #[test] fn test_parse_bool() { assert_parses::(&[ (Ok(true), b"\x01\x01\xff"), (Ok(false), b"\x01\x01\x00"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x01\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x01\x01\x01", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x01\x02\x00\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x01\x02\xff\x01", ), ]); } #[test] fn test_parse_octet_string() { let long_value = vec![b'a'; 70_000]; let really_long_value = vec![b'a'; 20_000_000]; assert_parses::<&[u8]>(&[ (Ok(b""), b"\x04\x00"), (Ok(b"\x01\x02\x03"), b"\x04\x03\x01\x02\x03"), ( Ok(b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), b"\x04\x81\x81aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" ), ( Ok(long_value.as_slice()), [b"\x04\x83\x01\x11\x70", long_value.as_slice()].concat().as_slice() ), ( Ok(really_long_value.as_slice()), [b"\x04\x84\x01\x31\x2d\x00", really_long_value.as_slice()].concat().as_slice() ), (Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x80"), (Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x81\x00"), (Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x81\x01\x09"), (Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x82\x00\x80"), ( Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x89\x01\x01\x01\x01\x01\x01\x01\x01\x01" ), (Err(ParseError::new(ParseErrorKind::ShortData{needed: 1})), b"\x04\x03\x01\x02"), (Err(ParseError::new(ParseErrorKind::ShortData{needed: 65531})), b"\x04\x82\xff\xff\xff\xff\xff\xff"), // 3 byte length form with leading 0. (Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x83\x00\xff\xff"), // 4 byte length form with leading 0. (Err(ParseError::new(ParseErrorKind::InvalidLength)), b"\x04\x84\x00\xff\xff\xff"), ]); assert_parses::<[u8; 0]>(&[ (Ok([]), b"\x04\x00"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x04\x02\x01\x02", ), ]); assert_parses::<[u8; 1]>(&[ (Ok([2]), b"\x04\x01\x02"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x04\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x04\x02\x01\x02", ), ]); } #[test] fn test_octet_string_encoded() { assert_parses::>(&[ (Ok(OctetStringEncoded::new(true)), b"\x04\x03\x01\x01\xff"), (Ok(OctetStringEncoded::new(false)), b"\x04\x03\x01\x01\x00"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x03), })), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x02), })), b"\x04\x02\x02\x00", ), ]) } #[test] fn test_parse_int_i64() { assert_parses::(&[ (Ok(0), b"\x02\x01\x00"), (Ok(127), b"\x02\x01\x7f"), (Ok(128), b"\x02\x02\x00\x80"), (Ok(256), b"\x02\x02\x01\x00"), (Ok(-128), b"\x02\x01\x80"), (Ok(-129), b"\x02\x02\xff\x7f"), (Ok(-256), b"\x02\x02\xff\x00"), (Ok(i64::MAX), b"\x02\x08\x7f\xff\xff\xff\xff\xff\xff\xff"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x3), })), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x02\x02\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x02", ), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x05\x00\x00\x00\x00\x01", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x02\xff\x80", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x09\x00\xD0\x07\x04\x00\x03\x31\x31\x00", ), ]); } #[test] fn parse_int_u64() { assert_parses::(&[ ( Ok(u64::MAX), b"\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x01\xff", ), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00", ), ]); } #[test] fn test_parse_int_i32() { assert_parses::(&[ (Ok(0), b"\x02\x01\x00"), (Ok(127), b"\x02\x01\x7f"), (Ok(128), b"\x02\x02\x00\x80"), (Ok(256), b"\x02\x02\x01\x00"), (Ok(-128), b"\x02\x01\x80"), (Ok(-129), b"\x02\x02\xff\x7f"), (Ok(-256), b"\x02\x02\xff\x00"), (Ok(i32::MAX), b"\x02\x04\x7f\xff\xff\xff"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x3), })), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x02\x02\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x02", ), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x09\x02\x00\x00\x00\x00\x00\x00\x00\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x05\x00\x00\x00\x00\x01", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x02\xff\x80", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ]); } #[test] fn test_parse_int_u16() { assert_parses::(&[ (Ok(0), b"\x02\x01\x00"), (Ok(1), b"\x02\x01\x01"), (Ok(256), b"\x02\x02\x01\x00"), (Ok(65535), b"\x02\x03\x00\xff\xff"), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x03\x01\x00\x00", ), ]); } #[test] fn test_parse_int_i16() { assert_parses::(&[ (Ok(0), b"\x02\x01\x00"), (Ok(1), b"\x02\x01\x01"), (Ok(-256), b"\x02\x02\xff\x00"), (Ok(-1), b"\x02\x01\xff"), (Ok(-32768), b"\x02\x02\x80\x00"), (Ok(32767), b"\x02\x02\x7f\xff"), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x03\x80\x00\x00", ), ]); } #[test] fn test_parse_int_i8() { assert_parses::(&[ (Ok(0i8), b"\x02\x01\x00"), (Ok(127i8), b"\x02\x01\x7f"), (Ok(-128i8), b"\x02\x01\x80"), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x02\x02\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ]); } #[test] fn test_parse_int_u8() { assert_parses::(&[ (Ok(0u8), b"\x02\x01\x00"), (Ok(127u8), b"\x02\x01\x7f"), (Ok(255u8), b"\x02\x02\x00\xff"), ( Err(ParseError::new(ParseErrorKind::IntegerOverflow)), b"\x02\x02\x01\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x01\x80", ), ]); } #[test] fn test_parse_biguint() { assert_parses::>(&[ (Ok(BigUint::new(b"\x00").unwrap()), b"\x02\x01\x00"), (Ok(BigUint::new(b"\x00\xff").unwrap()), b"\x02\x02\x00\xff"), ( Ok(BigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap()), b"\x02\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x01\x80", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x02\xff\x80", ), ]); } #[test] fn test_parse_ownedbiguint() { assert_parses::(&[ ( Ok(OwnedBigUint::new(b"\x00".to_vec()).unwrap()), b"\x02\x01\x00", ), ( Ok(OwnedBigUint::new(b"\x00\xff".to_vec()).unwrap()), b"\x02\x02\x00\xff", ), ( Ok(OwnedBigUint::new( b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec(), ) .unwrap()), b"\x02\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x01\x80", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x02\xff\x80", ), ]); } #[test] fn test_parse_bigint() { assert_parses::>(&[ (Ok(BigInt::new(b"\x80").unwrap()), b"\x02\x01\x80"), (Ok(BigInt::new(b"\xff").unwrap()), b"\x02\x01\xff"), ( Ok(BigInt::new(b"\x00\xff\xff").unwrap()), b"\x02\x03\x00\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x02\xff\xff", ), ( Ok(BigInt::new(b"\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap()), b"\x02\x0c\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ]); } #[test] fn test_parse_owned_bigint() { assert_parses::(&[ ( Ok(OwnedBigInt::new(b"\x80".to_vec()).unwrap()), b"\x02\x01\x80", ), ( Ok(OwnedBigInt::new(b"\xff".to_vec()).unwrap()), b"\x02\x01\xff", ), ( Ok(OwnedBigInt::new(b"\x00\xff\xff".to_vec()).unwrap()), b"\x02\x03\x00\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x02\xff\xff", ), ( Ok( OwnedBigInt::new(b"\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec()) .unwrap(), ), b"\x02\x0c\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x02\x00", ), ]); } #[test] fn test_parse_object_identifier() { assert_parses::(&[ ( Ok(ObjectIdentifier::from_string("2.5").unwrap()), b"\x06\x01\x55", ), ( Ok(ObjectIdentifier::from_string("2.5.2").unwrap()), b"\x06\x02\x55\x02", ), ( Ok(ObjectIdentifier::from_string("1.2.840.113549").unwrap()), b"\x06\x06\x2a\x86\x48\x86\xf7\x0d", ), ( Ok(ObjectIdentifier::from_string("1.2.3.4").unwrap()), b"\x06\x03\x2a\x03\x04", ), ( Ok(ObjectIdentifier::from_string("1.2.840.133549.1.1.5").unwrap()), b"\x06\x09\x2a\x86\x48\x88\x93\x2d\x01\x01\x05", ), ( Ok(ObjectIdentifier::from_string("2.100.3").unwrap()), b"\x06\x03\x81\x34\x03", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x06\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x06\x07\x55\x02\xc0\x80\x80\x80\x80", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x06\x02\x2a\x86", ), ]); } #[test] fn test_parse_bit_string() { assert_parses::>(&[ (Ok(BitString::new(b"", 0).unwrap()), b"\x03\x01\x00"), (Ok(BitString::new(b"\x00", 7).unwrap()), b"\x03\x02\x07\x00"), (Ok(BitString::new(b"\x80", 7).unwrap()), b"\x03\x02\x07\x80"), ( Ok(BitString::new(b"\x81\xf0", 4).unwrap()), b"\x03\x03\x04\x81\xf0", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x02\x07\x01", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x02\x07\x40", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x02\x08\x00", ), ]); } #[test] fn test_parse_owned_bit_string() { assert_parses::(&[ (Ok(OwnedBitString::new(vec![], 0).unwrap()), b"\x03\x01\x00"), ( Ok(OwnedBitString::new(vec![0x00], 7).unwrap()), b"\x03\x02\x07\x00", ), ( Ok(OwnedBitString::new(vec![0x80], 7).unwrap()), b"\x03\x02\x07\x80", ), ( Ok(OwnedBitString::new(vec![0x81, 0xf0], 4).unwrap()), b"\x03\x03\x04\x81\xf0", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x02\x07\x01", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x02\x07\x40", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x03\x02\x08\x00", ), ]); } #[test] fn test_parse_printable_string() { assert_parses::>(&[ (Ok(PrintableString::new("abc").unwrap()), b"\x13\x03abc"), (Ok(PrintableString::new(")").unwrap()), b"\x13\x01)"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x13\x03ab\x00", ), ]); } #[test] fn test_parse_ia5string() { assert_parses::>(&[ (Ok(IA5String::new("abc").unwrap()), b"\x16\x03abc"), (Ok(IA5String::new(")").unwrap()), b"\x16\x01)"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x16\x03ab\xff", ), ]); } #[test] fn test_parse_utf8string() { assert_parses::>(&[ (Ok(Utf8String::new("abc")), b"\x0c\x03abc"), (Ok(Utf8String::new(")")), b"\x0c\x01)"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x0c\x01\xff", ), ]); } #[test] fn test_parse_visiblestring() { assert_parses::>(&[ (Ok(VisibleString::new("abc").unwrap()), b"\x1a\x03abc"), (Ok(VisibleString::new(")").unwrap()), b"\x1a\x01)"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1a\x01\n", ), ]); } #[test] fn test_parse_bmpstring() { assert_parses::>(&[ ( Ok(BMPString::new(b"\x00a\x00b\x00c").unwrap()), b"\x1e\x06\x00a\x00b\x00c", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1e\x01a", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1e\x04\xde|X@", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1e\x02\xdeX", ), ]); } #[test] fn test_parse_universalstring() { assert_parses::>(&[ ( Ok(UniversalString::new(b"\x00\x00\x00a\x00\x00\x00b\x00\x00\x00c").unwrap()), b"\x1c\x0c\x00\x00\x00a\x00\x00\x00b\x00\x00\x00c", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1c\x01a", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1c\x02ab", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1c\x03abc", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1c\x03abc", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x1c\x04\x96\x8c\xeaU", ), ]); } #[test] fn test_parse_utctime() { assert_parses::(&[ ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x11910506164540-0700", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x11910506164540+0730", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0f5105062345+0000", ), ( Ok(UtcTime::new(DateTime::new(1991, 5, 6, 23, 45, 40).unwrap()).unwrap()), b"\x17\x0d910506234540Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0b9105062345Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0b5105062345Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0da10506234540Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d91a506234540Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d9105a6234540Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d910506a34540Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d910506334a40Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d91050633444aZ", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d910506334461Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e910506334400Za", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d000100000000Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d101302030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100002030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100100030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100132030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100231030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100102240405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100102036005Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0d100102030460Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e-100102030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e10-0102030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e10-0002030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e1001-02030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e100102-030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e10010203-0410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0e1001020304-10Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0c18102813516Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x1018102813516+0730", ), ( // 2049 year with a negative UTC-offset, so actually a 2050 // date. UTCTime doesn't support those. Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x17\x0f4912311047-2026", ), ]); } #[test] fn test_generalizedtime() { assert_parses::(&[ ( Ok(GeneralizedTime::new(DateTime::new(2010, 1, 2, 3, 4, 5).unwrap()).unwrap()), b"\x18\x0f20100102030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1320100102030405+0607", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1320100102030405-0607", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1320100602030405-0607", ), ( // 29th of February (Leap Year) Ok(GeneralizedTime::new(DateTime::new(2000, 2, 29, 3, 4, 5).unwrap()).unwrap()), b"\x18\x0f20000229030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100102030405", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e00000100000000Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20101302030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100002030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100100030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100132030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100231030405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100102240405Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100102036005Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0e20100102030460Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f-20100102030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f2010-0102030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f2010-0002030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f201001-02030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f20100102-030410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f2010010203-0410Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f201001020304-10Z", ), ( // 31st of June Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1320100631030405-0607", ), ( // 30th of February (Leap Year) Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1320000230030405-0607", ), ( // 29th of February (non-Leap Year) Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1319000229030405-0607", ), ( // Invalid timezone-offset hours Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1319000228030405-3007", ), ( // Invalid timezone-offset minutes Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1319000228030405-2367", ), ( // Trailing data Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1019000228030405Z ", ), // Tests for fractional seconds, which we currently don't support ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1620100102030405.123456Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1520100102030405.123456", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x1020100102030405.Z", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0f20100102030405.", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x11-1\n110723459+1002", ), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x18\x0d0 1204000060Z", ), ]); } #[test] fn test_enumerated() { assert_parses::(&[ (Ok(Enumerated::new(12)), b"\x0a\x01\x0c"), ( Err(ParseError::new(ParseErrorKind::InvalidValue)), b"\x0a\x09\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ]); } #[test] fn test_parse_sequence() { assert_parses::>(&[ ( Ok(Sequence::new(b"\x02\x01\x01\x02\x01\x02")), b"\x30\x06\x02\x01\x01\x02\x01\x02", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x30\x04\x02\x01\x01", ), ( Err(ParseError::new(ParseErrorKind::ExtraData)), b"\x30\x06\x02\x01\x01\x02\x01\x02\x00", ), ]); } #[test] fn test_sequence_parse() { assert_parses_cb( &[ (Ok((1, 2)), b"\x30\x06\x02\x01\x01\x02\x01\x02"), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x30\x03\x02\x01\x01", ), ( Err(ParseError::new(ParseErrorKind::ExtraData)), b"\x30\x07\x02\x01\x01\x02\x01\x02\x00", ), ], |p| { p.read_element::>()? .parse(|p| Ok((p.read_element::()?, p.read_element::()?))) }, ); } #[test] fn test_parse_is_empty() { assert_parses_cb( &[ ( Ok(vec![1, 2, 3]), b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), (Ok(vec![]), b"\x30\x00"), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x30\x02\x02\x01", ), ], |p| { p.read_element::>()?.parse(|p| { let mut result = vec![]; while !p.is_empty() { result.push(p.read_element::()?); } Ok(result) }) }, ); } #[test] fn test_parse_sequence_of() { assert_parses_cb( &[ ( Ok(vec![1, 2, 3]), b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), (Ok(vec![]), b"\x30\x00"), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 }) .add_location(ParseLocation::Index(0))), b"\x30\x02\x02\x01", ), ], |p| Ok(p.read_element::>()?.collect()), ); } #[test] fn test_sequence_of_constrained_lengths() { // Minimum only. assert_parses_cb( &[ ( Ok(vec![1, 2, 3]), b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ( Err(ParseError::new(ParseErrorKind::InvalidSize { min: 1, max: usize::MAX, actual: 0, })), b"\x30\x00", ), ], |p| Ok(p.read_element::>()?.collect()), ); // Minimum and maximum. assert_parses_cb( &[ ( Err(ParseError::new(ParseErrorKind::InvalidSize { min: 1, max: 2, actual: 3, })), b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ( Err(ParseError::new(ParseErrorKind::InvalidSize { min: 1, max: 2, actual: 0, })), b"\x30\x00", ), (Ok(vec![3, 1]), b"\x30\x06\x02\x01\x03\x02\x01\x01"), ], |p| Ok(p.read_element::>()?.collect()), ); } #[test] fn parse_set_of() { assert_parses_cb( &[ ( Ok(vec![1, 2, 3]), b"\x31\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), (Ok(vec![]), b"\x31\x00"), ( Err(ParseError::new(ParseErrorKind::InvalidSetOrdering) .add_location(ParseLocation::Index(1))), b"\x31\x06\x02\x01\x03\x02\x01\x01", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 }) .add_location(ParseLocation::Index(0))), b"\x31\x01\x02", ), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x1), }) .add_location(ParseLocation::Index(0))), b"\x31\x02\x01\x00", ), ], |p| Ok(p.read_element::>()?.collect()), ); } #[test] fn test_parse_optional() { assert_parses_cb( &[ (Ok((Some(true), None)), b"\x01\x01\xff"), (Ok((Some(false), None)), b"\x01\x01\x00"), (Ok((None, Some(18))), b"\x02\x01\x12"), (Ok((Some(true), Some(18))), b"\x01\x01\xff\x02\x01\x12"), (Ok((None, None)), b""), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x01", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x02", ), ], |p| { Ok(( p.read_element::>()?, p.read_element::>()?, )) }, ); assert_parses::>>(&[ ( Ok(Some(Tlv { tag: Tag::primitive(0x4), data: b"abc", full_data: b"\x04\x03abc", })), b"\x04\x03abc", ), (Ok(None), b""), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"\x04", ), ]); assert_parses::>>(&[ (Ok(None), b""), (Ok(Some(Choice2::ChoiceA(17))), b"\x02\x01\x11"), (Ok(Some(Choice2::ChoiceB(true))), b"\x01\x01\xff"), (Err(ParseError::new(ParseErrorKind::ExtraData)), b"\x03\x00"), ]); } #[test] fn test_choice1() { assert_parses::>(&[ (Ok(Choice1::ChoiceA(true)), b"\x01\x01\xff"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x03), })), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), ]); } #[test] fn test_choice2() { assert_parses::>(&[ (Ok(Choice2::ChoiceA(true)), b"\x01\x01\xff"), (Ok(Choice2::ChoiceB(18)), b"\x02\x01\x12"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x03), })), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), ]); } #[test] fn test_choice3() { assert_parses::>(&[ (Ok(Choice3::ChoiceA(true)), b"\x01\x01\xff"), (Ok(Choice3::ChoiceB(18)), b"\x02\x01\x12"), (Ok(Choice3::ChoiceC(())), b"\x05\x00"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x03), })), b"\x03\x00", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), ]); } #[test] fn test_parse_implicit() { assert_parses::>(&[ (Ok(Implicit::new(true)), b"\x82\x01\xff"), (Ok(Implicit::new(false)), b"\x82\x01\x00"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x01), })), b"\x01\x01\xff", ), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x02), })), b"\x02\x01\xff", ), ]); assert_parses::, 2>>(&[ (Ok(Implicit::new(Sequence::new(b"abc"))), b"\xa2\x03abc"), (Ok(Implicit::new(Sequence::new(b""))), b"\xa2\x00"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x01), })), b"\x01\x01\xff", ), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x02), })), b"\x02\x01\xff", ), ( Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), b"", ), ]); } #[test] fn test_parse_explicit() { assert_parses::>(&[ (Ok(Explicit::new(true)), b"\xa2\x03\x01\x01\xff"), (Ok(Explicit::new(false)), b"\xa2\x03\x01\x01\x00"), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x01), })), b"\x01\x01\xff", ), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x02), })), b"\x02\x01\xff", ), ( Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x03), })), b"\xa2\x03\x03\x01\xff", ), ]); } #[test] fn test_parse_box() { assert_parses::>(&[ (Ok(Box::new(12u8)), b"\x02\x01\x0c"), (Ok(Box::new(0)), b"\x02\x01\x00"), ]); } } asn1-0.17.0/src/tag.rs000064400000000000000000000110321046102023000124600ustar 00000000000000use crate::base128; use crate::parser::{ParseError, ParseErrorKind, ParseResult}; use crate::writer::{WriteBuf, WriteResult}; #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub(crate) enum TagClass { Universal = 0b00, Application = 0b01, ContextSpecific = 0b10, Private = 0b11, } #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct Tag { value: u32, constructed: bool, class: TagClass, } pub(crate) const CONSTRUCTED: u8 = 0x20; impl Tag { /// Parses a `Tag` from bytes and returns either the `Tag` and the /// remaining bytes from the input or an error. pub fn from_bytes(mut data: &[u8]) -> ParseResult<(Tag, &[u8])> { let tag = match data.first() { Some(&b) => b, None => return Err(ParseError::new(ParseErrorKind::ShortData { needed: 1 })), }; data = &data[1..]; let mut value = u32::from(tag & 0x1f); let constructed = tag & CONSTRUCTED == CONSTRUCTED; let tag_class_bits = tag >> 6; let class = if tag_class_bits == TagClass::Universal as u8 { TagClass::Universal } else if tag_class_bits == TagClass::Application as u8 { TagClass::Application } else if tag_class_bits == TagClass::ContextSpecific as u8 { TagClass::ContextSpecific } else { assert!(tag_class_bits == TagClass::Private as u8); TagClass::Private }; // Long form tag if value == 0x1f { let result = base128::read_base128_int(data).map_err(|e| { if matches!(e.kind(), ParseErrorKind::ShortData { .. }) { e } else { ParseError::new(ParseErrorKind::InvalidTag) } })?; // MSRV of 1.59 required for `(value, data) = ...;` value = result.0; data = result.1; // Tags must be encoded in minimal form. if value < 0x1f { return Err(ParseError::new(ParseErrorKind::InvalidTag)); } } Ok(( Tag { value, constructed, class, }, data, )) } pub(crate) const fn new(tag: u32, class: TagClass, constructed: bool) -> Tag { Tag { value: tag, constructed, class, } } /// This is `pub` for use in tests but is not considered part of the /// supported API. #[doc(hidden)] pub const fn primitive(tag: u32) -> Tag { Tag::new(tag, TagClass::Universal, false) } pub(crate) const fn constructed(tag: u32) -> Tag { Tag::new(tag, TagClass::Universal, true) } /// Returns the tag's representation (including tag class and constructed /// bits) as a `u8` if the `value` component fits in a short form /// (value < 31) or `None` if this is a long-form tag. pub fn as_u8(self) -> Option { if self.value >= 0x1f { return None; } Some( ((self.class as u8) << 6) | if self.constructed { CONSTRUCTED } else { 0 } | (self.value as u8), ) } pub(crate) fn write_bytes(self, dest: &mut WriteBuf) -> WriteResult { let mut b = ((self.class as u8) << 6) | if self.constructed { CONSTRUCTED } else { 0 }; if self.value >= 0x1f { b |= 0x1f; dest.push_byte(b)?; let len = base128::base128_length(self.value); let orig_len = dest.len(); for _ in 0..len { dest.push_byte(0)?; } base128::write_base128_int(&mut dest.as_mut_slice()[orig_len..], self.value); } else { b |= self.value as u8; dest.push_byte(b)?; } Ok(()) } pub(crate) const fn is_constructed(self) -> bool { self.constructed } } #[cfg(test)] mod tests { use super::{Tag, TagClass, CONSTRUCTED}; #[test] fn test_constructed() { for i in 0..31 { let tag = Tag::constructed(u32::from(i)); assert_eq!(tag.as_u8(), Some(CONSTRUCTED | i)); assert!(tag.is_constructed()); } } #[test] fn test_as_u8() { for (t, expected) in &[ (Tag::new(5, TagClass::Application, true), Some(0x65)), (Tag::new(5, TagClass::Universal, false), Some(0x05)), (Tag::new(0x1f, TagClass::Universal, false), None), ] { assert_eq!(&t.as_u8(), expected); } } } asn1-0.17.0/src/types.rs000064400000000000000000001646621046102023000130730ustar 00000000000000#[cfg(not(feature = "std"))] use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use core::borrow::Borrow; use core::hash::{Hash, Hasher}; use core::marker::PhantomData; use core::mem; use crate::writer::Writer; use crate::{ parse, parse_single, BitString, ObjectIdentifier, OwnedBitString, ParseError, ParseErrorKind, ParseLocation, ParseResult, Parser, Tag, WriteBuf, WriteResult, }; /// Any type that can be parsed as DER ASN.1. pub trait Asn1Readable<'a>: Sized { fn parse(parser: &mut Parser<'a>) -> ParseResult; fn can_parse(tag: Tag) -> bool; } /// Types with a fixed-tag that can be parsed as DER ASN.1 pub trait SimpleAsn1Readable<'a>: Sized { const TAG: Tag; fn parse_data(data: &'a [u8]) -> ParseResult; } impl<'a, T: SimpleAsn1Readable<'a>> Asn1Readable<'a> for T { #[inline] fn parse(parser: &mut Parser<'a>) -> ParseResult { let tlv = parser.read_tlv()?; if !Self::can_parse(tlv.tag) { return Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: tlv.tag, })); } Self::parse_data(tlv.data) } #[inline] fn can_parse(tag: Tag) -> bool { tag == Self::TAG } } impl<'a, T: SimpleAsn1Readable<'a>> SimpleAsn1Readable<'a> for Box { const TAG: Tag = T::TAG; fn parse_data(data: &'a [u8]) -> ParseResult { Ok(Box::new(T::parse_data(data)?)) } } /// Any type that can be written as DER ASN.1. pub trait Asn1Writable: Sized { fn write(&self, dest: &mut Writer<'_>) -> WriteResult; } // Types with a fixed-tag that can be written as DER ASN.1. pub trait SimpleAsn1Writable: Sized { const TAG: Tag; fn write_data(&self, dest: &mut WriteBuf) -> WriteResult; } pub trait Asn1DefinedByReadable<'a, T: Asn1Readable<'a>>: Sized { fn parse(item: T, parser: &mut Parser<'a>) -> ParseResult; } pub trait Asn1DefinedByWritable: Sized { fn item(&self) -> &T; fn write(&self, dest: &mut Writer<'_>) -> WriteResult; } impl Asn1Writable for T { #[inline] fn write(&self, w: &mut Writer<'_>) -> WriteResult { w.write_tlv(Self::TAG, move |dest| self.write_data(dest)) } } impl SimpleAsn1Writable for &T { const TAG: Tag = T::TAG; fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { T::write_data(self, dest) } } impl SimpleAsn1Writable for Box { const TAG: Tag = T::TAG; fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { T::write_data(self, dest) } } /// A TLV (type, length, value) represented as the tag and bytes content. /// Generally used for parsing ASN.1 `ANY` values. #[derive(Debug, PartialEq, Hash, Clone, Copy, Eq)] pub struct Tlv<'a> { pub(crate) tag: Tag, // `data` is the value of a TLV pub(crate) data: &'a [u8], // `full_data` contains the encoded type and length, in addition to the // value pub(crate) full_data: &'a [u8], } impl<'a> Tlv<'a> { /// The tag portion of a TLV. pub fn tag(&self) -> Tag { self.tag } /// The value portion of the TLV. pub fn data(&self) -> &'a [u8] { self.data } /// The full DER encoded TLV. pub fn full_data(&self) -> &'a [u8] { self.full_data } /// Parse this TLV as a given type. pub fn parse>(&self) -> ParseResult { parse_single::(self.full_data) } } impl<'a> Asn1Readable<'a> for Tlv<'a> { #[inline] fn parse(parser: &mut Parser<'a>) -> ParseResult { parser.read_tlv() } #[inline] fn can_parse(_tag: Tag) -> bool { true } } impl<'a> Asn1Writable for Tlv<'a> { #[inline] fn write(&self, w: &mut Writer<'_>) -> WriteResult { w.write_tlv(self.tag, move |dest| dest.push_slice(self.data)) } } /// The ASN.1 NULL type, for use with `Parser.read_element` and /// `Writer.write_element`. pub type Null = (); impl SimpleAsn1Readable<'_> for Null { const TAG: Tag = Tag::primitive(0x05); #[inline] fn parse_data(data: &[u8]) -> ParseResult { if data.is_empty() { Ok(()) } else { Err(ParseError::new(ParseErrorKind::InvalidValue)) } } } impl SimpleAsn1Writable for Null { const TAG: Tag = Tag::primitive(0x05); #[inline] fn write_data(&self, _dest: &mut WriteBuf) -> WriteResult { Ok(()) } } impl SimpleAsn1Readable<'_> for bool { const TAG: Tag = Tag::primitive(0x1); fn parse_data(data: &[u8]) -> ParseResult { match data { b"\x00" => Ok(false), b"\xff" => Ok(true), _ => Err(ParseError::new(ParseErrorKind::InvalidValue)), } } } impl SimpleAsn1Writable for bool { const TAG: Tag = Tag::primitive(0x1); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { if *self { dest.push_byte(0xff) } else { dest.push_byte(0x00) } } } impl<'a> SimpleAsn1Readable<'a> for &'a [u8] { const TAG: Tag = Tag::primitive(0x04); fn parse_data(data: &'a [u8]) -> ParseResult<&'a [u8]> { Ok(data) } } impl<'a> SimpleAsn1Writable for &'a [u8] { const TAG: Tag = Tag::primitive(0x04); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self) } } impl SimpleAsn1Readable<'_> for [u8; N] { const TAG: Tag = Tag::primitive(0x04); fn parse_data(data: &[u8]) -> ParseResult<[u8; N]> { data.try_into() .map_err(|_| ParseError::new(ParseErrorKind::InvalidValue)) } } impl SimpleAsn1Writable for [u8; N] { const TAG: Tag = Tag::primitive(0x04); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self) } } /// Represents values that are encoded as an `OCTET STRING` containing an /// encoded TLV, of type `T`. #[derive(PartialEq, Eq, Debug)] pub struct OctetStringEncoded(T); impl OctetStringEncoded { pub fn new(v: T) -> OctetStringEncoded { OctetStringEncoded(v) } pub fn get(&self) -> &T { &self.0 } pub fn into_inner(self) -> T { self.0 } } impl<'a, T: Asn1Readable<'a>> SimpleAsn1Readable<'a> for OctetStringEncoded { const TAG: Tag = Tag::primitive(0x04); fn parse_data(data: &'a [u8]) -> ParseResult { Ok(OctetStringEncoded::new(parse_single(data)?)) } } impl SimpleAsn1Writable for OctetStringEncoded { const TAG: Tag = Tag::primitive(0x04); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { self.0.write(&mut Writer::new(dest)) } } /// Type for use with `Parser.read_element` and `Writer.write_element` for /// handling ASN.1 `PrintableString`. A `PrintableString` contains an `&str` /// with only valid characers. #[derive(Clone, Debug, PartialEq, Eq)] pub struct PrintableString<'a>(&'a str); impl<'a> PrintableString<'a> { pub fn new(s: &'a str) -> Option> { if PrintableString::verify(s.as_bytes()) { Some(PrintableString(s)) } else { None } } fn new_from_bytes(s: &'a [u8]) -> Option> { if PrintableString::verify(s) { // TODO: This value is always valid utf-8 because we just verified // the contents, but I don't want to call an unsafe function, so we // end up validating it twice. If your profile says this is slow, // now you know why. Some(PrintableString(core::str::from_utf8(s).unwrap())) } else { None } } pub fn as_str(&self) -> &'a str { self.0 } fn verify(data: &[u8]) -> bool { for b in data { match 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'?' => {} _ => return false, }; } true } } impl<'a> SimpleAsn1Readable<'a> for PrintableString<'a> { const TAG: Tag = Tag::primitive(0x13); fn parse_data(data: &'a [u8]) -> ParseResult { PrintableString::new_from_bytes(data) .ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for PrintableString<'a> { const TAG: Tag = Tag::primitive(0x13); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.0.as_bytes()) } } /// Type for use with `Parser.read_element` and `Writer.write_element` for /// handling ASN.1 `IA5String`. An `IA5String` contains an `&str` /// with only valid characers. #[derive(Clone, Debug, PartialEq, Eq)] pub struct IA5String<'a>(&'a str); impl<'a> IA5String<'a> { pub fn new(s: &'a str) -> Option> { if IA5String::verify(s.as_bytes()) { Some(IA5String(s)) } else { None } } fn new_from_bytes(s: &'a [u8]) -> Option> { if IA5String::verify(s) { // TODO: This value is always valid utf-8 because we just verified // the contents, but I don't want to call an unsafe function, so we // end up validating it twice. If your profile says this is slow, // now you know why. Some(IA5String(core::str::from_utf8(s).unwrap())) } else { None } } fn verify(s: &[u8]) -> bool { s.is_ascii() } pub fn as_str(&self) -> &'a str { self.0 } } impl<'a> SimpleAsn1Readable<'a> for IA5String<'a> { const TAG: Tag = Tag::primitive(0x16); fn parse_data(data: &'a [u8]) -> ParseResult { IA5String::new_from_bytes(data).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for IA5String<'a> { const TAG: Tag = Tag::primitive(0x16); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.0.as_bytes()) } } /// Type for use with `Parser.read_element` and `Writer.write_element` for /// handling ASN.1 `UTF8String`. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Utf8String<'a>(&'a str); impl<'a> Utf8String<'a> { pub fn new(s: &'a str) -> Utf8String<'a> { Utf8String(s) } fn new_from_bytes(s: &'a [u8]) -> Option> { Some(Utf8String(core::str::from_utf8(s).ok()?)) } pub fn as_str(&self) -> &'a str { self.0 } } impl<'a> SimpleAsn1Readable<'a> for Utf8String<'a> { const TAG: Tag = Tag::primitive(0x0c); fn parse_data(data: &'a [u8]) -> ParseResult { Utf8String::new_from_bytes(data) .ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for Utf8String<'a> { const TAG: Tag = Tag::primitive(0x0c); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.0.as_bytes()) } } /// Type for use with `Parser.read_element` and `Writer.write_element` for /// handling ASN.1 `VisibleString`. An `VisibleString` contains an `&str` /// with only valid characers. #[derive(Clone, Debug, PartialEq, Eq)] pub struct VisibleString<'a>(&'a str); impl<'a> VisibleString<'a> { pub fn new(s: &'a str) -> Option> { if VisibleString::verify(s.as_bytes()) { Some(VisibleString(s)) } else { None } } fn new_from_bytes(s: &'a [u8]) -> Option> { if VisibleString::verify(s) { // TODO: This value is always valid utf-8 because we just verified // the contents, but I don't want to call an unsafe function, so we // end up validating it twice. If your profile says this is slow, // now you know why. Some(VisibleString(core::str::from_utf8(s).unwrap())) } else { None } } fn verify(s: &[u8]) -> bool { for b in s { if !(b.is_ascii_graphic() || *b == b' ') { return false; } } true } pub fn as_str(&self) -> &'a str { self.0 } } impl<'a> SimpleAsn1Readable<'a> for VisibleString<'a> { const TAG: Tag = Tag::primitive(0x1a); fn parse_data(data: &'a [u8]) -> ParseResult { VisibleString::new_from_bytes(data) .ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for VisibleString<'a> { const TAG: Tag = Tag::primitive(0x1a); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.0.as_bytes()) } } /// Type for use with `Parser.read_element` and `Writer.write_element` for /// handling ASN.1 `BMPString`. A `BMPString` contains encoded (UTF-16-BE) /// bytes which are known to be valid. #[derive(Debug, PartialEq, Clone, Eq)] pub struct BMPString<'a>(&'a [u8]); impl<'a> BMPString<'a> { pub fn new(b: &'a [u8]) -> Option> { if BMPString::verify(b) { Some(BMPString(b)) } else { None } } fn verify(b: &[u8]) -> bool { if b.len() % 2 == 1 { return false; } for r in core::char::decode_utf16( b.chunks_exact(2) .map(|v| u16::from_be_bytes(v.try_into().unwrap())), ) { if r.is_err() { return false; } } true } pub fn as_utf16_be_bytes(&self) -> &'a [u8] { self.0 } } impl<'a> SimpleAsn1Readable<'a> for BMPString<'a> { const TAG: Tag = Tag::primitive(0x1e); fn parse_data(data: &'a [u8]) -> ParseResult { BMPString::new(data).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for BMPString<'a> { const TAG: Tag = Tag::primitive(0x1e); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_utf16_be_bytes()) } } /// Type for use with `Parser.read_element` and `Writer.write_element` for /// handling ASN.1 `UniversalString`. A `UniversalString` contains encoded /// (UTF-32-BE) bytes which are known to be valid. #[derive(Debug, PartialEq, Clone, Eq)] pub struct UniversalString<'a>(&'a [u8]); impl<'a> UniversalString<'a> { pub fn new(b: &'a [u8]) -> Option> { if UniversalString::verify(b) { Some(UniversalString(b)) } else { None } } fn verify(b: &[u8]) -> bool { if b.len() % 4 != 0 { return false; } for r in b .chunks_exact(4) .map(|v| u32::from_be_bytes(v.try_into().unwrap())) { if core::char::from_u32(r).is_none() { return false; } } true } pub fn as_utf32_be_bytes(&self) -> &'a [u8] { self.0 } } impl<'a> SimpleAsn1Readable<'a> for UniversalString<'a> { const TAG: Tag = Tag::primitive(0x1c); fn parse_data(data: &'a [u8]) -> ParseResult { UniversalString::new(data).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for UniversalString<'a> { const TAG: Tag = Tag::primitive(0x1c); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_utf32_be_bytes()) } } fn validate_integer(data: &[u8], signed: bool) -> ParseResult<()> { if data.is_empty() { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } // Ensure integer is minimally encoded if data.len() > 1 && ((data[0] == 0 && data[1] & 0x80 == 0) || (data[0] == 0xff && data[1] & 0x80 == 0x80)) { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } // Reject negatives for unsigned types. if !signed && data[0] & 0x80 == 0x80 { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } Ok(()) } macro_rules! impl_asn1_element_for_int { ($t:ty; $signed:expr) => { impl SimpleAsn1Readable<'_> for $t { const TAG: Tag = Tag::primitive(0x02); #[inline] fn parse_data(mut data: &[u8]) -> ParseResult { validate_integer(data, $signed)?; // If we've got something like \x00\xff trim off the first \x00, since it's just // there to not mark the value as a negative. if !$signed && data.len() == mem::size_of::() + 1 && data[0] == 0 { data = &data[1..]; } if data.len() > mem::size_of::() { return Err(ParseError::new(ParseErrorKind::IntegerOverflow)); } let mut fixed_data = [0; mem::size_of::<$t>()]; fixed_data[mem::size_of::() - data.len()..].copy_from_slice(data); let mut ret = Self::from_be_bytes(fixed_data); // Shift up and down in order to sign extend the result. ret <<= (8 * mem::size_of::()) - data.len() * 8; ret >>= (8 * mem::size_of::()) - data.len() * 8; Ok(ret) } } impl SimpleAsn1Writable for $t { const TAG: Tag = Tag::primitive(0x02); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let mut num_bytes = 1; let mut v: $t = *self; #[allow(unused_comparisons)] while v > 127 || ($signed && v < (-128i64) as $t) { num_bytes += 1; v = v.checked_shr(8).unwrap_or(0); } for i in (1..=num_bytes).rev() { let digit = self.checked_shr((i - 1) * 8).unwrap_or(0); dest.push_byte(digit as u8)?; } Ok(()) } } }; } impl_asn1_element_for_int!(i8; true); impl_asn1_element_for_int!(u8; false); impl_asn1_element_for_int!(i16; true); impl_asn1_element_for_int!(u16; false); impl_asn1_element_for_int!(i32; true); impl_asn1_element_for_int!(u32; false); impl_asn1_element_for_int!(i64; true); impl_asn1_element_for_int!(u64; false); /// Arbitrary sized unsigned integer. Contents may be accessed as `&[u8]` of /// big-endian data. Its contents always match the DER encoding of a value /// (i.e. they are minimal) #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq)] pub struct BigUint<'a> { data: &'a [u8], } impl<'a> BigUint<'a> { /// Create a new `BigUint` from already encoded data. `data` must be encoded /// as required by DER: minimally and if the high bit would be set in the /// first octet, a leading `\x00` should be prepended (to disambiguate from /// negative values). pub fn new(data: &'a [u8]) -> Option { validate_integer(data, false).ok()?; Some(BigUint { data }) } /// Returns the contents of the integer as big-endian bytes. pub fn as_bytes(&self) -> &'a [u8] { self.data } } impl<'a> SimpleAsn1Readable<'a> for BigUint<'a> { const TAG: Tag = Tag::primitive(0x02); fn parse_data(data: &'a [u8]) -> ParseResult { BigUint::new(data).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for BigUint<'a> { const TAG: Tag = Tag::primitive(0x02); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_bytes()) } } /// Arbitrary sized unsigned integer which owns its data. Contents may be /// accessed as `&[u8]` of big-endian data. Its contents always match the DER /// encoding of a value (i.e. they are minimal) #[derive(PartialEq, Clone, Debug, Hash, Eq)] pub struct OwnedBigUint { data: Vec, } impl OwnedBigUint { /// Create a new `OwnedBigUint` from already encoded data. `data` must be /// encoded as required by DER: minimally and if the high bit would be set /// in the first octet, a leading `\x00` should be prepended (to /// disambiguate from negative values). pub fn new(data: Vec) -> Option { validate_integer(&data, false).ok()?; Some(OwnedBigUint { data }) } /// Returns the contents of the integer as big-endian bytes. pub fn as_bytes(&self) -> &[u8] { &self.data } } impl SimpleAsn1Readable<'_> for OwnedBigUint { const TAG: Tag = Tag::primitive(0x02); fn parse_data(data: &[u8]) -> ParseResult { OwnedBigUint::new(data.to_vec()) .ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl SimpleAsn1Writable for OwnedBigUint { const TAG: Tag = Tag::primitive(0x02); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_bytes()) } } /// Arbitrary sized signed integer. Contents may be accessed as `&[u8]` of /// big-endian data. Its contents always match the DER encoding of a value /// (i.e. they are minimal) #[derive(PartialEq, Clone, Copy, Debug, Hash, Eq)] pub struct BigInt<'a> { data: &'a [u8], } impl<'a> BigInt<'a> { /// Create a new `BigInt` from already encoded data. `data` must be encoded /// as required by DER: minimally and if the high bit would be set in the /// first octet, a leading `\x00` should be prepended (to disambiguate from /// negative values). pub fn new(data: &'a [u8]) -> Option { validate_integer(data, true).ok()?; Some(BigInt { data }) } /// Returns the contents of the integer as big-endian bytes. pub fn as_bytes(&self) -> &'a [u8] { self.data } /// Returns a boolean indicating whether the integer is negative. pub fn is_negative(&self) -> bool { self.data[0] & 0x80 == 0x80 } } impl<'a> SimpleAsn1Readable<'a> for BigInt<'a> { const TAG: Tag = Tag::primitive(0x02); fn parse_data(data: &'a [u8]) -> ParseResult { BigInt::new(data).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for BigInt<'a> { const TAG: Tag = Tag::primitive(0x02); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_bytes()) } } /// Arbitrary sized signed integer which owns its contents. Contents may be /// accessed as `&[u8]` of big-endian data. Its contents always match the DER /// encoding of a value (i.e. they are minimal) #[derive(PartialEq, Clone, Debug, Hash, Eq)] pub struct OwnedBigInt { data: Vec, } impl OwnedBigInt { /// Create a new `OwnedBigInt` from already encoded data. `data` must be /// encoded as required by DER: minimally and if the high bit would be set /// in the first octet, a leading `\x00` should be prepended (to /// disambiguate from negative values). pub fn new(data: Vec) -> Option { validate_integer(&data, true).ok()?; Some(OwnedBigInt { data }) } /// Returns the contents of the integer as big-endian bytes. pub fn as_bytes(&self) -> &[u8] { &self.data } /// Returns a boolean indicating whether the integer is negative. pub fn is_negative(&self) -> bool { self.data[0] & 0x80 == 0x80 } } impl SimpleAsn1Readable<'_> for OwnedBigInt { const TAG: Tag = Tag::primitive(0x02); fn parse_data(data: &[u8]) -> ParseResult { OwnedBigInt::new(data.to_vec()).ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl SimpleAsn1Writable for OwnedBigInt { const TAG: Tag = Tag::primitive(0x02); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_bytes()) } } impl<'a> SimpleAsn1Readable<'a> for ObjectIdentifier { const TAG: Tag = Tag::primitive(0x06); fn parse_data(data: &'a [u8]) -> ParseResult { ObjectIdentifier::from_der(data) } } impl SimpleAsn1Writable for ObjectIdentifier { const TAG: Tag = Tag::primitive(0x06); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_slice(self.as_der()) } } impl<'a> SimpleAsn1Readable<'a> for BitString<'a> { const TAG: Tag = Tag::primitive(0x03); fn parse_data(data: &'a [u8]) -> ParseResult> { if data.is_empty() { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } BitString::new(&data[1..], data[0]) .ok_or_else(|| ParseError::new(ParseErrorKind::InvalidValue)) } } impl<'a> SimpleAsn1Writable for BitString<'a> { const TAG: Tag = Tag::primitive(0x03); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { dest.push_byte(self.padding_bits())?; dest.push_slice(self.as_bytes()) } } impl<'a> SimpleAsn1Readable<'a> for OwnedBitString { const TAG: Tag = Tag::primitive(0x03); fn parse_data(data: &'a [u8]) -> ParseResult { let bs = BitString::parse_data(data)?; Ok(OwnedBitString::new(bs.as_bytes().to_vec(), bs.padding_bits()).unwrap()) } } impl SimpleAsn1Writable for OwnedBitString { const TAG: Tag = Tag::primitive(0x03); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { self.as_bitstring().write_data(dest) } } fn read_byte(data: &mut &[u8]) -> ParseResult { if data.is_empty() { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } let result = Ok(data[0]); *data = &data[1..]; result } fn read_digit(data: &mut &[u8]) -> ParseResult { let b = read_byte(data)?; if !b.is_ascii_digit() { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } Ok(b - b'0') } fn read_2_digits(data: &mut &[u8]) -> ParseResult { Ok(read_digit(data)? * 10 + read_digit(data)?) } fn read_4_digits(data: &mut &[u8]) -> ParseResult { Ok(u16::from(read_digit(data)?) * 1000 + u16::from(read_digit(data)?) * 100 + u16::from(read_digit(data)?) * 10 + u16::from(read_digit(data)?)) } fn validate_date(year: u16, month: u8, day: u8) -> ParseResult<()> { if day < 1 { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } let days_in_month = match month { 1 | 3 | 5 | 7 | 8 | 10 | 12 => 31, 4 | 6 | 9 | 11 => 30, 2 => { if (year % 4 == 0 && year % 100 != 0) || year % 400 == 0 { 29 } else { 28 } } _ => return Err(ParseError::new(ParseErrorKind::InvalidValue)), }; if day > days_in_month { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } Ok(()) } fn read_tz_and_finish(data: &mut &[u8]) -> ParseResult<()> { if read_byte(data)? != b'Z' { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } if !data.is_empty() { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } Ok(()) } fn push_two_digits(dest: &mut WriteBuf, val: u8) -> WriteResult { dest.push_byte(b'0' + ((val / 10) % 10))?; dest.push_byte(b'0' + (val % 10)) } fn push_four_digits(dest: &mut WriteBuf, val: u16) -> WriteResult { dest.push_byte(b'0' + ((val / 1000) % 10) as u8)?; dest.push_byte(b'0' + ((val / 100) % 10) as u8)?; dest.push_byte(b'0' + ((val / 10) % 10) as u8)?; dest.push_byte(b'0' + (val % 10) as u8) } /// A structure representing a (UTC timezone) date and time. /// Wrapped by `UtcTime` and `GeneralizedTime`. #[derive(Debug, Clone, PartialEq, Hash, Eq, PartialOrd)] pub struct DateTime { year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8, } impl DateTime { pub fn new( year: u16, month: u8, day: u8, hour: u8, minute: u8, second: u8, ) -> ParseResult { validate_date(year, month, day)?; if hour > 23 || minute > 59 || second > 59 { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } Ok(DateTime { year, month, day, hour, minute, second, }) } /// The calendar year. pub fn year(&self) -> u16 { self.year } /// The calendar month (1 to 12). pub fn month(&self) -> u8 { self.month } /// The calendar day (1 to 31). pub fn day(&self) -> u8 { self.day } /// The clock hour (0 to 23). pub fn hour(&self) -> u8 { self.hour } /// The clock minute (0 to 59). pub fn minute(&self) -> u8 { self.minute } /// The clock second (0 to 59). pub fn second(&self) -> u8 { self.second } } /// Used for parsing and writing ASN.1 `UTC TIME` values. Wraps a /// `DateTime`. #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct UtcTime(DateTime); impl UtcTime { pub fn new(dt: DateTime) -> ParseResult { if dt.year() < 1950 || dt.year() >= 2050 { return Err(ParseError::new(ParseErrorKind::InvalidValue)); } Ok(UtcTime(dt)) } pub fn as_datetime(&self) -> &DateTime { &self.0 } } impl SimpleAsn1Readable<'_> for UtcTime { const TAG: Tag = Tag::primitive(0x17); fn parse_data(mut data: &[u8]) -> ParseResult { let year = u16::from(read_2_digits(&mut data)?); let month = read_2_digits(&mut data)?; let day = read_2_digits(&mut data)?; // UTCTime only encodes times prior to 2050. We use the X.509 mapping of two-digit // year ordinals to full year: // https://tools.ietf.org/html/rfc5280#section-4.1.2.5.1 let year = if year >= 50 { 1900 + year } else { 2000 + year }; let hour = read_2_digits(&mut data)?; let minute = read_2_digits(&mut data)?; let second = read_2_digits(&mut data)?; read_tz_and_finish(&mut data)?; UtcTime::new(DateTime::new(year, month, day, hour, minute, second)?) } } impl SimpleAsn1Writable for UtcTime { const TAG: Tag = Tag::primitive(0x17); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let dt = self.as_datetime(); let year = if 1950 <= dt.year() && dt.year() < 2000 { dt.year() - 1900 } else { assert!(2000 <= dt.year() && dt.year() < 2050); dt.year() - 2000 }; push_two_digits(dest, year.try_into().unwrap())?; push_two_digits(dest, dt.month())?; push_two_digits(dest, dt.day())?; push_two_digits(dest, dt.hour())?; push_two_digits(dest, dt.minute())?; push_two_digits(dest, dt.second())?; dest.push_byte(b'Z') } } /// Used for parsing and writing ASN.1 `GENERALIZED TIME` values. Wraps a /// `DateTime`. #[derive(Debug, Clone, PartialEq, Hash, Eq)] pub struct GeneralizedTime(DateTime); impl GeneralizedTime { pub fn new(dt: DateTime) -> ParseResult { Ok(GeneralizedTime(dt)) } pub fn as_datetime(&self) -> &DateTime { &self.0 } } impl SimpleAsn1Readable<'_> for GeneralizedTime { const TAG: Tag = Tag::primitive(0x18); fn parse_data(mut data: &[u8]) -> ParseResult { let year = read_4_digits(&mut data)?; let month = read_2_digits(&mut data)?; let day = read_2_digits(&mut data)?; let hour = read_2_digits(&mut data)?; let minute = read_2_digits(&mut data)?; let second = read_2_digits(&mut data)?; read_tz_and_finish(&mut data)?; GeneralizedTime::new(DateTime::new(year, month, day, hour, minute, second)?) } } impl SimpleAsn1Writable for GeneralizedTime { const TAG: Tag = Tag::primitive(0x18); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let dt = self.as_datetime(); push_four_digits(dest, dt.year())?; push_two_digits(dest, dt.month())?; push_two_digits(dest, dt.day())?; push_two_digits(dest, dt.hour())?; push_two_digits(dest, dt.minute())?; push_two_digits(dest, dt.second())?; dest.push_byte(b'Z') } } /// An ASN.1 `ENUMERATED` value. #[derive(Debug, PartialEq, Eq)] pub struct Enumerated(u32); impl Enumerated { pub fn new(v: u32) -> Enumerated { Enumerated(v) } pub fn value(&self) -> u32 { self.0 } } impl<'a> SimpleAsn1Readable<'a> for Enumerated { const TAG: Tag = Tag::primitive(0xa); fn parse_data(data: &'a [u8]) -> ParseResult { Ok(Enumerated::new(u32::parse_data(data)?)) } } impl SimpleAsn1Writable for Enumerated { const TAG: Tag = Tag::primitive(0xa); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { u32::write_data(&self.value(), dest) } } impl<'a, T: Asn1Readable<'a>> Asn1Readable<'a> for Option { fn parse(parser: &mut Parser<'a>) -> ParseResult { match parser.peek_tag() { Some(tag) if Self::can_parse(tag) => Ok(Some(parser.read_element::()?)), Some(_) | None => Ok(None), } } #[inline] fn can_parse(tag: Tag) -> bool { T::can_parse(tag) } } impl Asn1Writable for Option { #[inline] fn write(&self, w: &mut Writer<'_>) -> WriteResult { if let Some(v) = self { w.write_element(v) } else { Ok(()) } } } macro_rules! declare_choice { ($count:ident => $(($number:ident $name:ident)),*) => { /// Represents an ASN.1 `CHOICE` with the provided number of potential types. /// /// If you need more variants than are provided, please file an issue or submit a pull /// request! #[derive(Debug, PartialEq, Eq)] pub enum $count< $($number,)* > { $( $name($number), )* } impl< 'a, $( $number: Asn1Readable<'a>, )* > Asn1Readable<'a> for $count<$($number,)*> { fn parse(parser: &mut Parser<'a>) -> ParseResult { let tlv = parser.read_tlv()?; $( if $number::can_parse(tlv.tag()) { return Ok($count::$name(tlv.parse::<$number>()?)); } )* Err(ParseError::new(ParseErrorKind::UnexpectedTag{actual: tlv.tag()})) } fn can_parse(tag: Tag) -> bool { $( if $number::can_parse(tag) { return true; } )* false } } impl< $( $number: Asn1Writable, )* > Asn1Writable for $count<$($number,)*> { fn write(&self, w: &mut Writer<'_>) -> WriteResult { match self { $( $count::$name(v) => w.write_element(v), )* } } } } } declare_choice!(Choice1 => (T1 ChoiceA)); declare_choice!(Choice2 => (T1 ChoiceA), (T2 ChoiceB)); declare_choice!(Choice3 => (T1 ChoiceA), (T2 ChoiceB), (T3 ChoiceC)); /// Represents an ASN.1 `SEQUENCE`. By itself, this merely indicates a sequence of bytes that are /// claimed to form an ASN1 sequence. In almost any circumstance, you'll want to immediately call /// `Sequence.parse` on this value to decode the actual contents therein. #[derive(Debug, PartialEq, Hash, Clone, Eq)] pub struct Sequence<'a> { data: &'a [u8], } impl<'a> Sequence<'a> { #[inline] pub(crate) fn new(data: &'a [u8]) -> Sequence<'a> { Sequence { data } } /// Parses the contents of the `Sequence`. Behaves the same as the module-level `parse` /// function. pub fn parse, F: Fn(&mut Parser<'a>) -> Result>( self, f: F, ) -> Result { parse(self.data, f) } } impl<'a> SimpleAsn1Readable<'a> for Sequence<'a> { const TAG: Tag = Tag::constructed(0x10); #[inline] fn parse_data(data: &'a [u8]) -> ParseResult> { Ok(Sequence::new(data)) } } impl<'a> SimpleAsn1Writable for Sequence<'a> { const TAG: Tag = Tag::constructed(0x10); #[inline] fn write_data(&self, data: &mut WriteBuf) -> WriteResult { data.push_slice(self.data) } } /// Writes an ASN.1 `SEQUENCE` using a callback that writes the inner /// elements. pub struct SequenceWriter<'a> { f: &'a dyn Fn(&mut Writer<'_>) -> WriteResult, } impl<'a> SequenceWriter<'a> { #[inline] pub fn new(f: &'a dyn Fn(&mut Writer<'_>) -> WriteResult) -> Self { SequenceWriter { f } } } impl<'a> SimpleAsn1Writable for SequenceWriter<'a> { const TAG: Tag = Tag::constructed(0x10); #[inline] fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { (self.f)(&mut Writer::new(dest)) } } /// Represents an ASN.1 `SEQUENCE OF`. This is an `Iterator` over values that /// are decoded. pub struct SequenceOf< 'a, T: Asn1Readable<'a>, const MINIMUM_LEN: usize = 0, const MAXIMUM_LEN: usize = { usize::MAX }, > { parser: Parser<'a>, length: usize, _phantom: PhantomData, } impl<'a, T: Asn1Readable<'a>, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { #[inline] pub(crate) fn new(data: &'a [u8]) -> ParseResult> { let length = parse(data, |p| { let mut i = 0; while !p.is_empty() { p.read_element::() .map_err(|e| e.add_location(ParseLocation::Index(i)))?; i += 1; } Ok(i) })?; if length < MINIMUM_LEN || length > MAXIMUM_LEN { return Err(ParseError::new(ParseErrorKind::InvalidSize { min: MINIMUM_LEN, max: MAXIMUM_LEN, actual: length, })); } Ok(Self { length, parser: Parser::new(data), _phantom: PhantomData, }) } pub fn len(&self) -> usize { self.length } pub fn is_empty(&self) -> bool { self.len() == 0 } } impl<'a, T: Asn1Readable<'a>, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> Clone for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { fn clone(&self) -> SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { SequenceOf { parser: self.parser.clone_internal(), length: self.length, _phantom: PhantomData, } } } impl<'a, T: Asn1Readable<'a> + PartialEq, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> PartialEq for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { fn eq(&self, other: &Self) -> bool { let mut it1 = self.clone(); let mut it2 = other.clone(); loop { match (it1.next(), it2.next()) { (Some(v1), Some(v2)) => { if v1 != v2 { return false; } } (None, None) => return true, _ => return false, } } } } impl<'a, T: Asn1Readable<'a> + Eq, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> Eq for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { } impl<'a, T: Asn1Readable<'a> + Hash, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> Hash for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { fn hash(&self, state: &mut H) { for val in self.clone() { val.hash(state); } } } impl<'a, T: Asn1Readable<'a> + 'a, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> SimpleAsn1Readable<'a> for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { const TAG: Tag = Tag::constructed(0x10); #[inline] fn parse_data(data: &'a [u8]) -> ParseResult { SequenceOf::new(data) } } impl<'a, T: Asn1Readable<'a>, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize> Iterator for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { type Item = T; fn next(&mut self) -> Option { if self.parser.is_empty() { return None; } self.length -= 1; Some( self.parser .read_element::() .expect("Should always succeed"), ) } } impl< 'a, T: Asn1Readable<'a> + Asn1Writable, const MINIMUM_LEN: usize, const MAXIMUM_LEN: usize, > SimpleAsn1Writable for SequenceOf<'a, T, MINIMUM_LEN, MAXIMUM_LEN> { const TAG: Tag = Tag::constructed(0x10); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let mut w = Writer::new(dest); for el in self.clone() { w.write_element(&el)?; } Ok(()) } } /// Writes a `SEQUENCE OF` ASN.1 structure from a slice of `T`. #[derive(Hash, PartialEq, Eq, Clone)] pub struct SequenceOfWriter<'a, T: Asn1Writable, V: Borrow<[T]> = &'a [T]> { vals: V, _phantom: PhantomData<&'a T>, } impl<'a, T: Asn1Writable, V: Borrow<[T]>> SequenceOfWriter<'a, T, V> { pub fn new(vals: V) -> Self { SequenceOfWriter { vals, _phantom: PhantomData, } } } impl<'a, T: Asn1Writable, V: Borrow<[T]>> SimpleAsn1Writable for SequenceOfWriter<'a, T, V> { const TAG: Tag = Tag::constructed(0x10); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let mut w = Writer::new(dest); for el in self.vals.borrow() { w.write_element(el)?; } Ok(()) } } /// Represents an ASN.1 `SET OF`. This is an `Iterator` over values that /// are decoded. pub struct SetOf<'a, T: Asn1Readable<'a>> { parser: Parser<'a>, _phantom: PhantomData, } impl<'a, T: Asn1Readable<'a>> SetOf<'a, T> { #[inline] pub(crate) fn new(data: &'a [u8]) -> SetOf<'a, T> { SetOf { parser: Parser::new(data), _phantom: PhantomData, } } } impl<'a, T: Asn1Readable<'a>> Clone for SetOf<'a, T> { fn clone(&self) -> SetOf<'a, T> { SetOf { parser: self.parser.clone_internal(), _phantom: PhantomData, } } } impl<'a, T: Asn1Readable<'a> + PartialEq> PartialEq for SetOf<'a, T> { fn eq(&self, other: &Self) -> bool { let mut it1 = self.clone(); let mut it2 = other.clone(); loop { match (it1.next(), it2.next()) { (Some(v1), Some(v2)) => { if v1 != v2 { return false; } } (None, None) => return true, _ => return false, } } } } impl<'a, T: Asn1Readable<'a> + Eq> Eq for SetOf<'a, T> {} impl<'a, T: Asn1Readable<'a> + Hash> Hash for SetOf<'a, T> { fn hash(&self, state: &mut H) { for val in self.clone() { val.hash(state); } } } impl<'a, T: Asn1Readable<'a> + 'a> SimpleAsn1Readable<'a> for SetOf<'a, T> { const TAG: Tag = Tag::constructed(0x11); #[inline] fn parse_data(data: &'a [u8]) -> ParseResult { parse(data, |p| { let mut last_element: Option> = None; let mut i = 0; while !p.is_empty() { let el = p .read_tlv() .map_err(|e| e.add_location(ParseLocation::Index(i)))?; if let Some(last_el) = last_element { if el.full_data < last_el.full_data { return Err(ParseError::new(ParseErrorKind::InvalidSetOrdering) .add_location(ParseLocation::Index(i))); } } last_element = Some(el); el.parse::() .map_err(|e| e.add_location(ParseLocation::Index(i)))?; i += 1; } Ok(()) })?; Ok(SetOf::new(data)) } } impl<'a, T: Asn1Readable<'a>> Iterator for SetOf<'a, T> { type Item = T; fn next(&mut self) -> Option { if self.parser.is_empty() { return None; } Some( self.parser .read_element::() .expect("Should always succeed"), ) } } impl<'a, T: Asn1Readable<'a> + Asn1Writable> SimpleAsn1Writable for SetOf<'a, T> { const TAG: Tag = Tag::constructed(0x11); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let mut w = Writer::new(dest); // We are known to be ordered correctly because that's an invariant for // `self`, so we don't need to sort here. for el in self.clone() { w.write_element(&el)?; } Ok(()) } } /// Writes an ASN.1 `SET OF` whose contents is a slice of `T`. This type handles /// ensuring that the values are properly ordered when written as DER. #[derive(Hash, PartialEq, Eq, Clone)] pub struct SetOfWriter<'a, T: Asn1Writable, V: Borrow<[T]> = &'a [T]> { vals: V, _phantom: PhantomData<&'a T>, } impl<'a, T: Asn1Writable, V: Borrow<[T]>> SetOfWriter<'a, T, V> { pub fn new(vals: V) -> Self { SetOfWriter { vals, _phantom: PhantomData, } } } impl<'a, T: Asn1Writable, V: Borrow<[T]>> SimpleAsn1Writable for SetOfWriter<'a, T, V> { const TAG: Tag = Tag::constructed(0x11); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { let vals = self.vals.borrow(); if vals.is_empty() { return Ok(()); } else if vals.len() == 1 { let mut w = Writer::new(dest); w.write_element(&vals[0])?; return Ok(()); } // Optimization: use the dest storage as scratch, then truncate. let mut data = WriteBuf::new(vec![]); let mut w = Writer::new(&mut data); // Optimization opportunity: use a SmallVec here. let mut spans = vec![]; let mut pos = 0; for el in vals { w.write_element(el)?; let l = w.buf.len(); spans.push(pos..l); pos = l; } let data = data.as_slice(); spans.sort_by_key(|v| &data[v.clone()]); for span in spans { dest.push_slice(&data[span])?; } Ok(()) } } /// `Implicit` is a type which wraps another ASN.1 type, indicating that the tag is an ASN.1 /// `IMPLICIT`. This will generally be used with `Option` or `Choice`. #[derive(PartialEq, Eq, Debug)] pub struct Implicit { inner: T, } impl Implicit { pub fn new(v: T) -> Self { Implicit { inner: v } } pub fn as_inner(&self) -> &T { &self.inner } pub fn into_inner(self) -> T { self.inner } } impl From for Implicit { fn from(v: T) -> Self { Implicit::new(v) } } impl<'a, T: SimpleAsn1Readable<'a>, const TAG: u32> SimpleAsn1Readable<'a> for Implicit { const TAG: Tag = crate::implicit_tag(TAG, T::TAG); fn parse_data(data: &'a [u8]) -> ParseResult { Ok(Implicit::new(T::parse_data(data)?)) } } impl SimpleAsn1Writable for Implicit { const TAG: Tag = crate::implicit_tag(TAG, T::TAG); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { self.inner.write_data(dest) } } /// `Explicit` is a type which wraps another ASN.1 type, indicating that the tag is an ASN.1 /// `EXPLICIT`. This will generally be used with `Option` or `Choice`. #[derive(PartialEq, Eq, Debug)] pub struct Explicit { inner: T, } impl Explicit { pub fn new(v: T) -> Self { Explicit { inner: v } } pub fn as_inner(&self) -> &T { &self.inner } pub fn into_inner(self) -> T { self.inner } } impl From for Explicit { fn from(v: T) -> Self { Explicit::new(v) } } impl<'a, T: Asn1Readable<'a>, const TAG: u32> SimpleAsn1Readable<'a> for Explicit { const TAG: Tag = crate::explicit_tag(TAG); fn parse_data(data: &'a [u8]) -> ParseResult { Ok(Explicit::new(parse(data, Parser::read_element::)?)) } } impl SimpleAsn1Writable for Explicit { const TAG: Tag = crate::explicit_tag(TAG); fn write_data(&self, dest: &mut WriteBuf) -> WriteResult { Writer::new(dest).write_element(&self.inner) } } impl<'a, T: Asn1Readable<'a>, U: Asn1DefinedByReadable<'a, T>, const TAG: u32> Asn1DefinedByReadable<'a, T> for Explicit { fn parse(item: T, parser: &mut Parser<'a>) -> ParseResult { let tlv = parser.read_element::, TAG>>()?; Ok(Explicit::new(parse(tlv.as_inner().full_data(), |p| { U::parse(item, p) })?)) } } impl, const TAG: u32> Asn1DefinedByWritable for Explicit { fn item(&self) -> &T { self.as_inner().item() } fn write(&self, dest: &mut Writer<'_>) -> WriteResult { dest.write_tlv(crate::explicit_tag(TAG), |dest| { self.as_inner().write(&mut Writer::new(dest)) }) } } #[derive(PartialEq, Eq, Debug, Clone, Hash)] pub struct DefinedByMarker(core::marker::PhantomData); impl DefinedByMarker { pub const fn marker() -> DefinedByMarker { DefinedByMarker(core::marker::PhantomData) } } #[cfg(test)] mod tests { use crate::{ parse_single, BigInt, BigUint, DateTime, DefinedByMarker, Enumerated, GeneralizedTime, IA5String, ObjectIdentifier, OctetStringEncoded, OwnedBigInt, OwnedBigUint, ParseError, ParseErrorKind, PrintableString, SequenceOf, SequenceOfWriter, SetOf, SetOfWriter, Tag, Tlv, UtcTime, Utf8String, VisibleString, }; use crate::{Explicit, Implicit}; #[cfg(not(feature = "std"))] use alloc::vec; #[cfg(not(feature = "std"))] use alloc::vec::Vec; #[cfg(feature = "std")] use core::hash::{Hash, Hasher}; #[cfg(feature = "std")] use std::collections::hash_map::DefaultHasher; #[test] fn test_octet_string_encoded() { assert_eq!(OctetStringEncoded::new(12).get(), &12); assert_eq!(OctetStringEncoded::new(12).into_inner(), 12); } #[test] fn test_printable_string_new() { assert!(PrintableString::new("abc").is_some()); assert!(PrintableString::new("").is_some()); assert!(PrintableString::new(" ").is_some()); assert!(PrintableString::new("%").is_none()); assert!(PrintableString::new("\x00").is_none()); } #[test] fn test_printable_string_as_str() { assert_eq!(PrintableString::new("abc").unwrap().as_str(), "abc"); } #[test] fn test_ia5string_new() { assert!(IA5String::new("abc").is_some()); assert!(IA5String::new("").is_some()); assert!(IA5String::new(" ").is_some()); assert!(IA5String::new("%").is_some()); assert!(IA5String::new("😄").is_none()); } #[test] fn test_ia5string_as_str() { assert_eq!(IA5String::new("abc").unwrap().as_str(), "abc"); } #[test] fn test_utf8string_as_str() { assert_eq!(Utf8String::new("abc").as_str(), "abc"); } #[test] fn test_visiblestring_new() { assert!(VisibleString::new("").is_some()); assert!(VisibleString::new("abc").is_some()); assert!(VisibleString::new("\n").is_none()); } #[test] fn test_visiblestring_as_str() { assert_eq!(VisibleString::new("abc").unwrap().as_str(), "abc"); } #[test] fn test_tlv_data() { let tlv = parse_single::>(b"\x01\x03abc").unwrap(); assert_eq!(tlv.data(), b"abc"); } #[test] fn test_tlv_full_data() { let tlv = parse_single::>(b"\x01\x03abc").unwrap(); assert_eq!(tlv.full_data(), b"\x01\x03abc"); } #[test] fn test_tlv_parse() { let tlv = Tlv { tag: Tag::primitive(0x2), data: b"\x03", full_data: b"\x02\x01\x03", }; assert_eq!(tlv.parse::(), Ok(3)); assert_eq!( tlv.parse::<&[u8]>(), Err(ParseError::new(ParseErrorKind::UnexpectedTag { actual: Tag::primitive(0x2) })) ); } #[test] fn test_biguint_as_bytes() { assert_eq!(BigUint::new(b"\x01").unwrap().as_bytes(), b"\x01"); assert_eq!( OwnedBigUint::new(b"\x01".to_vec()).unwrap().as_bytes(), b"\x01" ); } #[test] fn test_bigint_as_bytes() { assert_eq!(BigInt::new(b"\x01").unwrap().as_bytes(), b"\x01"); assert_eq!( OwnedBigInt::new(b"\x01".to_vec()).unwrap().as_bytes(), b"\x01" ); } #[test] fn test_bigint_is_negative() { assert!(!BigInt::new(b"\x01").unwrap().is_negative()); // 1 assert!(!BigInt::new(b"\x00").unwrap().is_negative()); // 0 assert!(BigInt::new(b"\xff").unwrap().is_negative()); // -1 assert!(!OwnedBigInt::new(b"\x01".to_vec()).unwrap().is_negative()); // 1 assert!(!OwnedBigInt::new(b"\x00".to_vec()).unwrap().is_negative()); // 0 assert!(OwnedBigInt::new(b"\xff".to_vec()).unwrap().is_negative()); // -1 } #[test] fn test_sequence_of_clone() { let mut seq1 = parse_single::>(b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03") .unwrap(); assert_eq!(seq1.next(), Some(1)); let seq2 = seq1.clone(); assert_eq!(seq1.collect::>(), vec![2, 3]); assert_eq!(seq2.collect::>(), vec![2, 3]); } #[test] fn test_sequence_of_len() { let mut seq1 = parse_single::>(b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03") .unwrap(); let seq2 = seq1.clone(); assert_eq!(seq1.len(), 3); assert!(seq1.next().is_some()); assert_eq!(seq1.len(), 2); assert_eq!(seq2.len(), 3); assert!(seq1.next().is_some()); assert!(seq1.next().is_some()); assert_eq!(seq1.len(), 0); assert!(seq1.next().is_none()); assert_eq!(seq1.len(), 0); assert!(seq1.is_empty()); assert_eq!(seq2.len(), 3); assert!(!seq2.is_empty()); } #[cfg(feature = "std")] fn hash(v: &T) -> u64 { let mut h = DefaultHasher::new(); v.hash(&mut h); h.finish() } #[test] fn test_set_of_eq() { let s1 = SetOf::::new(b""); let s2 = SetOf::::new(b""); let s3 = SetOf::::new(b"\x01\x01\x00"); let s4 = SetOf::::new(b"\x01\x01\xff"); assert!(s1 == s2); assert!(s2 != s3); assert!(s3 == s3); assert!(s3 != s4); } #[cfg(feature = "std")] #[test] fn test_set_of_hash() { let s1 = SetOf::::new(b""); let s2 = SetOf::::new(b""); let s3 = SetOf::::new(b"\x01\x01\x00"); let s4 = SetOf::::new(b"\x01\x01\xff"); assert_eq!(hash(&s1), hash(&s2)); assert_ne!(hash(&s2), hash(&s3)); assert_ne!(hash(&s3), hash(&s4)); } #[test] fn test_sequence_of_eq() { let s1 = SequenceOf::::new(b"").unwrap(); let s2 = SequenceOf::::new(b"").unwrap(); let s3 = SequenceOf::::new(b"\x01\x01\x00").unwrap(); let s4 = SequenceOf::::new(b"\x01\x01\xff").unwrap(); assert!(s1 == s2); assert!(s2 != s3); assert!(s3 == s3); assert!(s3 != s4); } #[cfg(feature = "std")] #[test] fn test_sequence_of_hash() { let s1 = SequenceOf::::new(b"").unwrap(); let s2 = SequenceOf::::new(b"").unwrap(); let s3 = SequenceOf::::new(b"\x01\x01\x00").unwrap(); let s4 = SequenceOf::::new(b"\x01\x01\xff").unwrap(); assert_eq!(hash(&s1), hash(&s2)); assert_ne!(hash(&s2), hash(&s3)); assert_ne!(hash(&s3), hash(&s4)); } #[test] fn test_sequence_of_writer_clone() { let s1 = SequenceOfWriter::new([1, 2, 3]); let s2 = s1.clone(); assert!(s1 == s2); } #[test] fn test_set_of_writer_clone() { let s1 = SetOfWriter::new([1, 2, 3]); let s2 = s1.clone(); assert!(s1 == s2); } #[test] fn test_datetime_new() { assert!(DateTime::new(2038, 13, 1, 12, 0, 0).is_err()); assert!(DateTime::new(2000, 1, 1, 12, 60, 0).is_err()); assert!(DateTime::new(2000, 1, 1, 12, 0, 60).is_err()); assert!(DateTime::new(2000, 1, 1, 24, 0, 0).is_err()); } #[test] fn test_datetime_partialord() { let point = DateTime::new(2023, 6, 15, 14, 26, 5).unwrap(); assert!(point < DateTime::new(2023, 6, 15, 14, 26, 6).unwrap()); assert!(point < DateTime::new(2023, 6, 15, 14, 27, 5).unwrap()); assert!(point < DateTime::new(2023, 6, 15, 15, 26, 5).unwrap()); assert!(point < DateTime::new(2023, 6, 16, 14, 26, 5).unwrap()); assert!(point < DateTime::new(2023, 7, 15, 14, 26, 5).unwrap()); assert!(point < DateTime::new(2024, 6, 15, 14, 26, 5).unwrap()); assert!(point > DateTime::new(2023, 6, 15, 14, 26, 4).unwrap()); assert!(point > DateTime::new(2023, 6, 15, 14, 25, 5).unwrap()); assert!(point > DateTime::new(2023, 6, 15, 13, 26, 5).unwrap()); assert!(point > DateTime::new(2023, 6, 14, 14, 26, 5).unwrap()); assert!(point > DateTime::new(2023, 5, 15, 14, 26, 5).unwrap()); assert!(point > DateTime::new(2022, 6, 15, 14, 26, 5).unwrap()); } #[test] fn test_utctime_new() { assert!(UtcTime::new(DateTime::new(1950, 1, 1, 12, 0, 0).unwrap()).is_ok()); assert!(UtcTime::new(DateTime::new(1949, 1, 1, 12, 0, 0).unwrap()).is_err()); assert!(UtcTime::new(DateTime::new(2050, 1, 1, 12, 0, 0).unwrap()).is_err()); assert!(UtcTime::new(DateTime::new(2100, 1, 1, 12, 0, 0).unwrap()).is_err()); } #[test] fn test_generalized_time_new() { assert!(GeneralizedTime::new(DateTime::new(2015, 6, 30, 23, 59, 59).unwrap()).is_ok()); } #[test] fn test_enumerated_value() { assert_eq!(Enumerated::new(4).value(), 4); } #[test] fn test_implicit_as_inner() { assert_eq!(Implicit::::new(12).as_inner(), &12); } #[test] fn test_explicit_as_inner() { assert_eq!(Explicit::::new(12).as_inner(), &12); } #[test] fn test_const() { const _: DefinedByMarker = DefinedByMarker::marker(); } } asn1-0.17.0/src/writer.rs000064400000000000000000000612221046102023000132270ustar 00000000000000use crate::types::{Asn1Writable, SimpleAsn1Writable}; use crate::Tag; #[cfg(not(feature = "std"))] use alloc::vec::Vec; use alloc::{fmt, vec}; /// `WriteError` are returned when there is an error writing the ASN.1 data. #[derive(PartialEq, Eq, Debug)] pub enum WriteError { AllocationError, } impl fmt::Display for WriteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { WriteError::AllocationError => write!(f, "allocation error"), } } } #[cfg(feature = "std")] impl std::error::Error for WriteError {} pub type WriteResult = Result; pub struct WriteBuf(Vec); impl WriteBuf { #[inline] pub(crate) fn new(data: Vec) -> WriteBuf { WriteBuf(data) } #[inline] pub(crate) fn len(&self) -> usize { self.0.len() } #[inline] pub(crate) fn as_slice(&self) -> &[u8] { self.0.as_slice() } #[inline] pub(crate) fn as_mut_slice(&mut self) -> &mut [u8] { self.0.as_mut_slice() } #[inline] pub fn push_byte(&mut self, b: u8) -> WriteResult { self.0 .try_reserve(1) .map_err(|_| WriteError::AllocationError)?; self.0.push(b); Ok(()) } #[inline] pub fn push_slice(&mut self, data: &[u8]) -> WriteResult { self.0 .try_reserve(data.len()) .map_err(|_| WriteError::AllocationError)?; self.0.extend_from_slice(data); Ok(()) } } fn _length_length(length: usize) -> u8 { let mut i = length; let mut num_bytes = 1; while i > 255 { num_bytes += 1; i >>= 8; } num_bytes } fn _insert_at_position(buf: &mut WriteBuf, pos: usize, data: &[u8]) -> WriteResult { for _ in 0..data.len() { buf.push_byte(0)?; } let src_range = pos..buf.len() - data.len(); buf.as_mut_slice().copy_within(src_range, pos + data.len()); buf.as_mut_slice()[pos..pos + data.len()].copy_from_slice(data); Ok(()) } /// Encapsulates an ongoing write. For almost all use-cases the correct /// entrypoint is [`write()`] or [`write_single()`]. pub struct Writer<'a> { pub(crate) buf: &'a mut WriteBuf, } impl Writer<'_> { #[inline] #[doc(hidden)] pub fn new(buf: &mut WriteBuf) -> Writer<'_> { Writer { buf } } /// Writes a single element to the output. #[inline] pub fn write_element(&mut self, val: &T) -> WriteResult { val.write(self) } /// This is an alias for `write_element::>`. pub fn write_explicit_element(&mut self, val: &T, tag: u32) -> WriteResult { let tag = crate::explicit_tag(tag); self.write_tlv(tag, |dest| Writer::new(dest).write_element(val)) } /// This is an alias for `write_element::>>`. pub fn write_optional_explicit_element( &mut self, val: &Option, tag: u32, ) -> WriteResult { if let Some(v) = val { let tag = crate::explicit_tag(tag); self.write_tlv(tag, |dest| Writer::new(dest).write_element(v)) } else { Ok(()) } } /// This is an alias for `write_element::>`. pub fn write_implicit_element( &mut self, val: &T, tag: u32, ) -> WriteResult { let tag = crate::implicit_tag(tag, T::TAG); self.write_tlv(tag, |dest| val.write_data(dest)) } /// This is an alias for `write_element::>>`. pub fn write_optional_implicit_element( &mut self, val: &Option, tag: u32, ) -> WriteResult { if let Some(v) = val { let tag = crate::implicit_tag(tag, T::TAG); self.write_tlv(tag, |dest| v.write_data(dest)) } else { Ok(()) } } /// Writes a TLV with the specified tag where the value is any bytes /// written to the `Vec` in the callback. The length portion of the /// TLV is automatically computed. #[inline] pub fn write_tlv WriteResult>( &mut self, tag: Tag, body: F, ) -> WriteResult { tag.write_bytes(self.buf)?; // Push a 0-byte placeholder for the length. Needing only a single byte // for the element is probably the most common case. self.buf.push_byte(0)?; let start_len = self.buf.len(); body(self.buf)?; self.insert_length(start_len) } #[inline] fn insert_length(&mut self, start_len: usize) -> WriteResult { let added_len = self.buf.len() - start_len; if added_len >= 128 { let n = _length_length(added_len); self.buf.as_mut_slice()[start_len - 1] = 0x80 | n; let mut length_buf = [0u8; 8]; for (pos, i) in (1..=n).rev().enumerate() { length_buf[pos] = (added_len >> ((i - 1) * 8)) as u8; } _insert_at_position(self.buf, start_len, &length_buf[..n as usize])?; } else { self.buf.as_mut_slice()[start_len - 1] = added_len as u8; } Ok(()) } } /// Constructs a writer and invokes a callback which writes ASN.1 elements into /// the writer, then returns the generated DER bytes. #[inline] pub fn write) -> WriteResult>(f: F) -> WriteResult> { let mut v = WriteBuf::new(vec![]); let mut w = Writer::new(&mut v); f(&mut w)?; Ok(v.0) } /// Writes a single top-level ASN.1 element, returning the generated DER bytes. /// Most often this will be used where `T` is a type with /// `#[derive(asn1::Asn1Write)]`. pub fn write_single(v: &T) -> WriteResult> { write(|w| w.write_element(v)) } #[cfg(test)] mod tests { #[cfg(not(feature = "std"))] use alloc::boxed::Box; #[cfg(not(feature = "std"))] use alloc::vec; use super::{_insert_at_position, write, write_single, WriteBuf, Writer}; use crate::types::Asn1Writable; use crate::{ parse_single, BMPString, BigInt, BigUint, BitString, Choice1, Choice2, Choice3, DateTime, Enumerated, Explicit, GeneralizedTime, IA5String, Implicit, ObjectIdentifier, OctetStringEncoded, OwnedBigInt, OwnedBigUint, OwnedBitString, PrintableString, Sequence, SequenceOf, SequenceOfWriter, SequenceWriter, SetOf, SetOfWriter, Tlv, UniversalString, UtcTime, Utf8String, VisibleString, WriteError, }; #[cfg(not(feature = "std"))] use alloc::vec::Vec; fn assert_writes(data: &[(T, &[u8])]) where T: Asn1Writable, { for (val, expected) in data { let result = write_single(val).unwrap(); assert_eq!(&result, expected); } } #[test] fn test_insert_at_position() { let mut v = WriteBuf::new(vec![1, 2, 3, 4]); _insert_at_position(&mut v, 2, &[5, 6]).unwrap(); assert_eq!(v.as_slice(), &[1, 2, 5, 6, 3, 4]); } #[test] fn test_write_error_eq() { let e1 = WriteError::AllocationError; let e2 = WriteError::AllocationError; assert_eq!(e1, e2); } #[test] fn test_write_element() { assert_eq!(write(|w| w.write_element(&())).unwrap(), b"\x05\x00"); } #[test] fn test_write_null() { assert_writes::<()>(&[((), b"\x05\x00")]); } #[test] fn test_write_bool() { assert_writes::(&[(false, b"\x01\x01\x00"), (true, b"\x01\x01\xff")]); } #[test] fn test_write_octet_string() { assert_writes::<&[u8]>(&[ (b"", b"\x04\x00"), (b"\x01\x02\x03", b"\x04\x03\x01\x02\x03"), (b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", b"\x04\x81\x81aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), (b"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", b"\x04\x82\x01\x02aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"), ]); assert_writes::<[u8; 0]>(&[([], b"\x04\x00")]); assert_writes::<[u8; 1]>(&[([1], b"\x04\x01\x01")]); assert_writes::<[u8; 2]>(&[([2, 3], b"\x04\x02\x02\x03")]); } #[test] fn test_write_octet_string_encoded() { assert_writes::>(&[ (OctetStringEncoded::new(true), b"\x04\x03\x01\x01\xff"), (OctetStringEncoded::new(false), b"\x04\x03\x01\x01\x00"), ]); } #[test] fn test_write_printable_string() { assert_writes::>(&[ ( PrintableString::new("Test User 1").unwrap(), b"\x13\x0bTest User 1", ), ( PrintableString::new("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").unwrap(), b"\x13\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ), ]); } #[test] fn test_write_ia5string() { assert_writes::>(&[ ( IA5String::new("Test User 1").unwrap(), b"\x16\x0bTest User 1", ), ( IA5String::new("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").unwrap(), b"\x16\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ), ]); } #[test] fn test_write_utf8string() { assert_writes::>(&[ ( Utf8String::new("Test User 1"), b"\x0c\x0bTest User 1", ), ( Utf8String::new("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"), b"\x0c\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ), ]); } #[test] fn test_write_visiblestring() { assert_writes::>(&[ ( VisibleString::new("Test User 1").unwrap(), b"\x1a\x0bTest User 1", ), ( VisibleString::new("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx").unwrap(), b"\x1a\x81\x80xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", ), ]); } #[test] fn test_write_bmpstring() { assert_writes::>(&[( BMPString::new(b"\x00a\x00b\x00c").unwrap(), b"\x1e\x06\x00a\x00b\x00c", )]); } #[test] fn test_write_universalstring() { assert_writes::>(&[( UniversalString::new(b"\x00\x00\x00a\x00\x00\x00b\x00\x00\x00c").unwrap(), b"\x1c\x0c\x00\x00\x00a\x00\x00\x00b\x00\x00\x00c", )]); } #[test] fn test_write_i64() { assert_writes::(&[ (0, b"\x02\x01\x00"), (127, b"\x02\x01\x7f"), (128, b"\x02\x02\x00\x80"), (255, b"\x02\x02\x00\xff"), (256, b"\x02\x02\x01\x00"), (-1, b"\x02\x01\xff"), (-128, b"\x02\x01\x80"), (-129, b"\x02\x02\xff\x7f"), ]); } #[test] fn test_write_u64() { assert_writes::(&[( 12356915591483590945, b"\x02\x09\x00\xab\x7c\x95\x42\xbd\xdd\x89\x21", )]); } #[test] fn test_write_i32() { assert_writes::(&[ (0, b"\x02\x01\x00"), (127, b"\x02\x01\x7f"), (128, b"\x02\x02\x00\x80"), (255, b"\x02\x02\x00\xff"), (256, b"\x02\x02\x01\x00"), (-1, b"\x02\x01\xff"), (-128, b"\x02\x01\x80"), (-129, b"\x02\x02\xff\x7f"), ]); } #[test] fn test_write_u16() { assert_writes::(&[ (0, b"\x02\x01\x00"), (1, b"\x02\x01\x01"), (256, b"\x02\x02\x01\x00"), (65535, b"\x02\x03\x00\xff\xff"), ]); } #[test] fn test_write_i16() { assert_writes::(&[ (0, b"\x02\x01\x00"), (1, b"\x02\x01\x01"), (-256, b"\x02\x02\xff\x00"), (-1, b"\x02\x01\xff"), (-32768, b"\x02\x02\x80\x00"), (32767, b"\x02\x02\x7f\xff"), ]); } #[test] fn test_write_u8() { assert_writes::(&[ (0, b"\x02\x01\x00"), (127, b"\x02\x01\x7f"), (128, b"\x02\x02\x00\x80"), ]); } #[test] fn test_write_i8() { assert_writes::(&[ (0, b"\x02\x01\x00"), (127, b"\x02\x01\x7f"), (-1, b"\x02\x01\xff"), (-128, b"\x02\x01\x80"), ]); } #[test] fn test_write_biguint() { assert_writes::>(&[ (BigUint::new(b"\x00\xff").unwrap(), b"\x02\x02\x00\xff"), ( BigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap(), b"\x02\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ]); } #[test] fn test_write_ownedbiguint() { assert_writes::(&[ ( OwnedBigUint::new(b"\x00\xff".to_vec()).unwrap(), b"\x02\x02\x00\xff", ), ( OwnedBigUint::new(b"\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec()) .unwrap(), b"\x02\x0d\x00\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ]); } #[test] fn test_write_bigint() { assert_writes::>(&[ (BigInt::new(b"\xff").unwrap(), b"\x02\x01\xff"), ( BigInt::new(b"\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff").unwrap(), b"\x02\x0c\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ]); } #[test] fn test_write_ownedbigint() { assert_writes::(&[ (OwnedBigInt::new(b"\xff".to_vec()).unwrap(), b"\x02\x01\xff"), ( OwnedBigInt::new(b"\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff".to_vec()) .unwrap(), b"\x02\x0c\xff\x7f\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff", ), ]); } #[test] fn test_write_object_identifier() { assert_writes::(&[ ( ObjectIdentifier::from_string("1.2.840.113549").unwrap(), b"\x06\x06\x2a\x86\x48\x86\xf7\x0d", ), ( ObjectIdentifier::from_string("1.2.3.4").unwrap(), b"\x06\x03\x2a\x03\x04", ), ( ObjectIdentifier::from_string("1.2.840.133549.1.1.5").unwrap(), b"\x06\x09\x2a\x86\x48\x88\x93\x2d\x01\x01\x05", ), ( ObjectIdentifier::from_string("2.100.3").unwrap(), b"\x06\x03\x81\x34\x03", ), ( ObjectIdentifier::from_string("2.4.0").unwrap(), b"\x06\x02\x54\x00", ), ]); } #[test] fn test_write_bit_string() { assert_writes::>(&[ (BitString::new(b"", 0).unwrap(), b"\x03\x01\x00"), (BitString::new(b"\x80", 7).unwrap(), b"\x03\x02\x07\x80"), ( BitString::new(b"\x81\xf0", 4).unwrap(), b"\x03\x03\x04\x81\xf0", ), ]); assert_writes::(&[ (OwnedBitString::new(vec![], 0).unwrap(), b"\x03\x01\x00"), ( OwnedBitString::new(vec![0x80], 7).unwrap(), b"\x03\x02\x07\x80", ), ( OwnedBitString::new(vec![0x81, 0xf0], 4).unwrap(), b"\x03\x03\x04\x81\xf0", ), ]); } #[test] fn test_write_utctime() { assert_writes::(&[ ( UtcTime::new(DateTime::new(1991, 5, 6, 23, 45, 40).unwrap()).unwrap(), b"\x17\x0d910506234540Z", ), ( UtcTime::new(DateTime::new(1970, 1, 1, 0, 0, 0).unwrap()).unwrap(), b"\x17\x0d700101000000Z", ), ( UtcTime::new(DateTime::new(2009, 11, 15, 22, 56, 16).unwrap()).unwrap(), b"\x17\x0d091115225616Z", ), ]); } #[test] fn test_write_generalizedtime() { assert_writes(&[ ( GeneralizedTime::new(DateTime::new(1991, 5, 6, 23, 45, 40).unwrap()).unwrap(), b"\x18\x0f19910506234540Z", ), ( GeneralizedTime::new(DateTime::new(1970, 1, 1, 0, 0, 0).unwrap()).unwrap(), b"\x18\x0f19700101000000Z", ), ( GeneralizedTime::new(DateTime::new(2009, 11, 15, 22, 56, 16).unwrap()).unwrap(), b"\x18\x0f20091115225616Z", ), ]); } #[test] fn test_write_enumerated() { assert_writes::(&[ (Enumerated::new(0), b"\x0a\x01\x00"), (Enumerated::new(12), b"\x0a\x01\x0c"), ]); } #[test] fn test_write_sequence() { assert_eq!( write(|w| { w.write_element(&SequenceWriter::new(&|w: &mut Writer<'_>| { w.write_element(&()) })) }) .unwrap(), b"\x30\x02\x05\x00" ); assert_eq!( write(|w| { w.write_element(&SequenceWriter::new(&|w: &mut Writer<'_>| { w.write_element(&true) })) }) .unwrap(), b"\x30\x03\x01\x01\xff" ); assert_writes(&[( parse_single::>(b"\x30\x06\x01\x01\xff\x02\x01\x06").unwrap(), b"\x30\x06\x01\x01\xff\x02\x01\x06", )]); } #[test] fn test_write_sequence_of() { assert_writes::>(&[ (SequenceOfWriter::new(&[]), b"\x30\x00"), ( SequenceOfWriter::new(&[1u8, 2, 3]), b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ]); assert_writes::>>(&[ (SequenceOfWriter::new(vec![]), b"\x30\x00"), ( SequenceOfWriter::new(vec![1u8, 2, 3]), b"\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ]); assert_writes::, &[SequenceWriter<'_>]>>(&[ (SequenceOfWriter::new(&[]), b"\x30\x00"), ( SequenceOfWriter::new(&[SequenceWriter::new(&|_w| Ok(()))]), b"\x30\x02\x30\x00", ), ( SequenceOfWriter::new(&[SequenceWriter::new(&|w: &mut Writer<'_>| { w.write_element(&1u64) })]), b"\x30\x05\x30\x03\x02\x01\x01", ), ]); assert_writes(&[( parse_single::>(b"\x30\x06\x02\x01\x05\x02\x01\x07").unwrap(), b"\x30\x06\x02\x01\x05\x02\x01\x07", )]); } #[test] fn test_write_set_of() { assert_writes::>(&[ (SetOfWriter::new(&[]), b"\x31\x00"), (SetOfWriter::new(&[1u8]), b"\x31\x03\x02\x01\x01"), ( SetOfWriter::new(&[1, 2, 3]), b"\x31\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ( SetOfWriter::new(&[3, 2, 1]), b"\x31\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ]); assert_writes(&[ (SetOfWriter::new(vec![]), b"\x31\x00"), (SetOfWriter::new(vec![1u8]), b"\x31\x03\x02\x01\x01"), ( SetOfWriter::new(vec![1, 2, 3]), b"\x31\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ( SetOfWriter::new(vec![3, 2, 1]), b"\x31\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03", ), ]); assert_writes(&[( parse_single::>(b"\x31\x06\x02\x01\x05\x02\x01\x07").unwrap(), b"\x31\x06\x02\x01\x05\x02\x01\x07", )]); } #[test] fn test_write_implicit() { assert_writes::>(&[ (Implicit::new(true), b"\x82\x01\xff"), (Implicit::new(false), b"\x82\x01\x00"), ]); assert_eq!( write(|w| { w.write_optional_implicit_element(&Some(true), 2) }).unwrap(), b"\x82\x01\xff" ); assert_eq!( write(|w| { w.write_optional_explicit_element::(&None, 2) }).unwrap(), b"" ); assert_eq!( write(|w| { w.write_optional_implicit_element(&Some(SequenceWriter::new(&|_w| Ok(()))), 2) }) .unwrap(), b"\xa2\x00" ); assert_eq!( write(|w| { w.write_optional_explicit_element::>(&None, 2) }) .unwrap(), b"" ); assert_eq!( write(|w| { w.write_implicit_element(&true, 2) }).unwrap(), b"\x82\x01\xff" ); assert_eq!( write(|w| { w.write_implicit_element(&SequenceWriter::new(&|_w| { Ok(()) }), 2) }) .unwrap(), b"\xa2\x00" ); } #[test] fn test_write_explicit() { assert_writes::>(&[ (Explicit::new(true), b"\xa2\x03\x01\x01\xff"), (Explicit::new(false), b"\xa2\x03\x01\x01\x00"), ]); assert_eq!( write(|w| { w.write_optional_explicit_element(&Some(true), 2) }).unwrap(), b"\xa2\x03\x01\x01\xff" ); assert_eq!( write(|w| { w.write_optional_explicit_element::(&None, 2) }).unwrap(), b"" ); assert_eq!( write(|w| { w.write_explicit_element(&true, 2) }).unwrap(), b"\xa2\x03\x01\x01\xff" ); } #[test] fn test_write_option() { assert_writes::>(&[ (Some(true), b"\x01\x01\xff"), (Some(false), b"\x01\x01\x00"), (None, b""), ]); } #[test] fn test_write_choice() { assert_writes::>(&[(Choice1::ChoiceA(true), b"\x01\x01\xff")]); assert_writes::>(&[ (Choice2::ChoiceA(true), b"\x01\x01\xff"), (Choice2::ChoiceB(18), b"\x02\x01\x12"), ]); assert_writes::>(&[ (Choice3::ChoiceA(true), b"\x01\x01\xff"), (Choice3::ChoiceB(18), b"\x02\x01\x12"), (Choice3::ChoiceC(()), b"\x05\x00"), ]); } #[test] fn test_write_tlv() { assert_writes(&[ ( parse_single::>(b"\x01\x01\x00").unwrap(), b"\x01\x01\x00", ), ( parse_single::>(b"\x1f\x81\x80\x01\x00").unwrap(), b"\x1f\x81\x80\x01\x00", ), ( parse_single::>(b"\x1f\x1f\x00").unwrap(), b"\x1f\x1f\x00", ), ]); } #[test] fn test_write_box() { assert_writes(&[ (Box::new(12u8), b"\x02\x01\x0c"), (Box::new(0), b"\x02\x01\x00"), ]); } #[test] fn test_write_error_display() { use alloc::string::ToString; assert_eq!(&WriteError::AllocationError.to_string(), "allocation error"); } } asn1-0.17.0/tests/derive_test.rs000064400000000000000000000473501046102023000146110ustar 00000000000000use std::fmt; fn assert_roundtrips< 'a, T: asn1::Asn1Readable<'a> + asn1::Asn1Writable + PartialEq + fmt::Debug, >( data: &[(asn1::ParseResult, &'a [u8])], ) { for (value, der_bytes) in data { let parsed = asn1::parse_single::(der_bytes); assert_eq!(value, &parsed); if let Ok(v) = value { let result = asn1::write_single(v).unwrap(); assert_eq!(&result, der_bytes); } } } #[test] fn test_struct_no_fields() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct NoFields; assert_roundtrips(&[ (Ok(NoFields), b"\x30\x00"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::ExtraData)), b"\x30\x01\x00", ), ]); } #[test] fn test_struct_simple_fields() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct SimpleFields { a: u64, b: u64, } assert_roundtrips(&[( Ok(SimpleFields { a: 2, b: 3 }), b"\x30\x06\x02\x01\x02\x02\x01\x03", )]); } #[test] fn test_tuple_struct_simple_fields() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct SimpleFields(u8, u8); assert_roundtrips(&[(Ok(SimpleFields(2, 3)), b"\x30\x06\x02\x01\x02\x02\x01\x03")]); } #[test] fn test_struct_lifetime() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct Lifetimes<'a> { a: &'a [u8], } assert_roundtrips(&[(Ok(Lifetimes { a: b"abc" }), b"\x30\x05\x04\x03abc")]); } #[test] fn test_optional() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct OptionalFields { zzz: Option, } assert_roundtrips(&[ (Ok(OptionalFields { zzz: None }), b"\x30\x00"), (Ok(OptionalFields { zzz: Some(8) }), b"\x30\x03\x02\x01\x08"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::ExtraData)), b"\x30\x03\x04\x00\x00", ), ]); } #[test] fn test_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct EmptySequence; #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct ExplicitFields { #[explicit(5)] a: Option, #[explicit(7)] b: Option, } assert_roundtrips(&[ ( Ok(ExplicitFields { a: Some(3), b: Some(EmptySequence), }), b"\x30\x09\xa5\x03\x02\x01\x03\xa7\x02\x30\x00", ), ( Ok(ExplicitFields { a: None, b: Some(EmptySequence), }), b"\x30\x04\xa7\x02\x30\x00", ), ( Ok(ExplicitFields { a: Some(3), b: None, }), b"\x30\x05\xa5\x03\x02\x01\x03", ), (Ok(ExplicitFields { a: None, b: None }), b"\x30\x00"), ]); } #[test] fn test_implicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct EmptySequence; #[derive(asn1::Asn1Read, asn1::Asn1Write, Debug, PartialEq, Eq)] struct ImplicitFields { #[implicit(5)] a: Option, #[implicit(7)] b: Option, } assert_roundtrips(&[ ( Ok(ImplicitFields { a: Some(3), b: Some(EmptySequence), }), b"\x30\x05\x85\x01\x03\xa7\x00", ), ( Ok(ImplicitFields { a: None, b: Some(EmptySequence), }), b"\x30\x02\xa7\x00", ), ( Ok(ImplicitFields { a: Some(3), b: None, }), b"\x30\x03\x85\x01\x03", ), (Ok(ImplicitFields { a: None, b: None }), b"\x30\x00"), ]); } #[test] fn test_default() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct DefaultFields { #[default(13)] a: u8, #[default(15)] #[explicit(1)] b: u8, #[default(17)] #[implicit(5)] c: u8, } assert_roundtrips(&[ ( Ok(DefaultFields { a: 13, b: 15, c: 17, }), b"\x30\x00", ), ( Ok(DefaultFields { a: 3, b: 15, c: 17 }), b"\x30\x03\x02\x01\x03", ), ( Ok(DefaultFields { a: 13, b: 5, c: 17 }), b"\x30\x05\xa1\x03\x02\x01\x05", ), ( Ok(DefaultFields { a: 13, b: 15, c: 7 }), b"\x30\x03\x85\x01\x07", ), ( Ok(DefaultFields { a: 3, b: 5, c: 7 }), b"\x30\x0b\x02\x01\x03\xa1\x03\x02\x01\x05\x85\x01\x07", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::a"))), b"\x30\x03\x02\x01\x0d", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::b"))), b"\x30\x05\xa1\x03\x02\x01\x0f", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::c"))), b"\x30\x03\x85\x01\x11", ), ]); } #[test] fn test_default_not_literal() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3, 4); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct DefaultFields { #[default(OID1)] a: asn1::ObjectIdentifier, } assert_roundtrips(&[ (Ok(DefaultFields { a: OID1 }), b"\x30\x00"), ( Ok(DefaultFields { a: OID2 }), b"\x30\x05\x06\x03\x2a\x03\x04", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::a"))), b"\x30\x04\x06\x02\x2a\x03", ), ]); } #[test] fn test_default_const_generics() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug)] struct DefaultFields { #[default(15)] a: asn1::Explicit, #[default(17)] b: asn1::Implicit, } assert_roundtrips(&[ ( Ok(DefaultFields { a: asn1::Explicit::new(15), b: asn1::Implicit::new(17), }), b"\x30\x00", ), ( Ok(DefaultFields { a: asn1::Explicit::new(5), b: asn1::Implicit::new(17), }), b"\x30\x05\xa1\x03\x02\x01\x05", ), ( Ok(DefaultFields { a: asn1::Explicit::new(15), b: asn1::Implicit::new(7), }), b"\x30\x03\x85\x01\x07", ), ( Ok(DefaultFields { a: asn1::Explicit::new(5), b: asn1::Implicit::new(7), }), b"\x30\x08\xa1\x03\x02\x01\x05\x85\x01\x07", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::a"))), b"\x30\x05\xa1\x03\x02\x01\x0f", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultFields::b"))), b"\x30\x03\x85\x01\x11", ), ]); } #[test] fn test_default_bool() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct DefaultField { #[default(false)] a: bool, } assert_roundtrips(&[ (Ok(DefaultField { a: true }), b"\x30\x03\x01\x01\xff"), (Ok(DefaultField { a: false }), b"\x30\x00"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::EncodedDefault) .add_location(asn1::ParseLocation::Field("DefaultField::a"))), b"\x30\x03\x01\x01\x00", ), ]); } #[test] fn test_enum() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum BasicChoice { A(u64), B(()), } assert_roundtrips(&[ (Ok(BasicChoice::A(17)), b"\x02\x01\x11"), (Ok(BasicChoice::B(())), b"\x05\x00"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(4), })), b"\x04\x00", ), ]); assert_roundtrips(&[ (Ok(Some(BasicChoice::A(17))), b"\x02\x01\x11"), (Ok(Some(BasicChoice::B(()))), b"\x05\x00"), (Ok(None), b""), ]); } #[test] fn test_enum_lifetimes() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum LifetimesChoice<'a> { A(u64), B(&'a [u8]), } assert_roundtrips(&[ (Ok(LifetimesChoice::A(17)), b"\x02\x01\x11"), (Ok(LifetimesChoice::B(b"lol")), b"\x04\x03lol"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(5), })), b"\x05\x00", ), ]); assert_roundtrips(&[ (Ok(Some(LifetimesChoice::A(17))), b"\x02\x01\x11"), (Ok(Some(LifetimesChoice::B(b"lol"))), b"\x04\x03lol"), (Ok(None), b""), ]); } #[test] fn test_enum_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum ExplicitChoice<'a> { #[explicit(5)] A(u64), B(&'a [u8]), } assert_roundtrips(&[ (Ok(ExplicitChoice::A(17)), b"\xa5\x03\x02\x01\x11"), (Ok(ExplicitChoice::B(b"lol")), b"\x04\x03lol"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(5), })), b"\x05\x00", ), ]); assert_roundtrips(&[ (Ok(Some(ExplicitChoice::A(17))), b"\xa5\x03\x02\x01\x11"), (Ok(Some(ExplicitChoice::B(b"lol"))), b"\x04\x03lol"), (Ok(None), b""), ]); } #[test] fn test_enum_implicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct EmptySequence; #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum ImplicitChoice<'a> { #[implicit(5)] A(u64), #[implicit(7)] B(EmptySequence), C(&'a [u8]), } assert_roundtrips(&[ (Ok(ImplicitChoice::A(17)), b"\x85\x01\x11"), (Ok(ImplicitChoice::B(EmptySequence)), b"\xa7\x00"), (Ok(ImplicitChoice::C(b"lol")), b"\x04\x03lol"), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(5), })), b"\x05\x00", ), ]); assert_roundtrips(&[ (Ok(Some(ImplicitChoice::A(17))), b"\x85\x01\x11"), (Ok(Some(ImplicitChoice::B(EmptySequence))), b"\xa7\x00"), (Ok(Some(ImplicitChoice::C(b"lol"))), b"\x04\x03lol"), (Ok(None), b""), ]); } #[test] fn test_error_parse_location() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct InnerSeq(u64); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] enum InnerEnum { Int(u64), } #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct OuterSeq { inner: InnerSeq, inner_enum: Option, } assert_roundtrips::(&[ ( Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue) .add_location(asn1::ParseLocation::Field("InnerSeq::0")) .add_location(asn1::ParseLocation::Field("OuterSeq::inner"))), b"\x30\x04\x30\x02\x02\x00", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::InvalidValue) .add_location(asn1::ParseLocation::Field("InnerEnum::Int")) .add_location(asn1::ParseLocation::Field("OuterSeq::inner_enum"))), b"\x30\x07\x30\x03\x02\x01\x01\x02\x00", ), ]); } #[test] fn test_required_implicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct RequiredImplicit { #[implicit(0, required)] value: u8, } assert_roundtrips::(&[ (Ok(RequiredImplicit { value: 8 }), b"\x30\x03\x80\x01\x08"), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::ShortData { needed: 1 }) .add_location(asn1::ParseLocation::Field("RequiredImplicit::value")), ), b"\x30\x00", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(11), }) .add_location(asn1::ParseLocation::Field("RequiredImplicit::value"))), b"\x30\x03\x0b\x01\x00", ), ]); } #[test] fn test_required_explicit() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct RequiredExplicit { #[explicit(0, required)] value: u8, } assert_roundtrips::(&[ ( Ok(RequiredExplicit { value: 8 }), b"\x30\x05\xa0\x03\x02\x01\x08", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::ShortData { needed: 1 }) .add_location(asn1::ParseLocation::Field("RequiredExplicit::value")), ), b"\x30\x00", ), ( Err(asn1::ParseError::new(asn1::ParseErrorKind::UnexpectedTag { actual: asn1::Tag::primitive(11), }) .add_location(asn1::ParseLocation::Field("RequiredExplicit::value"))), b"\x30\x03\x0b\x01\x00", ), ]); } #[test] fn test_defined_by() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 5); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] OctetString(&'a [u8]), #[defined_by(OID2)] Integer(u32), } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::OctetString(b"abc"), }), b"\x30\x09\x06\x02\x2a\x03\x04\x03abc", ), ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Integer(17), }), b"\x30\x07\x06\x02\x2a\x05\x02\x01\x11", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::UnknownDefinedBy) .add_location(asn1::ParseLocation::Field("S::value")), ), b"\x30\x04\x06\x02\x2a\x07", ), ]); } #[test] fn test_defined_by_default() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 5); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] Integer(u32), #[default] Other(asn1::ObjectIdentifier, asn1::Tlv<'a>), } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Integer(7), }), b"\x30\x07\x06\x02\x2a\x03\x02\x01\x07", ), ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Other(OID2, asn1::parse_single(b"\x05\x00").unwrap()), }), b"\x30\x06\x06\x02\x2a\x05\x05\x00", ), ]) } #[test] fn test_defined_by_optional() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 5); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] OctetString(&'a [u8]), #[defined_by(OID2)] Other, } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::OctetString(b"abc"), }), b"\x30\x09\x06\x02\x2a\x03\x04\x03abc", ), ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::Other, }), b"\x30\x04\x06\x02\x2a\x05", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::UnknownDefinedBy) .add_location(asn1::ParseLocation::Field("S::value")), ), b"\x30\x04\x06\x02\x2a\x07", ), ]); } #[test] fn test_defined_by_mod() { mod oids { pub const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); } #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: Value<'a>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(oids::OID1)] OctetString(&'a [u8]), } assert_roundtrips::(&[ ( Ok(S { oid: asn1::DefinedByMarker::marker(), value: Value::OctetString(b"abc"), }), b"\x30\x09\x06\x02\x2a\x03\x04\x03abc", ), ( Err( asn1::ParseError::new(asn1::ParseErrorKind::UnknownDefinedBy) .add_location(asn1::ParseLocation::Field("S::value")), ), b"\x30\x04\x06\x02\x2a\x07", ), ]); } #[test] fn test_defined_by_explicit() { pub const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3); #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S<'a> { oid: asn1::DefinedByMarker, #[defined_by(oid)] value: asn1::Explicit, 1>, } #[derive(asn1::Asn1DefinedByRead, asn1::Asn1DefinedByWrite, PartialEq, Debug, Eq)] enum Value<'a> { #[defined_by(OID1)] OctetString(&'a [u8]), } assert_roundtrips::(&[( Ok(S { oid: asn1::DefinedByMarker::marker(), value: asn1::Explicit::new(Value::OctetString(b"abc")), }), b"\x30\x0b\x06\x02\x2a\x03\xa1\x05\x04\x03abc", )]); } #[test] fn test_generics() { #[derive(asn1::Asn1Read, asn1::Asn1Write, PartialEq, Debug, Eq)] struct S { value: T, } assert_roundtrips::>(&[(Ok(S { value: 12 }), b"\x30\x03\x02\x01\x0c")]); assert_roundtrips::>(&[(Ok(S { value: true }), b"\x30\x03\x01\x01\xff")]); assert_eq!( asn1::write_single(&S { value: asn1::SequenceOfWriter::new([true, true]), }) .unwrap(), b"\x30\x08\x30\x06\x01\x01\xff\x01\x01\xff" ) } asn1-0.17.0/tests/oid_tests.rs000064400000000000000000000007151046102023000142630ustar 00000000000000#[test] fn test_oid_value() { assert_eq!( asn1::oid!(1, 2, 3, 4), asn1::ObjectIdentifier::from_string("1.2.3.4").unwrap() ); } #[test] fn test_match_statement() { const OID1: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3, 4); const OID2: asn1::ObjectIdentifier = asn1::oid!(1, 2, 3, 5); let oid = asn1::ObjectIdentifier::from_string("1.2.3.4").unwrap(); assert!(matches!(oid, OID1)); assert!(!matches!(oid, OID2)); } asn1-0.17.0/tests/roundtrip_tests.rs000064400000000000000000000011231046102023000155300ustar 00000000000000fn assert_roundtrips(i: T) where for<'a> T: asn1::Asn1Writable + asn1::Asn1Readable<'a> + std::fmt::Debug + PartialEq, { let result = asn1::write_single::(&i).unwrap(); let parsed = asn1::parse_single::(&result).unwrap(); assert_eq!(parsed, i); } #[test] fn test_u8() { for i in u8::MIN..=u8::MAX { assert_roundtrips::(i); } } #[test] fn test_i8() { for i in i8::MIN..=i8::MAX { assert_roundtrips::(i); } } #[test] fn test_u64() { for v in [0, 12356915591483590945, u64::MAX] { assert_roundtrips::(v); } }