Skip to main content

netget/server/ngap/
aper.rs

1//! ALIGNED PER (ITU-T X.691) — the subset NGAP needs, hand-written and bounded.
2//!
3//! NGAP (TS 38.413 section 9.4) is encoded with the ALIGNED variant of PER. Every IE's value is
4//! an *open type* — a length-prefixed, independently encoded octet string — so a receiver can step
5//! over an IE it does not understand by its length alone. That property is what makes a hand-rolled
6//! subset honest: NetGet decodes the IEs of the procedures it implements and skips the rest by
7//! length, reporting their ids, rather than pretending to understand the whole 38.413 module.
8//!
9//! Every read is bounds-checked against the input, and every count or length the peer declares is
10//! checked against the constraint *and* against the octets actually present before anything is
11//! allocated for it. A [`Reader`] never recurses, so there is no depth to bound here; the schema
12//! walk in `codec.rs` is a fixed, non-recursive sequence of calls.
13//!
14//! The rules implemented, with their X.691 clause:
15//!
16//! - **Constrained whole number** (11.5.7 / 10.5.7): range 1 is empty; range ≤ 255 is a bit-field
17//!   of the minimum width, *not* aligned; range 256 is one aligned octet; range ≤ 64K is two
18//!   aligned octets; beyond that the value takes a 1..N octet field whose length (minus one) is
19//!   itself a constrained whole number, followed by the aligned octets.
20//! - **Length determinant** (11.9): a size constraint with `ub < 64K` is a constrained whole number
21//!   and is not aligned when its range is ≤ 255; otherwise the general form — aligned, one octet
22//!   below 128, two octets (`10xxxxxx`) below 16384. Fragmentation (≥ 16384) is refused: nothing in
23//!   the NGAP subset NetGet speaks comes near it, and a peer that announces it is answered with a
24//!   transfer-syntax error rather than buffered.
25//! - **Normally small non-negative whole number** (11.6): extension indices and extension-addition
26//!   counts. Values above 63 are refused.
27//! - **Open type** (11.2): aligned general length, then a complete encoding.
28
29use std::fmt;
30
31/// A decode or encode refusal. Carries what was being read so the log names the field; nothing
32/// in it ever reaches the peer (the peer gets a Cause value).
33#[derive(Debug, Clone, PartialEq, Eq)]
34pub struct AperError(pub String);
35
36impl fmt::Display for AperError {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        f.write_str(&self.0)
39    }
40}
41
42impl std::error::Error for AperError {}
43
44pub type AperResult<T> = Result<T, AperError>;
45
46pub fn err<T>(msg: impl Into<String>) -> AperResult<T> {
47    Err(AperError(msg.into()))
48}
49
50/// The largest length NetGet will accept from a general length determinant: one below the
51/// fragmentation threshold (X.691 11.9.3.8). Anything at or above it is refused.
52pub const MAX_UNFRAGMENTED: usize = 16_383;
53
54/// Bits needed to encode `range` distinct values (`range >= 1`).
55pub fn bits_for_range(range: u64) -> u32 {
56    if range <= 1 {
57        0
58    } else {
59        64 - (range - 1).leading_zeros()
60    }
61}
62
63/// Octets needed to hold `value` (at least one).
64fn octets_for(value: u64) -> usize {
65    let bits = 64 - value.leading_zeros() as usize;
66    bits.div_ceil(8).max(1)
67}
68
69// ============================================================================
70// Reader
71// ============================================================================
72
73/// A bit cursor over one complete APER encoding.
74pub struct Reader<'a> {
75    data: &'a [u8],
76    /// Position in bits from the start of `data`.
77    pos: usize,
78}
79
80impl<'a> Reader<'a> {
81    pub fn new(data: &'a [u8]) -> Self {
82        Self { data, pos: 0 }
83    }
84
85    fn remaining_bits(&self) -> usize {
86        self.data.len() * 8 - self.pos
87    }
88
89    /// Octets left after aligning, for checking a declared length before trusting it.
90    pub fn remaining_octets_aligned(&self) -> usize {
91        let aligned = self.pos.div_ceil(8);
92        self.data.len().saturating_sub(aligned)
93    }
94
95    pub fn align(&mut self) {
96        self.pos = self.pos.div_ceil(8) * 8;
97    }
98
99    pub fn bits(&mut self, n: u32, what: &str) -> AperResult<u64> {
100        if n > 64 {
101            return err(format!("{what}: {n}-bit field is wider than 64"));
102        }
103        if self.remaining_bits() < n as usize {
104            return err(format!(
105                "{what}: needs {n} bits, {} remain",
106                self.remaining_bits()
107            ));
108        }
109        let mut value = 0u64;
110        for _ in 0..n {
111            let byte = self.data[self.pos / 8];
112            let bit = (byte >> (7 - (self.pos % 8))) & 1;
113            value = (value << 1) | bit as u64;
114            self.pos += 1;
115        }
116        Ok(value)
117    }
118
119    pub fn bit(&mut self, what: &str) -> AperResult<bool> {
120        Ok(self.bits(1, what)? == 1)
121    }
122
123    /// `n` octets starting at the next octet boundary.
124    pub fn octets_aligned(&mut self, n: usize, what: &str) -> AperResult<&'a [u8]> {
125        self.align();
126        let start = self.pos / 8;
127        if self.data.len() - start < n {
128            return err(format!(
129                "{what}: declares {n} octets, {} remain",
130                self.data.len() - start
131            ));
132        }
133        self.pos += n * 8;
134        Ok(&self.data[start..start + n])
135    }
136
137    /// `n` octets that are not aligned (fixed-size strings of at most two octets).
138    pub fn octets_unaligned(&mut self, n: usize, what: &str) -> AperResult<Vec<u8>> {
139        let mut out = Vec::with_capacity(n);
140        for _ in 0..n {
141            out.push(self.bits(8, what)? as u8);
142        }
143        Ok(out)
144    }
145
146    /// X.691 10.5.7, ALIGNED variant.
147    pub fn constrained(&mut self, lb: u64, ub: u64, what: &str) -> AperResult<u64> {
148        if ub < lb {
149            return err(format!("{what}: empty range"));
150        }
151        let range = (ub - lb).checked_add(1);
152        let value = match range {
153            Some(1) => 0,
154            Some(r) if r <= 255 => self.bits(bits_for_range(r), what)?,
155            Some(256) => {
156                self.align();
157                self.bits(8, what)?
158            }
159            Some(r) if r <= 65_536 => {
160                self.align();
161                self.bits(16, what)?
162            }
163            _ => {
164                // Indefinite-length case: (octets - 1) as a constrained whole number, then the
165                // octets, aligned.
166                let max_octets = octets_for(ub - lb) as u64;
167                let len = self.bits(bits_for_range(max_octets), what)? + 1;
168                let raw = self.octets_aligned(len as usize, what)?;
169                raw.iter().fold(0u64, |acc, b| (acc << 8) | *b as u64)
170            }
171        };
172        let v = lb
173            .checked_add(value)
174            .ok_or_else(|| AperError(format!("{what}: value overflows")))?;
175        if v > ub {
176            return err(format!("{what}: {v} is above the upper bound {ub}"));
177        }
178        Ok(v)
179    }
180
181    /// General (unconstrained) length determinant, X.691 11.9.3.6-11.9.3.8. Fragmented lengths
182    /// are refused.
183    pub fn general_length(&mut self, what: &str) -> AperResult<usize> {
184        self.align();
185        let first = self.bits(8, what)? as usize;
186        if first & 0x80 == 0 {
187            return Ok(first);
188        }
189        if first & 0xC0 == 0x80 {
190            let second = self.bits(8, what)? as usize;
191            return Ok(((first & 0x3F) << 8) | second);
192        }
193        err(format!(
194            "{what}: fragmented length (≥16384) is not accepted"
195        ))
196    }
197
198    /// A length or count bounded by a size constraint `lb..=ub` (X.691 11.9.4).
199    pub fn size(&mut self, lb: usize, ub: usize, what: &str) -> AperResult<usize> {
200        let n = if ub < 65_536 {
201            self.constrained(lb as u64, ub as u64, what)? as usize
202        } else {
203            self.general_length(what)?
204        };
205        if n < lb || n > ub {
206            return err(format!("{what}: size {n} outside {lb}..{ub}"));
207        }
208        Ok(n)
209    }
210
211    /// X.691 11.6.
212    pub fn normally_small(&mut self, what: &str) -> AperResult<u64> {
213        if self.bit(what)? {
214            return err(format!(
215                "{what}: normally-small value above 63 is not accepted"
216            ));
217        }
218        self.bits(6, what)
219    }
220
221    /// An ENUMERATED with `root` root values, extensible or not. Returns `(extended, index)`.
222    pub fn enumerated(
223        &mut self,
224        root: u64,
225        extensible: bool,
226        what: &str,
227    ) -> AperResult<(bool, u64)> {
228        if extensible && self.bit(what)? {
229            return Ok((true, self.normally_small(what)?));
230        }
231        Ok((false, self.constrained(0, root - 1, what)?))
232    }
233
234    /// An open type's contents: the octets of one complete inner encoding.
235    pub fn open_type(&mut self, what: &str) -> AperResult<&'a [u8]> {
236        let len = self.general_length(what)?;
237        if len > self.remaining_octets_aligned() {
238            return err(format!(
239                "{what}: open type declares {len} octets, {} remain",
240                self.remaining_octets_aligned()
241            ));
242        }
243        self.octets_aligned(len, what)
244    }
245
246    /// OCTET STRING with no size constraint: general length, aligned octets.
247    pub fn octet_string(&mut self, max: usize, what: &str) -> AperResult<&'a [u8]> {
248        let len = self.general_length(what)?;
249        if len > max {
250            return err(format!(
251                "{what}: {len} octets exceeds the {max}-octet bound"
252            ));
253        }
254        self.octets_aligned(len, what)
255    }
256
257    /// OCTET STRING (SIZE(n)) — X.691 17.6/17.7.
258    pub fn fixed_octets(&mut self, n: usize, what: &str) -> AperResult<Vec<u8>> {
259        if n <= 2 {
260            self.octets_unaligned(n, what)
261        } else {
262            Ok(self.octets_aligned(n, what)?.to_vec())
263        }
264    }
265
266    /// BIT STRING (SIZE(n)) — X.691 16.9/16.10. Returned right-aligned in a u64.
267    pub fn fixed_bits(&mut self, n: u32, what: &str) -> AperResult<u64> {
268        if n > 16 {
269            self.align();
270        }
271        self.bits(n, what)
272    }
273
274    /// BIT STRING (SIZE(lb..ub)) with `ub < 64K` and `ub <= 64` — X.691 16.11. Returns
275    /// `(value, length_in_bits)`.
276    pub fn varying_bits(&mut self, lb: u32, ub: u32, what: &str) -> AperResult<(u64, u32)> {
277        let n = self.size(lb as usize, ub as usize, what)? as u32;
278        if n > 64 {
279            return err(format!("{what}: {n} bits is wider than 64"));
280        }
281        self.align();
282        Ok((self.bits(n, what)?, n))
283    }
284
285    /// The extension bit of a SEQUENCE or CHOICE with an extension marker.
286    pub fn extension_bit(&mut self, what: &str) -> AperResult<bool> {
287        self.bit(what)
288    }
289
290    /// Step over a SEQUENCE's extension additions (X.691 19.7-19.9): a normally-small count, a
291    /// presence bitmap, then one open type per present addition. Returns how many were present.
292    pub fn skip_extension_additions(&mut self, what: &str) -> AperResult<usize> {
293        let n = self.normally_small(what)? as u32 + 1;
294        let mut present = 0usize;
295        for _ in 0..n {
296            if self.bit(what)? {
297                present += 1;
298            }
299        }
300        for _ in 0..present {
301            self.open_type(what)?;
302        }
303        Ok(present)
304    }
305
306    /// PrintableString (SIZE(lb..ub, ...)) with 8-bit characters (X.691 30.5, ALIGNED).
307    pub fn printable_string(&mut self, lb: usize, ub: usize, what: &str) -> AperResult<String> {
308        let extended = self.bit(what)?;
309        let n = if extended {
310            // Outside the root: a semi-constrained length. Accept it only up to the root's own
311            // upper bound — the extension exists for a later release, not for a bigger buffer.
312            let n = self.general_length(what)?;
313            if n > ub {
314                return err(format!("{what}: {n} characters exceeds {ub}"));
315            }
316            n
317        } else {
318            self.constrained(lb as u64, ub as u64, what)? as usize
319        };
320        // X.691 30.5.7: alignment follows the constraint's upper bound, not this string's length.
321        let raw = if ub * 8 > 16 || extended {
322            self.octets_aligned(n, what)?.to_vec()
323        } else {
324            self.octets_unaligned(n, what)?
325        };
326        if !raw.iter().all(|b| is_printable(*b)) {
327            return err(format!("{what}: not a PrintableString"));
328        }
329        Ok(String::from_utf8_lossy(&raw).into_owned())
330    }
331
332    /// True when every remaining bit is zero padding of the final octet.
333    pub fn at_end(&self) -> bool {
334        let aligned = self.pos.div_ceil(8);
335        aligned >= self.data.len()
336    }
337}
338
339/// X.680 41.4 Table 10: the PrintableString character set.
340pub fn is_printable(b: u8) -> bool {
341    b.is_ascii_alphanumeric() || b" '()+,-./:=?".contains(&b)
342}
343
344// ============================================================================
345// Writer
346// ============================================================================
347
348/// A bit cursor building one complete APER encoding.
349#[derive(Default)]
350pub struct Writer {
351    data: Vec<u8>,
352    /// Bits used in the final octet of `data` (0 means the last octet is full or there is none).
353    bit_len: usize,
354}
355
356impl Writer {
357    pub fn new() -> Self {
358        Self::default()
359    }
360
361    pub fn bits(&mut self, value: u64, n: u32) {
362        for i in (0..n).rev() {
363            let bit = ((value >> i) & 1) as u8;
364            if self.bit_len.is_multiple_of(8) {
365                self.data.push(0);
366            }
367            if bit == 1 {
368                let last = self.data.len() - 1;
369                self.data[last] |= 1 << (7 - (self.bit_len % 8));
370            }
371            self.bit_len += 1;
372        }
373    }
374
375    pub fn bit(&mut self, b: bool) {
376        self.bits(b as u64, 1);
377    }
378
379    pub fn align(&mut self) {
380        self.bit_len = self.bit_len.div_ceil(8) * 8;
381    }
382
383    pub fn octets_aligned(&mut self, bytes: &[u8]) {
384        self.align();
385        self.data.extend_from_slice(bytes);
386        self.bit_len += bytes.len() * 8;
387    }
388
389    pub fn octets_unaligned(&mut self, bytes: &[u8]) {
390        for b in bytes {
391            self.bits(*b as u64, 8);
392        }
393    }
394
395    pub fn constrained(&mut self, value: u64, lb: u64, ub: u64, what: &str) -> AperResult<()> {
396        if value < lb || value > ub {
397            return err(format!("{what}: {value} outside {lb}..{ub}"));
398        }
399        let v = value - lb;
400        match (ub - lb).checked_add(1) {
401            Some(1) => {}
402            Some(r) if r <= 255 => self.bits(v, bits_for_range(r)),
403            Some(256) => {
404                self.align();
405                self.bits(v, 8);
406            }
407            Some(r) if r <= 65_536 => {
408                self.align();
409                self.bits(v, 16);
410            }
411            _ => {
412                let max_octets = octets_for(ub - lb) as u64;
413                let len = octets_for(v);
414                self.bits(len as u64 - 1, bits_for_range(max_octets));
415                self.align();
416                for i in (0..len).rev() {
417                    self.bits((v >> (8 * i)) & 0xFF, 8);
418                }
419            }
420        }
421        Ok(())
422    }
423
424    pub fn general_length(&mut self, n: usize, what: &str) -> AperResult<()> {
425        self.align();
426        if n < 128 {
427            self.bits(n as u64, 8);
428        } else if n <= MAX_UNFRAGMENTED {
429            self.bits(0x8000 | n as u64, 16);
430        } else {
431            return err(format!(
432                "{what}: {n} would need a fragmented length, which NetGet does not write"
433            ));
434        }
435        Ok(())
436    }
437
438    pub fn size(&mut self, n: usize, lb: usize, ub: usize, what: &str) -> AperResult<()> {
439        if n < lb || n > ub {
440            return err(format!("{what}: size {n} outside {lb}..{ub}"));
441        }
442        if ub < 65_536 {
443            self.constrained(n as u64, lb as u64, ub as u64, what)
444        } else {
445            self.general_length(n, what)
446        }
447    }
448
449    pub fn normally_small(&mut self, value: u64, what: &str) -> AperResult<()> {
450        if value > 63 {
451            return err(format!("{what}: {value} is not a normally-small number"));
452        }
453        self.bit(false);
454        self.bits(value, 6);
455        Ok(())
456    }
457
458    /// ENUMERATED: `index` in the root when `!extended`, in the extension list otherwise.
459    pub fn enumerated(
460        &mut self,
461        index: u64,
462        extended: bool,
463        root: u64,
464        extensible: bool,
465        what: &str,
466    ) -> AperResult<()> {
467        if extensible {
468            self.bit(extended);
469        } else if extended {
470            return err(format!("{what}: not extensible"));
471        }
472        if extended {
473            self.normally_small(index, what)
474        } else {
475            self.constrained(index, 0, root - 1, what)
476        }
477    }
478
479    /// Append `inner` (one complete encoding) as an open type.
480    pub fn open_type(&mut self, inner: &[u8], what: &str) -> AperResult<()> {
481        self.general_length(inner.len(), what)?;
482        self.octets_aligned(inner);
483        Ok(())
484    }
485
486    pub fn octet_string(&mut self, bytes: &[u8], what: &str) -> AperResult<()> {
487        self.general_length(bytes.len(), what)?;
488        self.octets_aligned(bytes);
489        Ok(())
490    }
491
492    pub fn fixed_octets(&mut self, bytes: &[u8]) {
493        if bytes.len() <= 2 {
494            self.octets_unaligned(bytes);
495        } else {
496            self.octets_aligned(bytes);
497        }
498    }
499
500    pub fn fixed_bits(&mut self, value: u64, n: u32) {
501        if n > 16 {
502            self.align();
503        }
504        self.bits(value, n);
505    }
506
507    pub fn varying_bits(
508        &mut self,
509        value: u64,
510        n: u32,
511        lb: u32,
512        ub: u32,
513        what: &str,
514    ) -> AperResult<()> {
515        if n < 64 && value >> n != 0 {
516            return err(format!("{what}: {value} does not fit in {n} bits"));
517        }
518        self.size(n as usize, lb as usize, ub as usize, what)?;
519        self.align();
520        self.bits(value, n);
521        Ok(())
522    }
523
524    pub fn printable_string(
525        &mut self,
526        s: &str,
527        lb: usize,
528        ub: usize,
529        what: &str,
530    ) -> AperResult<()> {
531        let raw = s.as_bytes();
532        if !raw.iter().all(|b| is_printable(*b)) {
533            return err(format!(
534                "{what}: {s:?} is not a PrintableString (letters, digits, space and '()+,-./:=?)"
535            ));
536        }
537        if raw.len() < lb || raw.len() > ub {
538            return err(format!(
539                "{what}: {} characters outside {lb}..{ub}",
540                raw.len()
541            ));
542        }
543        self.bit(false);
544        self.constrained(raw.len() as u64, lb as u64, ub as u64, what)?;
545        if ub * 8 > 16 {
546            self.octets_aligned(raw);
547        } else {
548            self.octets_unaligned(raw);
549        }
550        Ok(())
551    }
552
553    /// The finished encoding. X.691 11.1: a complete encoding is at least one octet.
554    pub fn finish(mut self) -> Vec<u8> {
555        if self.data.is_empty() {
556            self.data.push(0);
557        }
558        self.data
559    }
560}