synta 0.2.2

ASN.1 parser, decoder, and encoder library with DER/BER support and C FFI
Documentation
//! Synta: ASN.1 parser, decoder, and encoder library
//!
//! This library provides ASN.1 (Abstract Syntax Notation One) parsing, decoding,
//! and encoding capabilities with support for DER (Distinguished Encoding Rules)
//! and BER (Basic Encoding Rules).
//!
//! # Features
//! - Safe and ergonomic API leveraging Rust's type system
//! - Zero-copy parsing where possible
//! - DER, BER, and CER encoding support
//! - Derive macros for automatic trait implementations (with `derive` feature)
//! - C FFI compatibility (with `ffi` feature)
//! - `no_std` support (with `alloc`)
//! - Optional serde `Serialize`/`Deserialize` support (with `serde` feature)
//!
//! # Cargo features
//!
//! | Feature   | Description |
//! |-----------|-------------|
//! | `std`     | Enable standard library support (enabled by default). Without this, `alloc` is used. |
//! | `derive`  | Enable derive macros (`Asn1Sequence`, `Asn1Set`, `Asn1Choice`). |
//! | `serde`   | Enable `serde::Serialize`/`Deserialize` for all ASN.1 types. |
//! | `unchecked` | **Unsafe optimization**: skip overflow and bounds checks in tag and length decoding. Only enable for fully trusted, pre-validated input. Malformed data may cause incorrect behaviour. |
//!
//! # ASN.1 types
//!
//! All standard ASN.1 types are re-exported at the crate root for convenient
//! import.  Two families of string and binary types are available:
//!
//! ## Owned types (heap-allocating)
//!
//! Suitable for constructing ASN.1 structures programmatically or when the
//! decoded value must outlive the input buffer.
//!
//! | Rust type         | ASN.1 type        |
//! |-------------------|-------------------|
//! | [`OctetString`]   | `OCTET STRING`    |
//! | [`BitString`]     | `BIT STRING`      |
//! | [`Utf8String`]    | `UTF8String`      |
//! | [`PrintableString`] | `PrintableString` |
//! | [`IA5String`]     | `IA5String`       |
//! | [`Integer`]       | `INTEGER`         |
//! | [`Boolean`]       | `BOOLEAN`         |
//! | [`ObjectIdentifier`] | `OBJECT IDENTIFIER` |
//!
//! ## Zero-copy borrowed types
//!
//! These types borrow directly from the decoder's input buffer, avoiding
//! allocation.  Use them for parse-only workloads where the values do not
//! need to outlive the encoded bytes.
//!
//! | Rust type            | ASN.1 type        |
//! |----------------------|-------------------|
//! | [`OctetStringRef`]   | `OCTET STRING`    |
//! | [`BitStringRef`]     | `BIT STRING`      |
//! | [`Utf8StringRef`]    | `UTF8String`      |
//! | [`PrintableStringRef`] | `PrintableString` |
//! | [`IA5StringRef`]     | `IA5String`       |
//!
//! # Example
//! ```
//! use synta::{Decoder, Encoding, Integer};
//!
//! // Decode an integer
//! let data = &[0x02, 0x01, 0x2A]; // INTEGER 42
//! let mut decoder = Decoder::new(data, Encoding::Der);
//! let value: Integer = decoder.decode().unwrap();
//! assert_eq!(value.as_i64().unwrap(), 42);
//! ```
//!
//! # Convenience traits: `ToDer` and `FromDer`
//!
//! [`ToDer`] and [`FromDer`] are blanket-implemented on all types that
//! implement [`Encode`] / [`Decode`].  They let you encode or decode a
//! single value without constructing an [`Encoder`] or [`Decoder`] manually:
//!
//! ```
//! use synta::{Integer, ToDer, FromDer};
//!
//! let n = Integer::from_i64(42);
//! let der = n.to_der().unwrap();
//! let n2 = Integer::from_der(&der).unwrap();
//! assert_eq!(n, n2);
//! ```
//!
//! Zero-copy borrowed types (e.g. `OctetStringRef<'a>`) cannot be decoded
//! via [`FromDer`] because they carry a lifetime tied to the input buffer;
//! use [`Decoder`] directly for those cases.
//!
//! # Serde support
//!
//! Enable the `serde` feature to serialize and deserialize all ASN.1 types
//! with any serde-compatible format (JSON, CBOR, etc.):
//!
//! ```toml
//! [dependencies]
//! synta = { version = "0.1", features = ["serde"] }
//! ```
//!
//! ```ignore
//! use synta::{Integer, ObjectIdentifier};
//!
//! let i = Integer::from_i64(255);
//! let json = serde_json::to_string(&i).unwrap(); // "\"ff\""
//!
//! let oid = ObjectIdentifier::new(&[1, 2, 840, 113549]).unwrap();
//! let json = serde_json::to_string(&oid).unwrap(); // "\"1.2.840.113549\""
//! ```

#![cfg_attr(not(feature = "std"), no_std)]

#[cfg(not(feature = "std"))]
extern crate alloc;

