der_parser/ber/
parser.rs

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