| Crates.io | x690 |
| lib.rs | x690 |
| version | 1.2.0 |
| created_at | 2025-07-20 17:40:39.15085+00 |
| updated_at | 2025-08-06 11:08:14.843032+00 |
| description | Basic Encoding Rules (BER) and Distinguished Encoding Rules (DER) as specified in ITU-T Recommendation X.690 |
| homepage | |
| repository | https://github.com/JonathanWilbur/asn1.rs/tree/master/x690 |
| max_upload_size | |
| id | 1761274 |
| size | 521,482 |
This library implements the encoding rules defined in ITU Recommendation X.690 (2021), namely:
Much of the functionality of this crate depends upon
wildboar-asn1, which is a "core"
crate for all aspects of ASN.1 that are mostly "codec-agnostic" so that it can
be used by other crates for implementing the Packed Encoding Rules,
Octet Encoding Rules, BACNet, etc.
The ITU-T Recommendation X.690 codecs use Tag-Length-Value (TLV) triplets to
encode ASN.1 values. These triplets are encoded on an integral number of bytes
(what the specifications call "octets" for disambiguation). In this library,
this TLV triplet is referred to as an X690Element, and generally, throughout
the library, the word "element" refers to one of these triplets.
The X690Element itself may be composed of constituent X690Elements, allowing
for the representation of "structured types."
For your convenience, the X690Element is:
pub struct X690Element {
/// The tag identifying the type and class of this element
pub tag: Tag,
/// The value content of this element
pub value: X690Value,
}
The length is determined at encoding time by the value. It is not stored
anywhere, but you can read it using X690Value::len().
The X690Value, which represents the value octets ("content octets") of the
TLV triplet is defined like:
pub enum X690Value {
/// A primitive value stored as raw bytes
Primitive(Bytes),
/// A constructed value containing child elements
Constructed(Arc<Vec<X690Element>>),
/// A value that has been serialized to bytes (for lazy decoding or faster encoding)
Serialized(Bytes),
}
If the Serialized alternative is used, the tag, length, and value can be
written in one single call to write() when used with x690_write_tlv(). This
is a performance hack that exists only for when you are serializing trustworthy
data.
You can serialize X690Elements to bytes using x690_write_tlv(). To read
X690Elements from bytes, you must use one of the codec-specific methods:
X690Codec::decode_from_slice() or or X690Codec::decode_from_bytes().
The Basic Encoding Rules (BER) and Distinguished Encoding Rules (DER) are
implemented as structs that implement the X690Codec trait. These structs
currently do not have any fields, but they may, in the future, to support
Encoding Control Notation (ECN), if that is ever supported.
This crate exports BER and DER, which are compile-time constructed instances
of the BasicEncodingRules and DistinguishedEncodingRules respectively. You
can import them and invoke them like so:
let b = true;
let el: X690Element = BER.encode_boolean(&b)?;
let decoded = BER.decode_boolean(&el)?;
assert_eq!(b, decoded);
This library also provides some functionality independently of any codec,
simply for ease-of-use. Most of these start with x690_read_* or
x690_write_* for decoding and encoding, respectively. These functions try to
encode in such a way that is compliant with DER, but no such guarantees are
made.
For structured types:
| Use... | ...to parse |
|===========================|=====================|
| _parse_set() | SET |
| _parse_sequence() | SEQUENCE |
| _decode_sequence_of() | SEQUENCE OF |
| _decode_set_of() | SET OF |
| _encode_explicit() | EXPLICIT tagged |
| _encode_implicit() | IMPLICIT tagged |
der - Enable the Distinguished Encoding Rulessimdutf8 - Use SIMD instructions to accelerate UTF-8 operationslikely_stable - Use branch prediction hint macros so your CPU speculatively
executes the branches more likely to be taken.The feature flags have not been benchmarked thus far. It is unknown if they actually improve performance.
The above functions start with underscores just because they
AlgorithmIdentifier (a SEQUENCE)This data structure is used in X.509 certificates and in many other cryptographic artifacts.
use wildboar_asn1::*;
use x690::*;
struct AlgorithmIdentifier {
pub algorithm: OBJECT_IDENTIFIER,
pub parameters: Option<ASN1Value>,
}
const _rctl1_components_for_AlgorithmIdentifier: &[ComponentSpec; 2] = &[
ComponentSpec::new(
"algorithm",
false,
TagSelector::tag((
TagClass::UNIVERSAL,
UNIV_TAG_OBJECT_IDENTIFIER,
)),
None,
None,
),
ComponentSpec::new("parameters", true, TagSelector::any, None, None),
];
const _eal_components_for_AlgorithmIdentifier: &[ComponentSpec; 0] = &[];
const _rctl2_components_for_AlgorithmIdentifier: &[ComponentSpec; 0] = &[];
fn decode_AlgorithmIdentifier(el: &X690Element) -> ASN1Result<AlgorithmIdentifier> {
let elements = el.components()?;
let seq_iter = X690StructureIterator::new(
&elements,
_rctl1_components_for_AlgorithmIdentifier,
_eal_components_for_AlgorithmIdentifier,
_rctl2_components_for_AlgorithmIdentifier,
);
let mut maybe_algorithm: Option<OBJECT_IDENTIFIER> = None;
let mut parameters: Option<ASN1Value> = None;
for (i, component) in seq_iter.enumerate() {
match component.unwrap() {
"algorithm" => {
maybe_algorithm = Some(DER.decode_object_identifier(&elements[i])?);
},
"parameters" => {
parameters = Some(DER.decode_any(&elements[i])?);
}
_ => {
return Err(ASN1Error::new(ASN1ErrorCode::unrecognized_components_in_inextensible_type));
}
}
}
let algorithm: OBJECT_IDENTIFIER = maybe_algorithm.unwrap();
let parameters: Option<ASN1Value> = parameters;
Ok(AlgorithmIdentifier {
algorithm,
parameters,
})
}
pub fn encode_AlgorithmIdentifier(value_: &AlgorithmIdentifier) -> ASN1Result<X690Element> {
let mut components_: Vec<X690Element> = Vec::with_capacity(12);
components_.push(DER.encode_object_identifier(&value_.algorithm)?);
if let Some(v_) = &value_.parameters {
components_.push(x690_identity(&v_)?);
}
Ok(X690Element::new(
Tag::new(TagClass::UNIVERSAL, UNIV_TAG_SEQUENCE),
X690Value::Constructed(Arc::new(
[components_, value_._unrecognized.clone()].concat(),
)),
))
}
pub fn validate_AlgorithmIdentifier(el: &X690Element) -> ASN1Result<()> {
let _elements = match &el.value {
X690Value::Constructed(children) => children,
_ => {
return Err(
el.to_asn1_err_named(ASN1ErrorCode::invalid_construction, "AlgorithmIdentifier")
)
}
};
let _seq_iter = X690StructureIterator::new(
_elements.as_slice(),
_rctl1_components_for_AlgorithmIdentifier,
_eal_components_for_AlgorithmIdentifier,
_rctl2_components_for_AlgorithmIdentifier,
);
let mut _i: usize = 0;
for _fallible_component_name in _seq_iter {
let _component_name = _fallible_component_name?;
let _maybe_el = _elements.get(_i);
_i += 1;
let _el = _maybe_el.unwrap();
match _component_name {
"algorithm" => DER.validate_object_identifier(_el)?,
"parameters" => DER.validate_any(_el)?,
_ => (),
}
}
Ok(())
}
let alg = AlgorithmIdentifier{
algorithm: OBJECT_IDENTIFIER::from_str("2.5.4.3").unwrap(),
parameters: None,
};
let encoded = encode_AlgorithmIdentifier(&alg)?;
assert!(validate_AlgorithmIdentifier(&encoded).is_ok()); // For validating without fully decoding.
let decoded = decode_AlgorithmIdentifier(&encoded)?;
assert_eq(alg.algorithm, decoded.algorithm);
UnboundedDirectoryString (a CHOICE)This type is used in X.500 directories.
use wildboar_asn1::*;
use x690::*;
pub enum UnboundedDirectoryString {
teletexString(TeletexString),
printableString(PrintableString),
bmpString(BMPString),
universalString(UniversalString),
uTF8String(UTF8String),
}
pub fn _decode_UnboundedDirectoryString(el: &X690Element) -> ASN1Result<UnboundedDirectoryString> {
match (el.tag.tag_class, el.tag.tag_number) {
(TagClass::UNIVERSAL, 20) => Ok(UnboundedDirectoryString::teletexString(
BER.decode_t61_string(&el)?,
)),
(TagClass::UNIVERSAL, 19) => Ok(UnboundedDirectoryString::printableString(
BER.decode_printable_string(&el)?,
)),
(TagClass::UNIVERSAL, 30) => Ok(UnboundedDirectoryString::bmpString(
BER.decode_bmp_string(&el)?,
)),
(TagClass::UNIVERSAL, 28) => Ok(UnboundedDirectoryString::universalString(
BER.decode_universal_string(&el)?,
)),
(TagClass::UNIVERSAL, 12) => Ok(UnboundedDirectoryString::uTF8String(
BER.decode_utf8_string(&el)?,
)),
_ => {
return Err(el.to_asn1_err_named(
ASN1ErrorCode::unrecognized_alternative_in_inextensible_choice,
"UnboundedDirectoryString",
))
}
}
}
pub fn _encode_UnboundedDirectoryString(
value_: &UnboundedDirectoryString,
) -> ASN1Result<X690Element> {
match value_ {
UnboundedDirectoryString::teletexString(v) => BER.encode_t61_string(&v),
UnboundedDirectoryString::printableString(v) => BER.encode_printable_string(&v),
UnboundedDirectoryString::bmpString(v) => BER.encode_bmp_string(&v),
UnboundedDirectoryString::universalString(v) => BER.encode_universal_string(&v),
UnboundedDirectoryString::uTF8String(v) => BER.encode_utf8_string(&v),
}
}
pub fn _validate_UnboundedDirectoryString(el: &X690Element) -> ASN1Result<()> {
match (el.tag.tag_class, el.tag.tag_number) {
(TagClass::UNIVERSAL, 20) => BER.validate_t61_string(&el),
(TagClass::UNIVERSAL, 19) => BER.validate_printable_string(&el),
(TagClass::UNIVERSAL, 30) => BER.validate_bmp_string(&el),
(TagClass::UNIVERSAL, 28) => BER.validate_universal_string(&el),
(TagClass::UNIVERSAL, 12) => BER.validate_utf8_string(&el),
_ => {
return Err(el.to_asn1_err_named(
ASN1ErrorCode::unrecognized_alternative_in_inextensible_choice,
"UnboundedDirectoryString",
))
}
}
}
let ds = UnboundedDirectoryString::UTF8String("hi mom".to_owned());
let encoded = _encode_UnboundedDirectoryString(&ds)?;
assert!(_validate_UnboundedDirectoryString(&encoded).is_ok()); // For validating without fully decoding.
let decoded = _decode_UnboundedDirectoryString(&encoded)?;
match (ds, decoded) {
(UnboundedDirectoryString::UTF8String(a), UnboundedDirectoryString::UTF8String(b)) => assert_eq!(a, b),
(_, _) => panic!(),
};
OSString directly to BMPString on Windows?BMPString and UniversalString methodsMany of the functions in this library start with underscores because they are
expected to be used in generated code. Valid ASN.1 identifiers are not allowed
to start with hyphens, so code generation that converts hyphens to underscores
are guaranteed to produce no symbols that conflict with those that start with
underscores in this library. This library is already in use by the
ASN.1 Compilation Service
offered by Wildboar Software, which was used to
compile the X.500 directory-related ASN.1 modules to what you see in x500.
I decided that there were not enough uses cases for SmallVec to justify it,
even as a feature flag. Most things stored in a dynamically-allocated array
in this library are too large to fit in a SmallVec.
All but a small fraction of the code in this library was produced by a human; ChatGPT generated a very small part of the remainder.
The documentation, on the other hand, was generated largely by AI, but it was reviewed by the author of the code for correctness. Cursor's AI was used for this.