oxirush_aper/lib.rs
1/*
2 OxiRush
3 Copyright 2025-2026 Valentin D'Emmanuele
4
5 Licensed under the Apache License, Version 2.0 (the "License");
6 you may not use this file except in compliance with the License.
7 You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11 Unless required by applicable law or agreed to in writing, software
12 distributed under the License is distributed on an "AS IS" BASIS,
13 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 See the License for the specific language governing permissions and
15 limitations under the License.
16*/
17
18//! Aligned PER (ITU-T X.691, the ALIGNED variant of BASIC-PER) for generated bindings
19//! of 3GPP-style ASN.1: a reader, a writer, and the types of the values.
20//!
21//! ```rust
22//! use oxirush_aper::{Reader, Writer};
23//!
24//! fn main() -> Result<(), Box<dyn std::error::Error>> {
25//! let mut octets = Vec::new();
26//! let mut writer = Writer::new(&mut octets);
27//! // INTEGER (0..7): three bits
28//! writer.unsigned(5, 0, 7)?;
29//! // INTEGER (0..255, ...): the extension bit, then one octet, aligned
30//! writer.unsigned_extensible(200, 0, 255)?;
31//! // ENUMERATED with 6 root values and an extension marker: the fourth
32//! writer.index(3, 6, true);
33//! // SIZE (1..256) of a SEQUENCE OF: its count, then its items
34//! writer.size(2, 1, 256)?;
35//! writer.unsigned(1, 0, 1)?;
36//! writer.unsigned(0, 0, 1)?;
37//! println!("{octets:02x?}");
38//!
39//! let mut reader = Reader::new(&octets);
40//! println!("{}", reader.unsigned(0, 7)?);
41//! println!("{}", reader.unsigned_extensible(0, 255)?);
42//! println!("{}", reader.enumerated(6, 0, true)?);
43//! println!("{} items: {} {}", reader.size(1, 256)?, reader.bit()?, reader.bit()?);
44//!
45//! println!("{}", Writer::new(&mut Vec::new()).unsigned(8, 0, 7).unwrap_err());
46//! Ok(())
47//! }
48//! ```
49//!
50//! ```text
51//! [a0, c8, 30, 01, 80]
52//! 5
53//! 200
54//! 3
55//! 2 items: true false
56//! the value does not encode: 8 is outside its constraint (0..7)
57//! ```
58//!
59//! # Tasks
60//!
61//! The [README](https://github.com/linouxis9/oxirush-aper#readme) has an example of
62//! each, with what it prints.
63//!
64//! | Task | Items |
65//! |------|-------|
66//! | Write the codec of a type | [`AsnType`], [`Decode`], [`Encode`], [`decode`], [`encode`], [`encode_into`] |
67//! | Read and write the fields | [`Reader`], [`Writer`], [`UNBOUNDED`] |
68//! | Bit strings | [`Bits`], [`BitString`] |
69//! | An open type | [`Reader::open_type`], [`Writer::begin_open_type`], [`Writer::end_open_type`], [`Octets`] |
70//! | Extension additions | [`Reader::additions`], [`Additions`], [`Writer::additions`], [`Reader::skip_additions`] |
71//! | What the reader accepts | [`decode_with_faults`], [`decode_strict`], [`Fault`], [`Reader::noting`] |
72//! | Check a PDU against the object set of its message | [`check`] |
73//! | The macros and the readers of a protocol crate | [`protocol`] |
74//!
75//! A [`Reader`] and a [`Writer`] have a method for each field of X.691 that the two
76//! protocols of the family use. They work on octets where the encoding is aligned and
77//! on bits where it is not, the bounds of a constraint are their arguments, and a
78//! reader allocates nothing but the lists and the strings in fragments of what it
79//! reads.
80//!
81//! The values are native numbers, arrays for an OCTET STRING of one size, [`Octets`]
82//! for the others and for an open type, [`Bits`] for a BIT STRING of 64 bits at most,
83//! [`BitString`] for a longer one, `Cow<str>` for a character string and
84//! [`ObjectIdentifier`].
85//!
86//! # The OxiRush family
87//!
88//! - [oxirush-utils](https://docs.rs/oxirush-utils): the 3GPP identities, and the JSON tree with its paths that the others share.
89//! - [oxirush-aper](https://docs.rs/oxirush-aper): aligned PER, the runtime of the NGAP and S1AP codecs.
90//! - [oxirush-nas](https://docs.rs/oxirush-nas): the NAS messages of 5GS and EPS.
91//! - [oxirush-security](https://docs.rs/oxirush-security): NAS security, AKA and the derivation of keys.
92//! - [oxirush-ngap](https://docs.rs/oxirush-ngap): NGAP, between a gNB and an AMF.
93//! - [oxirush-s1ap](https://docs.rs/oxirush-s1ap): S1AP, between an eNB and an MME.
94//! - [oxirush-gtp-u](https://docs.rs/oxirush-gtp-u): GTP-U tunnels, with a test UPF.
95//! - [oxirush-sctp](https://docs.rs/oxirush-sctp): SCTP sockets, which carry NGAP and S1AP.
96
97#![no_std]
98#![forbid(unsafe_code)]
99#![cfg_attr(docsrs, feature(doc_cfg))]
100
101extern crate alloc;
102
103mod bits;
104pub mod check;
105mod error;
106mod faults;
107// The plumbing of the generated crates, hidden from the documentation: it changes with
108// them, and oxirush-ngap and oxirush-s1ap depend on the one version of this crate that is
109// released with theirs (`=0.1.0`), so that no other version of it reaches their builds.
110#[cfg(feature = "inspect")]
111#[doc(hidden)]
112pub mod inspect;
113#[cfg(feature = "inspect")]
114pub mod json;
115mod octets;
116mod oid;
117pub mod protocol;
118mod reader;
119mod writer;
120
121use alloc::vec::Vec;
122
123/// The examples of the README, which are compiled and run with the tests of the
124/// documentation.
125#[cfg(doctest)]
126#[doc = include_str!("../README.md")]
127struct Readme;
128
129pub use bits::{BitString, Bits};
130pub use error::{Criticality, Error, ErrorIe, ErrorKind};
131pub use faults::{Fault, Faults, Noted};
132pub use octets::Octets;
133pub use oid::ObjectIdentifier;
134pub use reader::{Additions, Reader};
135pub use writer::Writer;
136
137/// The upper bound of a size constraint that has none, as `SIZE (1..MAX)`.
138///
139/// ```
140/// use oxirush_aper::{Reader, UNBOUNDED, Writer};
141///
142/// // OCTET STRING (SIZE (1..MAX)): the length of the octets, then the octets
143/// let mut octets = Vec::new();
144/// Writer::new(&mut octets).octet_string(&[0xca, 0xfe], 1, UNBOUNDED)?;
145/// assert_eq!(octets, [0x02, 0xca, 0xfe]);
146/// assert_eq!(Reader::new(&octets).octet_string(1, UNBOUNDED)?, [0xca, 0xfe]);
147/// # Ok::<(), oxirush_aper::Error>(())
148/// ```
149pub const UNBOUNDED: usize = usize::MAX;
150
151/// A type of an ASN.1 module. The example of [`Decode`] implements it.
152pub trait AsnType {
153 /// The name that ASN.1 gives the type, as `AMF-UE-NGAP-ID`: an error names the
154 /// type by it.
155 const NAME: &'static str;
156}
157
158/// A type whose values are read in aligned PER. `'a` is how long the octets live that a
159/// value borrows from.
160///
161/// A type of a module implements [`AsnType`], `Decode` and [`Encode`]:
162///
163/// ```
164/// use oxirush_aper::{AsnType, Decode, Encode, Error, Reader, Writer};
165///
166/// // AMF-UE-NGAP-ID ::= INTEGER (0..1099511627775)
167/// #[derive(Debug, PartialEq)]
168/// struct AmfUeNgapId(u64);
169///
170/// impl AsnType for AmfUeNgapId {
171/// const NAME: &'static str = "AMF-UE-NGAP-ID";
172/// }
173///
174/// impl Decode<'_> for AmfUeNgapId {
175/// fn decode(reader: &mut Reader<'_>) -> Result<Self, Error> {
176/// Ok(Self(reader.unsigned(0, 1_099_511_627_775)?))
177/// }
178/// }
179///
180/// impl Encode for AmfUeNgapId {
181/// fn encode(&self, writer: &mut Writer<'_>) -> Result<(), Error> {
182/// writer.unsigned(self.0, 0, 1_099_511_627_775)
183/// }
184/// }
185///
186/// // The length of the number in three bits, then its octet, aligned
187/// let octets = oxirush_aper::encode(&AmfUeNgapId(42))?;
188/// assert_eq!(octets, [0x00, 0x2a]);
189/// assert_eq!(oxirush_aper::decode::<AmfUeNgapId>(&octets)?, AmfUeNgapId(42));
190/// # Ok::<(), Error>(())
191/// ```
192pub trait Decode<'a>: AsnType + Sized {
193 /// The value at the reader, which is moved after it.
194 fn decode(reader: &mut Reader<'a>) -> Result<Self, Error>;
195}
196
197/// A type whose values are written in aligned PER. The example of [`Decode`]
198/// implements it.
199pub trait Encode: AsnType {
200 /// Write the value.
201 fn encode(&self, writer: &mut Writer<'_>) -> Result<(), Error>;
202}
203
204/// NULL: no bit.
205impl AsnType for () {
206 const NAME: &'static str = "NULL";
207}
208
209impl<'a> Decode<'a> for () {
210 fn decode(_: &mut Reader<'a>) -> Result<Self, Error> {
211 Ok(())
212 }
213}
214
215impl Encode for () {
216 fn encode(&self, _: &mut Writer<'_>) -> Result<(), Error> {
217 Ok(())
218 }
219}
220
221/// BOOLEAN: one bit.
222impl AsnType for bool {
223 const NAME: &'static str = "BOOLEAN";
224}
225
226impl<'a> Decode<'a> for bool {
227 fn decode(reader: &mut Reader<'a>) -> Result<Self, Error> {
228 reader.bit()
229 }
230}
231
232impl Encode for bool {
233 fn encode(&self, writer: &mut Writer<'_>) -> Result<(), Error> {
234 writer.bit(*self);
235 Ok(())
236 }
237}
238
239/// The value of the type `T` that `octets` are the complete encoding of (X.691 §11.1):
240/// one value, whole, and no octet after it. What the value holds of octets and of
241/// strings is borrowed from `octets`.
242///
243/// ```
244/// use oxirush_aper::Octets;
245///
246/// // An OCTET STRING without a constraint, whose octets stay in the input
247/// let input = [0x02, 0xca, 0xfe];
248/// let octets: Octets = oxirush_aper::decode(&input)?;
249/// assert_eq!(octets.as_borrowed(), Some(&input[1..]));
250///
251/// // Octets that end early, and an octet after the value
252/// assert!(oxirush_aper::decode::<Octets>(&input[..2]).is_err());
253/// assert!(oxirush_aper::decode::<Octets>(&[0x01, 0xca, 0xfe]).is_err());
254/// # Ok::<(), oxirush_aper::Error>(())
255/// ```
256///
257/// # Errors
258///
259/// An error of the kind `Decode`, which names the type, says why, and gives the field
260/// where the decoding stopped.
261pub fn decode<'a, T: Decode<'a>>(octets: &'a [u8]) -> Result<T, Error> {
262 if octets.is_empty() {
263 return Err(Error::decode("there are no octets").decoding(T::NAME));
264 }
265 let mut reader = Reader::new(octets);
266 match T::decode(&mut reader) {
267 Ok(value) => match reader.finish() {
268 Ok(()) => Ok(value),
269 Err(error) => Err(error.decoding(T::NAME)),
270 },
271 Err(error) => Err(error.decoding(T::NAME)),
272 }
273}
274
275/// The value of the type `T` that `octets` are the complete encoding of, as [`decode`]
276/// reads it, and what of the encoding is in another form than the one that X.691 gives
277/// it: the [`Faults`] of the reader, with the padding of the end among them. They are
278/// none for octets that are written by the clauses.
279///
280/// A value in such a form is whole, and the encoder of a peer may write it: a receiver
281/// reads it, a tool that tests the peer says that its encoder is not canonical, and
282/// [`decode_strict`] refuses it. What a value holds as the octets of an open type is not
283/// read here, and has its faults where it is decoded.
284///
285/// ```
286/// use oxirush_aper::{Fault, Octets, decode_with_faults};
287///
288/// // An OCTET STRING of one octet, with its length in two octets
289/// let (octets, faults) = decode_with_faults::<Octets>(&[0x80, 0x01, 0xaa])?;
290/// assert_eq!(octets, [0xaa]);
291/// assert_eq!(faults[0].fault, Fault::LengthInTwoOctets);
292/// assert_eq!(faults[0].to_string(), "at bit 0, a length of 127 or less is in two octets (X.691 §11.9.3.6)");
293/// # Ok::<(), oxirush_aper::Error>(())
294/// ```
295///
296/// # Errors
297///
298/// Those of [`decode`].
299pub fn decode_with_faults<'a, T: Decode<'a>>(octets: &'a [u8]) -> Result<(T, Faults), Error> {
300 if octets.is_empty() {
301 return Err(Error::decode("there are no octets").decoding(T::NAME));
302 }
303 let mut reader = Reader::noting(octets);
304 let value = match T::decode(&mut reader) {
305 Ok(value) => value,
306 Err(error) => return Err(error.decoding(T::NAME)),
307 };
308 // The bits after the value, up to the end of its last octet, are padding.
309 reader.align();
310 match reader.finish() {
311 Ok(()) => Ok((value, *reader.faults())),
312 Err(error) => Err(error.decoding(T::NAME)),
313 }
314}
315
316/// The value of the type `T` that `octets` are the complete encoding of, when they are
317/// the one encoding that X.691 gives the value: what [`decode`] reads in another form,
318/// a [`Fault`], is an error here. The counterpart of [`encode_strict`].
319///
320/// ```
321/// use oxirush_aper::{Octets, decode, decode_strict};
322///
323/// // An OCTET STRING of one octet, whose length is in two octets
324/// let input = [0x80, 0x01, 0xaa];
325/// assert!(decode::<Octets>(&input).is_ok());
326/// assert!(decode_strict::<Octets>(&input).is_err());
327/// ```
328///
329/// # Errors
330///
331/// Those of [`decode`], and an error of the kind `Decode` that says the first fault,
332/// its clause and the bit at which it is.
333pub fn decode_strict<'a, T: Decode<'a>>(octets: &'a [u8]) -> Result<T, Error> {
334 let (value, faults) = decode_with_faults(octets)?;
335 match faults.first() {
336 None => Ok(value),
337 Some(noted) => {
338 let why = alloc::format!(
339 "{noted}: the value is read, and a sender by the clause writes it otherwise"
340 );
341 Err(Error::decode(why).decoding(T::NAME))
342 }
343 }
344}
345
346/// The complete encoding of `value` (X.691 §11.1): its fields, zero bits up to the end
347/// of the last octet, and one zero octet for a value that has no field.
348///
349/// # Errors
350///
351/// An error of the kind `Encode`, which names the type and says why, as for a number
352/// outside its constraint.
353///
354/// ```
355/// use oxirush_aper::Octets;
356///
357/// // An OCTET STRING without a constraint: its length, then its octets
358/// let octets = Octets::from(&[0xca, 0xfe][..]);
359/// assert_eq!(oxirush_aper::encode(&octets)?, [0x02, 0xca, 0xfe]);
360/// # Ok::<(), oxirush_aper::Error>(())
361/// ```
362pub fn encode<T: Encode>(value: &T) -> Result<Vec<u8>, Error> {
363 let mut octets = Vec::new();
364 encode_into(value, &mut octets)?;
365 Ok(octets)
366}
367
368/// The complete encoding of `value`, as [`encode`] gives it, for a value that is one of
369/// this version of its type: a number or a size outside the root of an extensible
370/// constraint, which `encode` writes as an extension, is an error.
371///
372/// An extensible constraint, as `INTEGER (0..255, ...)` or `SIZE (1..150, ...)`, has a
373/// receiver read what a later version of the type allows, so `encode` writes any value.
374/// The sender of this version sends none of them: a name of no character is no AMF
375/// name, though it encodes.
376///
377/// # Errors
378///
379/// An error of the kind `Encode`, which names the type, says why, and gives the field
380/// that does not encode.
381pub fn encode_strict<T: Encode>(value: &T) -> Result<Vec<u8>, Error> {
382 let mut octets = Vec::new();
383 match value.encode(&mut Writer::strict(&mut octets)) {
384 Ok(()) => {
385 if octets.is_empty() {
386 octets.push(0);
387 }
388 Ok(octets)
389 }
390 Err(error) => Err(error.encoding(T::NAME)),
391 }
392}
393
394/// Append the complete encoding of `value` to `octets`. After an error, `octets` are
395/// as they were.
396///
397/// ```
398/// // A BOOLEAN, one bit in an octet, then a NULL, no bit, which is one zero octet
399/// let mut octets = vec![0xff];
400/// oxirush_aper::encode_into(&true, &mut octets)?;
401/// oxirush_aper::encode_into(&(), &mut octets)?;
402/// assert_eq!(octets, [0xff, 0x80, 0x00]);
403/// # Ok::<(), oxirush_aper::Error>(())
404/// ```
405pub fn encode_into<T: Encode>(value: &T, octets: &mut Vec<u8>) -> Result<(), Error> {
406 let start = octets.len();
407 match value.encode(&mut Writer::new(octets)) {
408 Ok(()) => {
409 if octets.len() == start {
410 octets.push(0);
411 }
412 Ok(())
413 }
414 Err(error) => {
415 octets.truncate(start);
416 Err(error.encoding(T::NAME))
417 }
418 }
419}