// Internal serde helpers (only compiled with the `serde` feature)
#[cfg(feature = "serde")]
pub(crate) mod serde_impl;

// Module declarations
pub mod ber;
pub mod config;
pub mod der;
pub mod error;
pub mod format;
pub mod hex;
pub mod length;
pub mod reader;
pub mod tag;
pub mod traits;
pub mod types;
pub mod writer;

// Re-export commonly used types
pub use config::DecoderConfig;
pub use error::{Error, Result};
pub use format::{format_asn1_bytes, Asn1FormatMode};
pub use hex::{bytes_to_hex, hex_to_bytes};
pub use length::Length;
pub use tag::{Tag, TagClass};

/// ASN.1 encoding rules
///
/// Specifies which encoding rules to use when encoding or decoding ASN.1 data.
/// Different encoding rules have different levels of strictness and features.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Encoding {
    /// Distinguished Encoding Rules (deterministic subset of BER)
    ///
    /// DER is a strict subset of BER that ensures deterministic encoding.
    /// It is commonly used in cryptographic applications where canonical
    /// encoding is required (e.g., X.509 certificates).
    Der,
    /// Basic Encoding Rules (most flexible)
    ///
    /// BER is the most flexible encoding, allowing multiple ways to encode
    /// the same data. It supports indefinite length encoding and other
    /// features not available in DER.
    Ber,
    /// Canonical Encoding Rules (similar to DER but for streaming)
    ///
    /// CER is designed for streaming applications where the total length of a
    /// value may not be known in advance.  The encoding rules are:
    ///
    /// - Constructed types whose content exceeds **1 000 bytes** must use
    ///   indefinite-length encoding (terminated by end-of-contents octets
    ///   `0x00 0x00`).  Shorter constructed types use definite length.
    /// - Primitive types always use definite-length encoding.
    /// - All other DER constraints (e.g. minimal integer encoding, TRUE=0xFF)
    ///   remain in force.
    ///
    /// CER is rarely used in practice; DER is preferred for certificates and
    /// most cryptographic protocols.
    Cer,
}

// Re-export core decoder/encoder from DER module (they work for all encodings)
pub use der::decoder::Decoder;
pub use der::encoder::Encoder;

// Re-export primitive types
pub use types::oid::ObjectIdentifier;
pub use types::primitive::{Boolean, Enumerated, Integer, Null, Real};
// Owned heap-allocating string and binary types
pub use types::string::{
    BitString, BmpString, GeneralString, IA5String, NumericString, OctetString, PrintableString,
    TeletexString, UniversalString, Utf8String, VisibleString,
};
// Zero-copy borrowed string and binary types (borrow from the decoder input buffer)
pub use types::string::{
    BitStringRef, IA5StringRef, OctetStringRef, PrintableStringRef, Utf8StringRef,
};
pub use types::time::{expand_yy, parse2, parse4, GeneralizedTime, UtcTime};

// Re-export constructed types
pub use types::constructed::{Element, Sequence, SequenceOf, Set, SetOf};
// Explicit Vec-backed aliases (same as SequenceOf/SetOf but clearer names)
pub use types::constructed::{SequenceOf as SequenceOfVec, SetOf as SetOfVec};

// Re-export tagged types
pub use types::tagged::{ExplicitTag, ImplicitTag};

// Re-export zero-copy raw TLV capture
pub use types::raw::RawDer;

// Re-export derive macros (when derive feature is enabled)
#[cfg(feature = "derive")]
pub use synta_derive::{Asn1Choice, Asn1Sequence, Asn1Set};

// Re-export traits
pub use traits::convenience::{FromDer, ToDer};
pub use traits::decode::Decode;
pub use traits::decode_implicit::DecodeImplicit;
pub use traits::encode::Encode;
pub use traits::tag_for_optional::TagForOptional;
pub use traits::tagged::Tagged;

// ---- serde support for the Encoding enum ----

#[cfg(feature = "serde")]
impl serde::Serialize for Encoding {
    fn serialize<S: serde::Serializer>(&self, s: S) -> core::result::Result<S::Ok, S::Error> {
        s.serialize_str(match self {
            Encoding::Der => "der",
            Encoding::Ber => "ber",
            Encoding::Cer => "cer",
        })
    }
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Encoding {
    fn deserialize<D: serde::Deserializer<'de>>(d: D) -> core::result::Result<Self, D::Error> {
        struct V;
        impl<'de> serde::de::Visitor<'de> for V {
            type Value = Encoding;
            fn expecting(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
                write!(f, "\"der\", \"ber\", or \"cer\"")
            }
            fn visit_str<E: serde::de::Error>(self, v: &str) -> core::result::Result<Encoding, E> {
                match v {
                    "der" | "DER" => Ok(Encoding::Der),
                    "ber" | "BER" => Ok(Encoding::Ber),
                    "cer" | "CER" => Ok(Encoding::Cer),
                    _ => Err(E::unknown_variant(v, &["der", "ber", "cer"])),
                }
            }
        }
        d.deserialize_str(V)
    }
}