der_parser/ber/
parser.rs

1use crate::ber::wrap_any::parse_ber_any_r;
2use crate::ber::*;
3use crate::error::*;
4use asn1_rs::{FromBer, Tag};
5use nom::bytes::streaming::take;
6use nom::{Err, Offset};
7
8/// Default maximum recursion limit
9pub const MAX_RECURSION: usize = 50;
10
11/// Default maximum object size (2^32)
12pub const MAX_OBJECT_SIZE: usize = 4_294_967_295;
13
14/// Skip object content, and return true if object was End-Of-Content
15pub(crate) fn ber_skip_object_content<'a>(
16    i: &'a [u8],
17    hdr: &Header,
18    max_depth: usize,
19) -> BerResult<'a, bool> {
20    if max_depth == 0 {
21        return Err(Err::Error(BerError::BerMaxDepth));
22    }
23    match hdr.length() {
24        Length::Definite(l) => {
25            if l == 0 && hdr.tag() == Tag::EndOfContent {
26                return Ok((i, true));
27            }
28            let (i, _) = take(l)(i)?;
29            Ok((i, false))
30        }
31        Length::Indefinite => {
32            if hdr.is_primitive() {
33                return Err(Err::Error(BerError::ConstructExpected));
34            }
35            // read objects until EndOfContent (00 00)
36            // this is recursive
37            let mut i = i;
38            loop {
39                let (i2, header2) = ber_read_element_header(i)?;
40                let (i3, eoc) = ber_skip_object_content(i2, &header2, max_depth - 1)?;
41                if eoc {
42                    // return false, since top object was not EndOfContent
43                    return Ok((i3, false));
44                }
45                i = i3;
46            }
47        }
48    }
49}
50
51/// Read object raw content (bytes)
52pub(crate) fn ber_get_object_content<'a>(
53    i: &'a [u8],
54    hdr: &Header,
55    max_depth: usize,
56) -> BerResult<'a, &'a [u8]> {
57    let start_i = i;
58    let (i, _) = ber_skip_object_content(i, hdr, max_depth)?;
59    let len = start_i.offset(i);
60    let (content, i) = start_i.split_at(len);
61    // if len is indefinite, there are 2 extra bytes for EOC
62    if hdr.length() == Length::Indefinite {
63        let len = content.len();
64        assert!(len >= 2);
65        Ok((i, &content[..len - 2]))
66    } else {
67        Ok((i, content))
68    }
69}
70
71/// Try to parse an input bit string as u64.
72///
73/// Note: this is for the primitive BER/DER encoding only, the
74/// constructed BER encoding for BIT STRING does not seem to be
75/// supported at all by the library currently.
76#[inline]
77pub(crate) fn bitstring_to_u64(
78    padding_bits: usize,
79    data: &BitStringObject,
80) -> Result<u64, BerError> {
81    let raw_bytes = data.data;
82    let bit_size = (raw_bytes.len() * 8)
83        .checked_sub(padding_bits)
84        .ok_or(BerError::InvalidLength)?;
85    if bit_size > 64 {
86        return Err(BerError::IntegerTooLarge);
87    }
88    let padding_bits = padding_bits % 8;
89    let num_bytes = if bit_size % 8 > 0 {
90        (bit_size / 8) + 1
91    } else {
92        bit_size / 8
93    };
94    let mut resulting_integer: u64 = 0;
95    for &c in &raw_bytes[..num_bytes] {
96        resulting_integer <<= 8;
97        resulting_integer |= c as u64;
98    }
99    Ok(resulting_integer >> padding_bits)
100}
101
102/// Read an object header
103///
104/// ### Example
105///
106/// ```
107/// # use der_parser::ber::{ber_read_element_header, Class, Length, Tag};
108/// #
109/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
110/// let (i, hdr) = ber_read_element_header(bytes).expect("could not read header");
111///
112/// assert_eq!(hdr.class(), Class::Universal);
113/// assert_eq!(hdr.tag(), Tag::Integer);
114/// assert_eq!(hdr.length(), Length::Definite(3));
115/// ```
116#[inline]
117pub fn ber_read_element_header(i: &[u8]) -> BerResult<Header> {
118    Header::from_ber(i)
119}
120
121/// Parse the next bytes as the *content* of a BER object.
122///
123/// Content type is *not* checked to match tag, caller is responsible of providing the correct tag
124///
125/// This function is mostly used when parsing implicit tagged objects, when reading primitive
126/// types.
127///
128/// `max_depth` is the maximum allowed recursion for objects.
129///
130/// ### Example
131///
132/// ```
133/// # use der_parser::ber::{ber_read_element_content_as, ber_read_element_header, Tag};
134/// #
135/// # let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
136/// let (i, hdr) = ber_read_element_header(bytes).expect("could not read header");
137/// let (_, content) = ber_read_element_content_as(
138///     i, hdr.tag(), hdr.length(), hdr.is_constructed(), 5
139/// ).expect("parsing failed");
140/// #
141/// # assert_eq!(hdr.tag(), Tag::Integer);
142/// # assert_eq!(content.as_u32(), Ok(0x10001));
143/// ```
144#[inline]
145pub fn ber_read_element_content_as(
146    i: &[u8],
147    tag: Tag,
148    length: Length,
149    constructed: bool,
150    max_depth: usize,
151) -> BerResult<BerObjectContent> {
152    try_read_berobjectcontent_as(i, tag, length, constructed, max_depth)
153}
154
155/// Parse the next bytes as the content of a BER object (combinator, header reference)
156///
157/// Content type is *not* checked to match tag, caller is responsible of providing the correct tag
158///
159/// Caller is also responsible to check if parsing function consumed the expected number of
160/// bytes (`header.len`).
161///
162/// The arguments of the parse function are: `(input, ber_object_header, max_recursion)`.
163///
164/// This function differs from [`parse_ber_content2`](fn.parse_ber_content2.html) because it passes
165/// the BER object header by reference (required for ex. by `parse_ber_implicit`).
166///
167/// Example: manually parsing header and content
168///
169/// ```
170/// # use der_parser::ber::*;
171/// #
172/// # let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
173/// let (i, header) = ber_read_element_header(bytes).expect("parsing failed");
174/// let (rem, content) = parse_ber_content(header.tag())(i, &header, MAX_RECURSION)
175///     .expect("parsing failed");
176/// #
177/// # assert_eq!(header.tag(), Tag::Integer);
178/// ```
179pub fn parse_ber_content<'a>(
180    tag: Tag,
181) -> impl Fn(&'a [u8], &'_ Header, usize) -> BerResult<'a, BerObjectContent<'a>> {
182    move |i: &[u8], hdr: &Header, max_recursion: usize| {
183        ber_read_element_content_as(i, tag, hdr.length(), hdr.is_constructed(), max_recursion)
184    }
185}
186
187/// Parse the next bytes as the content of a BER object (combinator, owned header)
188///
189/// Content type is *not* checked to match tag, caller is responsible of providing the correct tag
190///
191/// Caller is also responsible to check if parsing function consumed the expected number of
192/// bytes (`header.len`).
193///
194/// The arguments of the parse function are: `(input, ber_object_header, max_recursion)`.
195///
196/// This function differs from [`parse_ber_content`](fn.parse_ber_content.html) because it passes
197/// an owned BER object header (required for ex. by `parse_ber_tagged_implicit_g`).
198///
199/// Example: manually parsing header and content
200///
201/// ```
202/// # use der_parser::ber::*;
203/// #
204/// # let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
205/// let (i, header) = ber_read_element_header(bytes).expect("parsing failed");
206/// let (rem, content) = parse_ber_content(header.tag())(i, &header, MAX_RECURSION)
207///     .expect("parsing failed");
208/// #
209/// # assert_eq!(header.tag(), Tag::Integer);
210/// ```
211pub fn parse_ber_content2<'a>(
212    tag: Tag,
213) -> impl Fn(&'a [u8], Header<'a>, usize) -> BerResult<'a, BerObjectContent<'a>> {
214    move |i: &[u8], hdr: Header, max_recursion: usize| {
215        ber_read_element_content_as(i, tag, hdr.length(), hdr.is_constructed(), max_recursion)
216    }
217}
218
219/// Parse a BER object, expecting a value with specified tag
220///
221/// The object is parsed recursively, with a maximum depth of `MAX_RECURSION`.
222///
223/// ### Example
224///
225/// ```
226/// use der_parser::ber::Tag;
227/// use der_parser::ber::parse_ber_with_tag;
228///
229/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
230/// let (_, obj) = parse_ber_with_tag(bytes, Tag::Integer).expect("parsing failed");
231///
232/// assert_eq!(obj.header.tag(), Tag::Integer);
233/// ```
234pub fn parse_ber_with_tag<T: Into<Tag>>(i: &[u8], tag: T) -> BerResult {
235    let tag = tag.into();
236    let (i, hdr) = ber_read_element_header(i)?;
237    hdr.assert_tag(tag)?;
238    let (i, content) = ber_read_element_content_as(
239        i,
240        hdr.tag(),
241        hdr.length(),
242        hdr.is_constructed(),
243        MAX_RECURSION,
244    )?;
245    Ok((i, BerObject::from_header_and_content(hdr, content)))
246}
247
248/// Read end of content marker
249#[inline]
250pub fn parse_ber_endofcontent(i: &[u8]) -> BerResult {
251    parse_ber_with_tag(i, Tag::EndOfContent)
252}
253
254/// Read a boolean value
255///
256/// The encoding of a boolean value shall be primitive. The contents octets shall consist of a
257/// single octet.
258///
259/// If the boolean value is FALSE, the octet shall be zero.
260/// If the boolean value is TRUE, the octet shall be one byte, and have all bits set to one (0xff).
261#[inline]
262pub fn parse_ber_bool(i: &[u8]) -> BerResult {
263    parse_ber_with_tag(i, Tag::Boolean)
264}
265
266/// Read an integer value
267///
268/// The encoding of a boolean value shall be primitive. The contents octets shall consist of one or
269/// more octets.
270///
271/// To access the content, use the [`as_u64`](struct.BerObject.html#method.as_u64),
272/// [`as_u32`](struct.BerObject.html#method.as_u32),
273/// [`as_biguint`](struct.BerObject.html#method.as_biguint) or
274/// [`as_bigint`](struct.BerObject.html#method.as_bigint) methods.
275/// Remember that a BER integer has unlimited size, so these methods return `Result` or `Option`
276/// objects.
277///
278/// # Examples
279///
280/// ```rust
281/// # extern crate nom;
282/// # use der_parser::ber::parse_ber_integer;
283/// # use der_parser::ber::{BerObject,BerObjectContent};
284/// let empty = &b""[..];
285/// let bytes = [0x02, 0x03, 0x01, 0x00, 0x01];
286/// let expected  = BerObject::from_obj(BerObjectContent::Integer(b"\x01\x00\x01"));
287/// assert_eq!(
288///     parse_ber_integer(&bytes),
289///     Ok((empty, expected))
290/// );
291/// ```
292#[inline]
293pub fn parse_ber_integer(i: &[u8]) -> BerResult {
294    parse_ber_with_tag(i, Tag::Integer)
295}
296
297/// Read an bitstring value
298#[inline]
299pub fn parse_ber_bitstring(i: &[u8]) -> BerResult {
300    parse_ber_with_tag(i, Tag::BitString)
301}
302
303/// Read an octetstring value
304#[inline]
305pub fn parse_ber_octetstring(i: &[u8]) -> BerResult {
306    parse_ber_with_tag(i, Tag::OctetString)
307}
308
309/// Read a null value
310#[inline]
311pub fn parse_ber_null(i: &[u8]) -> BerResult {
312    parse_ber_with_tag(i, Tag::Null)
313}
314
315/// Read an object identifier value
316#[inline]
317pub fn parse_ber_oid(i: &[u8]) -> BerResult {
318    parse_ber_with_tag(i, Tag::Oid)
319}
320
321/// Read an enumerated value
322#[inline]
323pub fn parse_ber_enum(i: &[u8]) -> BerResult {
324    parse_ber_with_tag(i, Tag::Enumerated)
325}
326
327/// Read a UTF-8 string value. The encoding is checked.
328#[inline]
329pub fn parse_ber_utf8string(i: &[u8]) -> BerResult {
330    parse_ber_with_tag(i, Tag::Utf8String)
331}
332
333/// Read a relative object identifier value
334#[inline]
335pub fn parse_ber_relative_oid(i: &[u8]) -> BerResult {
336    parse_ber_with_tag(i, Tag::RelativeOid)
337}
338
339/// Parse a sequence of BER elements
340///
341/// Read a sequence of BER objects, without any constraint on the types.
342/// Sequence is parsed recursively, so if constructed elements are found, they are parsed using the
343/// same function.
344///
345/// To read a specific sequence of objects (giving the expected types), use the
346/// [`parse_ber_sequence_defined`](macro.parse_ber_sequence_defined.html) macro.
347#[inline]
348pub fn parse_ber_sequence(i: &[u8]) -> BerResult {
349    parse_ber_with_tag(i, Tag::Sequence)
350}
351
352/// Parse a set of BER elements
353///
354/// Read a set of BER objects, without any constraint on the types.
355/// Set is parsed recursively, so if constructed elements are found, they are parsed using the
356/// same function.
357///
358/// To read a specific set of objects (giving the expected types), use the
359/// [`parse_ber_set_defined`](macro.parse_ber_set_defined.html) macro.
360#[inline]
361pub fn parse_ber_set(i: &[u8]) -> BerResult {
362    parse_ber_with_tag(i, Tag::Set)
363}
364
365/// Read a numeric string value. The content is verified to
366/// contain only digits and spaces.
367#[inline]
368pub fn parse_ber_numericstring(i: &[u8]) -> BerResult {
369    parse_ber_with_tag(i, Tag::NumericString)
370}
371
372/// Read a visible string value. The content is verified to
373/// contain only the allowed characters.
374#[inline]
375pub fn parse_ber_visiblestring(i: &[u8]) -> BerResult {
376    parse_ber_with_tag(i, Tag::VisibleString)
377}
378
379/// Read a printable string value. The content is verified to
380/// contain only the allowed characters.
381#[inline]
382pub fn parse_ber_printablestring(i: &[u8]) -> BerResult {
383    parse_ber_with_tag(i, Tag::PrintableString)
384}
385
386/// Read a T61 string value
387#[inline]
388pub fn parse_ber_t61string(i: &[u8]) -> BerResult {
389    parse_ber_with_tag(i, Tag::T61String)
390}
391
392/// Read a Videotex string value
393#[inline]
394pub fn parse_ber_videotexstring(i: &[u8]) -> BerResult {
395    parse_ber_with_tag(i, Tag::VideotexString)
396}
397
398/// Read an IA5 string value. The content is verified to be ASCII.
399#[inline]
400pub fn parse_ber_ia5string(i: &[u8]) -> BerResult {
401    parse_ber_with_tag(i, Tag::Ia5String)
402}
403
404/// Read an UTC time value
405#[inline]
406pub fn parse_ber_utctime(i: &[u8]) -> BerResult {
407    parse_ber_with_tag(i, Tag::UtcTime)
408}
409
410/// Read a Generalized time value
411#[inline]
412pub fn parse_ber_generalizedtime(i: &[u8]) -> BerResult {
413    parse_ber_with_tag(i, Tag::GeneralizedTime)
414}
415
416/// Read an ObjectDescriptor value
417#[inline]
418pub fn parse_ber_objectdescriptor(i: &[u8]) -> BerResult {
419    parse_ber_with_tag(i, Tag::ObjectDescriptor)
420}
421
422/// Read a GraphicString value
423#[inline]
424pub fn parse_ber_graphicstring(i: &[u8]) -> BerResult {
425    parse_ber_with_tag(i, Tag::GraphicString)
426}
427
428/// Read a GeneralString value
429#[inline]
430pub fn parse_ber_generalstring(i: &[u8]) -> BerResult {
431    parse_ber_with_tag(i, Tag::GeneralString)
432}
433
434/// Read a BmpString value
435#[inline]
436pub fn parse_ber_bmpstring(i: &[u8]) -> BerResult {
437    parse_ber_with_tag(i, Tag::BmpString)
438}
439
440/// Read a UniversalString value
441#[inline]
442pub fn parse_ber_universalstring(i: &[u8]) -> BerResult {
443    parse_ber_with_tag(i, Tag::UniversalString)
444}
445
446/// Parse an optional tagged object, applying function to get content
447///
448/// This function returns a `BerObject`, trying to read content as generic BER objects.
449/// If parsing failed, return an optional object containing `None`.
450///
451/// To support other return or error types, use
452/// [parse_ber_tagged_explicit_g](fn.parse_ber_tagged_explicit_g.html)
453///
454/// This function will never fail: if parsing content failed, the BER value `Optional(None)` is
455/// returned.
456pub fn parse_ber_explicit_optional<F>(i: &[u8], tag: Tag, f: F) -> BerResult
457where
458    F: Fn(&[u8]) -> BerResult,
459{
460    parse_ber_optional(parse_ber_tagged_explicit_g(tag, |content, hdr| {
461        let (rem, obj) = f(content)?;
462        let content = BerObjectContent::Tagged(hdr.class(), hdr.tag(), Box::new(obj));
463        let tagged = BerObject::from_header_and_content(hdr, content);
464        Ok((rem, tagged))
465    }))(i)
466}
467
468/// Parse an implicit tagged object, applying function to read content
469///
470/// Note: unlike explicit tagged functions, the callback must be a *content* parsing function,
471/// often based on the [`parse_ber_content`](fn.parse_ber_content.html) combinator.
472///
473/// The built object will use the original header (and tag), so the content may not match the tag
474/// value.
475///
476/// For a combinator version, see [parse_ber_tagged_implicit](fn.parse_ber_tagged_implicit.html).
477///
478/// For a generic version (different output and error types), see
479/// [parse_ber_tagged_implicit_g](fn.parse_ber_tagged_implicit_g.html).
480///
481/// # Examples
482///
483/// The following parses `[3] IMPLICIT INTEGER` into a `BerObject`:
484///
485/// ```rust
486/// # use der_parser::ber::*;
487/// # use der_parser::error::BerResult;
488/// #
489/// fn parse_int_implicit(i:&[u8]) -> BerResult<BerObject> {
490///     parse_ber_implicit(
491///         i,
492///         3,
493///         parse_ber_content(Tag::Integer),
494///     )
495/// }
496///
497/// # let bytes = &[0x83, 0x03, 0x01, 0x00, 0x01];
498/// let res = parse_int_implicit(bytes);
499/// # match res {
500/// #     Ok((rem, content)) => {
501/// #         assert!(rem.is_empty());
502/// #         assert_eq!(content.as_u32(), Ok(0x10001));
503/// #     },
504/// #     _ => assert!(false)
505/// # }
506/// ```
507#[inline]
508pub fn parse_ber_implicit<'a, T, F>(i: &'a [u8], tag: T, f: F) -> BerResult<'a>
509where
510    F: Fn(&'a [u8], &'_ Header, usize) -> BerResult<'a, BerObjectContent<'a>>,
511    T: Into<Tag>,
512{
513    parse_ber_tagged_implicit(tag, f)(i)
514}
515
516/// Combinator for building optional BER values
517///
518/// To read optional BER values, it is to use the nom `opt()` combinator. However, this results in
519/// a `Option<BerObject>` and prevents using some functions from this crate (the generic functions
520/// can still be used).
521///
522/// This combinator is used when parsing BER values, while keeping `BerObject` output only.
523///
524/// This function will never fail: if parsing content failed, the BER value `Optional(None)` is
525/// returned.
526///
527/// ### Example
528///
529/// ```
530/// # use der_parser::ber::*;
531/// #
532/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
533/// let mut parser = parse_ber_optional(parse_ber_integer);
534/// let (_, obj) = parser(bytes).expect("parsing failed");
535///
536/// assert_eq!(obj.header.tag(), Tag::Integer);
537/// assert!(obj.as_optional().is_ok());
538/// ```
539pub fn parse_ber_optional<'a, F>(mut f: F) -> impl FnMut(&'a [u8]) -> BerResult<'a>
540where
541    F: FnMut(&'a [u8]) -> BerResult<'a>,
542{
543    move |i: &[u8]| {
544        let res = f(i);
545        match res {
546            Ok((rem, inner)) => {
547                let opt = BerObject::from_header_and_content(
548                    inner.header.clone(),
549                    BerObjectContent::Optional(Some(Box::new(inner))),
550                );
551                Ok((rem, opt))
552            }
553            Err(_) => Ok((i, BerObject::from_obj(BerObjectContent::Optional(None)))),
554        }
555    }
556}
557
558/// Parse BER object and try to decode it as a 32-bits signed integer
559///
560/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
561/// integer type.
562#[inline]
563pub fn parse_ber_i32(i: &[u8]) -> BerResult<i32> {
564    <i32>::from_ber(i)
565}
566
567/// Parse BER object and try to decode it as a 64-bits signed integer
568///
569/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
570/// integer type.
571#[inline]
572pub fn parse_ber_i64(i: &[u8]) -> BerResult<i64> {
573    <i64>::from_ber(i)
574}
575
576/// Parse BER object and try to decode it as a 32-bits unsigned integer
577///
578/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
579/// integer type.
580#[inline]
581pub fn parse_ber_u32(i: &[u8]) -> BerResult<u32> {
582    <u32>::from_ber(i)
583}
584
585/// Parse BER object and try to decode it as a 64-bits unsigned integer
586///
587/// Return `IntegerTooLarge` if object is an integer, but can not be represented in the target
588/// integer type.
589#[inline]
590pub fn parse_ber_u64(i: &[u8]) -> BerResult<u64> {
591    <u64>::from_ber(i)
592}
593
594/// Parse BER object and get content as slice
595#[inline]
596pub fn parse_ber_slice<T: Into<Tag>>(i: &[u8], tag: T) -> BerResult<&[u8]> {
597    let tag = tag.into();
598    parse_ber_container(move |content, hdr| {
599        hdr.assert_tag(tag)?;
600        Ok((&b""[..], content))
601    })(i)
602}
603
604/// Parse BER object recursively, specifying the maximum recursion depth
605///
606/// Return a tuple containing the remaining (unparsed) bytes and the BER Object, or an error.
607///
608/// ### Example
609///
610/// ```
611/// use der_parser::ber::{parse_ber_recursive, Tag};
612///
613/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
614/// let (_, obj) = parse_ber_recursive(bytes, 1).expect("parsing failed");
615///
616/// assert_eq!(obj.header.tag(), Tag::Integer);
617/// ```
618#[inline]
619pub fn parse_ber_recursive(i: &[u8], max_depth: usize) -> BerResult {
620    parse_ber_any_r(i, max_depth)
621}
622
623/// Parse BER object recursively
624///
625/// Return a tuple containing the remaining (unparsed) bytes and the BER Object, or an error.
626///
627/// *Note*: this is the same as calling `parse_ber_recursive` with `MAX_RECURSION`.
628///
629/// ### Example
630///
631/// ```
632/// use der_parser::ber::{parse_ber, Tag};
633///
634/// let bytes = &[0x02, 0x03, 0x01, 0x00, 0x01];
635/// let (_, obj) = parse_ber(bytes).expect("parsing failed");
636///
637/// assert_eq!(obj.header.tag(), Tag::Integer);
638/// ```
639#[inline]
640pub fn parse_ber(i: &[u8]) -> BerResult {
641    parse_ber_recursive(i, MAX_RECURSION)
642}
643
644#[test]
645fn test_bitstring_to_u64() {
646    // ignored bits modulo 8 to 0
647    let data = &hex_literal::hex!("0d 71 82");
648    let r = bitstring_to_u64(8, &BitStringObject { data });
649    assert_eq!(r, Ok(0x0d71));
650
651    // input too large to fit a 64-bits integer
652    let data = &hex_literal::hex!("0d 71 82 0e 73 72 76 6e 67 6e 62 6c 6e 2d 65 78 30 31");
653    let r = bitstring_to_u64(0, &BitStringObject { data });
654    assert!(r.is_err());
655
656    // test large number but with many ignored bits
657    let data = &hex_literal::hex!("0d 71 82 0e 73 72 76 6e 67 6e 62 6c 6e 2d 65 78 30 31");
658    let r = bitstring_to_u64(130, &BitStringObject { data });
659    // 2 = 130 % 8
660    assert_eq!(r, Ok(0x0d71 >> 2));
661}