webpki/subject_name/
ip_address.rs

1// Copyright 2015-2020 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR
10// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15#[cfg(feature = "std")]
16use core::fmt::Write;
17
18use crate::Error;
19
20#[cfg(feature = "alloc")]
21use alloc::string::String;
22
23const VALID_IP_BY_CONSTRUCTION: &str = "IP address is a valid string by construction";
24
25/// Either a IPv4 or IPv6 address, plus its owned string representation
26#[cfg(feature = "alloc")]
27#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
28#[derive(Clone, Debug, Eq, PartialEq, Hash)]
29pub enum IpAddr {
30    /// An IPv4 address and its owned string representation
31    V4(String, [u8; 4]),
32    /// An IPv6 address and its owned string representation
33    V6(String, [u8; 16]),
34}
35
36#[cfg(feature = "alloc")]
37#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
38impl AsRef<str> for IpAddr {
39    fn as_ref(&self) -> &str {
40        match self {
41            IpAddr::V4(ip_address, _) | IpAddr::V6(ip_address, _) => ip_address.as_str(),
42        }
43    }
44}
45
46/// Either a IPv4 or IPv6 address, plus its borrowed string representation
47#[derive(Debug, Clone, Copy, PartialEq, Eq)]
48pub enum IpAddrRef<'a> {
49    /// An IPv4 address and its borrowed string representation
50    V4(&'a [u8], [u8; 4]),
51    /// An IPv6 address and its borrowed string representation
52    V6(&'a [u8], [u8; 16]),
53}
54
55#[cfg(feature = "alloc")]
56#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
57impl<'a> From<IpAddrRef<'a>> for IpAddr {
58    fn from(ip_address: IpAddrRef<'a>) -> IpAddr {
59        match ip_address {
60            IpAddrRef::V4(ip_address, ip_address_octets) => IpAddr::V4(
61                String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION),
62                ip_address_octets,
63            ),
64            IpAddrRef::V6(ip_address, ip_address_octets) => IpAddr::V6(
65                String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION),
66                ip_address_octets,
67            ),
68        }
69    }
70}
71
72#[cfg(feature = "alloc")]
73#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
74impl<'a> From<&'a IpAddr> for IpAddrRef<'a> {
75    fn from(ip_address: &'a IpAddr) -> IpAddrRef<'a> {
76        match ip_address {
77            IpAddr::V4(ip_address, ip_address_octets) => {
78                IpAddrRef::V4(ip_address.as_bytes(), *ip_address_octets)
79            }
80            IpAddr::V6(ip_address, ip_address_octets) => {
81                IpAddrRef::V6(ip_address.as_bytes(), *ip_address_octets)
82            }
83        }
84    }
85}
86
87/// An error indicating that an `IpAddrRef` could not built because
88/// the input could not be parsed as an IP address.
89#[derive(Clone, Copy, Debug, Eq, PartialEq)]
90pub struct AddrParseError;
91
92impl core::fmt::Display for AddrParseError {
93    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
94        write!(f, "{:?}", self)
95    }
96}
97
98#[cfg(feature = "std")]
99#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
100impl ::std::error::Error for AddrParseError {}
101
102impl<'a> IpAddrRef<'a> {
103    /// Constructs an `IpAddrRef` from the given input if the input is
104    /// a valid IPv4 or IPv6 address.
105    pub fn try_from_ascii(ip_address: &'a [u8]) -> Result<Self, AddrParseError> {
106        if let Ok(ip_address) = parse_ipv4_address(ip_address) {
107            Ok(ip_address)
108        } else if let Ok(ip_address) = parse_ipv6_address(ip_address) {
109            Ok(ip_address)
110        } else {
111            Err(AddrParseError)
112        }
113    }
114
115    /// Constructs an `IpAddrRef` from the given input if the input is a
116    /// valid IP address.
117    pub fn try_from_ascii_str(ip_address: &'a str) -> Result<Self, AddrParseError> {
118        Self::try_from_ascii(ip_address.as_bytes())
119    }
120
121    /// Constructs an `IpAddr` from this `IpAddrRef`
122    #[cfg(feature = "alloc")]
123    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
124    pub fn to_owned(&self) -> IpAddr {
125        match self {
126            IpAddrRef::V4(ip_address, ip_address_octets) => IpAddr::V4(
127                String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION),
128                *ip_address_octets,
129            ),
130            IpAddrRef::V6(ip_address, ip_address_octets) => IpAddr::V6(
131                String::from_utf8(ip_address.to_vec()).expect(VALID_IP_BY_CONSTRUCTION),
132                *ip_address_octets,
133            ),
134        }
135    }
136}
137
138#[cfg(feature = "std")]
139fn ipv6_to_uncompressed_string(octets: [u8; 16]) -> String {
140    let mut result = String::with_capacity(39);
141    for i in 0..7 {
142        result
143            .write_fmt(format_args!(
144                "{:02x?}{:02x?}:",
145                octets[i * 2],
146                octets[(i * 2) + 1]
147            ))
148            .expect("unexpected error while formatting IPv6 address");
149    }
150    result
151        .write_fmt(format_args!("{:02x?}{:02x?}", octets[14], octets[15]))
152        .expect("unexpected error while formatting IPv6 address");
153
154    result
155}
156
157#[cfg(feature = "std")]
158#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
159impl From<std::net::IpAddr> for IpAddr {
160    fn from(ip_address: std::net::IpAddr) -> IpAddr {
161        match ip_address {
162            std::net::IpAddr::V4(ip_address) => {
163                IpAddr::V4(ip_address.to_string(), ip_address.octets())
164            }
165            std::net::IpAddr::V6(ip_address) => IpAddr::V6(
166                // We cannot rely on the Display implementation of
167                // std::net::Ipv6Addr given that it might return
168                // compressed IPv6 addresses if the address can be
169                // expressed in such form. However, given we don't
170                // support the IPv6 compressed form, we should not
171                // generate such format either when converting from a
172                // type that supports it.
173                ipv6_to_uncompressed_string(ip_address.octets()),
174                ip_address.octets(),
175            ),
176        }
177    }
178}
179
180impl<'a> From<IpAddrRef<'a>> for &'a str {
181    fn from(ip_address: IpAddrRef<'a>) -> &'a str {
182        match ip_address {
183            IpAddrRef::V4(ip_address, _) | IpAddrRef::V6(ip_address, _) => {
184                core::str::from_utf8(ip_address).expect(VALID_IP_BY_CONSTRUCTION)
185            }
186        }
187    }
188}
189
190impl<'a> From<IpAddrRef<'a>> for &'a [u8] {
191    fn from(ip_address: IpAddrRef<'a>) -> &'a [u8] {
192        match ip_address {
193            IpAddrRef::V4(ip_address, _) | IpAddrRef::V6(ip_address, _) => ip_address,
194        }
195    }
196}
197
198// https://tools.ietf.org/html/rfc5280#section-4.2.1.6 says:
199//   When the subjectAltName extension contains an iPAddress, the address
200//   MUST be stored in the octet string in "network byte order", as
201//   specified in [RFC791].  The least significant bit (LSB) of each octet
202//   is the LSB of the corresponding byte in the network address.  For IP
203//   version 4, as specified in [RFC791], the octet string MUST contain
204//   exactly four octets.  For IP version 6, as specified in
205//   [RFC2460], the octet string MUST contain exactly sixteen octets.
206pub(super) fn presented_id_matches_reference_id(
207    presented_id: untrusted::Input,
208    reference_id: untrusted::Input,
209) -> bool {
210    match (presented_id.len(), reference_id.len()) {
211        (4, 4) => (),
212        (16, 16) => (),
213        _ => {
214            return false;
215        }
216    };
217
218    let mut presented_ip_address = untrusted::Reader::new(presented_id);
219    let mut reference_ip_address = untrusted::Reader::new(reference_id);
220    while !presented_ip_address.at_end() {
221        let presented_ip_address_byte = presented_ip_address.read_byte().unwrap();
222        let reference_ip_address_byte = reference_ip_address.read_byte().unwrap();
223        if presented_ip_address_byte != reference_ip_address_byte {
224            return false;
225        }
226    }
227
228    true
229}
230
231// https://tools.ietf.org/html/rfc5280#section-4.2.1.10 says:
232//
233//     For IPv4 addresses, the iPAddress field of GeneralName MUST contain
234//     eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent
235//     an address range [RFC4632].  For IPv6 addresses, the iPAddress field
236//     MUST contain 32 octets similarly encoded.  For example, a name
237//     constraint for "class C" subnet 192.0.2.0 is represented as the
238//     octets C0 00 02 00 FF FF FF 00, representing the CIDR notation
239//     192.0.2.0/24 (mask 255.255.255.0).
240pub(super) fn presented_id_matches_constraint(
241    name: untrusted::Input,
242    constraint: untrusted::Input,
243) -> Result<bool, Error> {
244    match (name.len(), constraint.len()) {
245        (4, 8) => (),
246        (16, 32) => (),
247
248        // an IPv4 address never matches an IPv6 constraint, and vice versa.
249        (4, 32) | (16, 8) => {
250            return Ok(false);
251        }
252
253        // invalid constraint length
254        (4, _) | (16, _) => {
255            return Err(Error::InvalidNetworkMaskConstraint);
256        }
257
258        // invalid name length, or anything else
259        _ => {
260            return Err(Error::BadDer);
261        }
262    };
263
264    let (constraint_address, constraint_mask) = constraint.read_all(Error::BadDer, |value| {
265        let address = value.read_bytes(constraint.len() / 2).unwrap();
266        let mask = value.read_bytes(constraint.len() / 2).unwrap();
267        Ok((address, mask))
268    })?;
269
270    let mut name = untrusted::Reader::new(name);
271    let mut constraint_address = untrusted::Reader::new(constraint_address);
272    let mut constraint_mask = untrusted::Reader::new(constraint_mask);
273    let mut seen_zero_bit = false;
274
275    loop {
276        // Iterate through the name, constraint address, and constraint mask
277        // a byte at a time.
278        let name_byte = name.read_byte().unwrap();
279        let constraint_address_byte = constraint_address.read_byte().unwrap();
280        let constraint_mask_byte = constraint_mask.read_byte().unwrap();
281
282        // A valid mask consists of a sequence of 1 bits, followed by a
283        // sequence of 0 bits.  Either sequence could be empty.
284
285        let leading = constraint_mask_byte.leading_ones();
286        let trailing = constraint_mask_byte.trailing_zeros();
287
288        // At the resolution of a single octet, a valid mask is one where
289        // leading_ones() and trailing_zeros() sums to 8.
290        // This includes all-ones and all-zeroes.
291        if leading + trailing != 8 {
292            return Err(Error::InvalidNetworkMaskConstraint);
293        }
294
295        // There should be no bits set after the first octet with a zero bit is seen.
296        if seen_zero_bit && constraint_mask_byte != 0x00 {
297            return Err(Error::InvalidNetworkMaskConstraint);
298        }
299
300        // Note when a zero bit is seen for later octets.
301        if constraint_mask_byte != 0xff {
302            seen_zero_bit = true;
303        }
304
305        if ((name_byte ^ constraint_address_byte) & constraint_mask_byte) != 0 {
306            return Ok(false);
307        }
308        if name.at_end() {
309            break;
310        }
311    }
312
313    Ok(true)
314}
315
316pub(crate) fn parse_ipv4_address(ip_address_: &[u8]) -> Result<IpAddrRef, AddrParseError> {
317    let mut ip_address = untrusted::Reader::new(untrusted::Input::from(ip_address_));
318    let mut is_first_byte = true;
319    let mut current_octet: [u8; 3] = [0, 0, 0];
320    let mut current_size = 0;
321    let mut dot_count = 0;
322
323    let mut octet = 0;
324    let mut octets: [u8; 4] = [0, 0, 0, 0];
325
326    // Returns a u32 so it's possible to identify (and error) when
327    // provided textual octets > 255, not representable by u8.
328    fn radix10_to_octet(textual_octets: &[u8]) -> u32 {
329        let mut result: u32 = 0;
330        for digit in textual_octets.iter() {
331            result *= 10;
332            result += u32::from(*digit);
333        }
334        result
335    }
336
337    loop {
338        match ip_address.read_byte() {
339            Ok(b'.') => {
340                if is_first_byte {
341                    // IPv4 address cannot start with a dot.
342                    return Err(AddrParseError);
343                }
344                if ip_address.at_end() {
345                    // IPv4 address cannot end with a dot.
346                    return Err(AddrParseError);
347                }
348                if dot_count == 3 {
349                    // IPv4 address cannot have more than three dots.
350                    return Err(AddrParseError);
351                }
352                dot_count += 1;
353                if current_size == 0 {
354                    // IPv4 address cannot contain two dots in a row.
355                    return Err(AddrParseError);
356                }
357                let current_raw_octet = radix10_to_octet(&current_octet[..current_size]);
358                if current_raw_octet > 255 {
359                    // No octet can be greater than 255.
360                    return Err(AddrParseError);
361                }
362                octets[octet] =
363                    TryInto::<u8>::try_into(current_raw_octet).expect("invalid character");
364                octet += 1;
365                // We move on to the next textual octet.
366                current_octet = [0, 0, 0];
367                current_size = 0;
368            }
369            Ok(number @ b'0'..=b'9') => {
370                if number == b'0'
371                    && current_size == 0
372                    && !ip_address.peek(b'.')
373                    && !ip_address.at_end()
374                {
375                    // No octet can start with 0 if a dot does not follow and if we are not at the end.
376                    return Err(AddrParseError);
377                }
378                if current_size >= current_octet.len() {
379                    // More than 3 octets in a triple
380                    return Err(AddrParseError);
381                }
382                current_octet[current_size] = number - b'0';
383                current_size += 1;
384            }
385            _ => {
386                return Err(AddrParseError);
387            }
388        }
389        is_first_byte = false;
390
391        if ip_address.at_end() {
392            let last_octet = radix10_to_octet(&current_octet[..current_size]);
393            if current_size > 0 && last_octet > 255 {
394                // No octet can be greater than 255.
395                return Err(AddrParseError);
396            }
397            octets[octet] = TryInto::<u8>::try_into(last_octet).expect("invalid character");
398            break;
399        }
400    }
401    if dot_count != 3 {
402        return Err(AddrParseError);
403    }
404    Ok(IpAddrRef::V4(ip_address_, octets))
405}
406
407pub(crate) fn parse_ipv6_address(ip_address_: &[u8]) -> Result<IpAddrRef, AddrParseError> {
408    // Compressed addresses are not supported. Also, IPv4-mapped IPv6
409    // addresses are not supported. This makes 8 groups of 4
410    // hexadecimal characters + 7 colons.
411    if ip_address_.len() != 39 {
412        return Err(AddrParseError);
413    }
414
415    let mut ip_address = untrusted::Reader::new(untrusted::Input::from(ip_address_));
416    let mut is_first_byte = true;
417    let mut current_textual_block_size = 0;
418    let mut colon_count = 0;
419
420    let mut octet = 0;
421    let mut previous_character = None;
422    let mut octets: [u8; 16] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
423
424    loop {
425        match ip_address.read_byte() {
426            Ok(b':') => {
427                if is_first_byte {
428                    // Uncompressed IPv6 address cannot start with a colon.
429                    return Err(AddrParseError);
430                }
431                if ip_address.at_end() {
432                    // Uncompressed IPv6 address cannot end with a colon.
433                    return Err(AddrParseError);
434                }
435                if colon_count == 7 {
436                    // IPv6 address cannot have more than seven colons.
437                    return Err(AddrParseError);
438                }
439                colon_count += 1;
440                if current_textual_block_size == 0 {
441                    // Uncompressed IPv6 address cannot contain two colons in a row.
442                    return Err(AddrParseError);
443                }
444                if current_textual_block_size != 4 {
445                    // Compressed IPv6 addresses are not supported.
446                    return Err(AddrParseError);
447                }
448                // We move on to the next textual block.
449                current_textual_block_size = 0;
450                previous_character = None;
451            }
452            Ok(character @ b'0'..=b'9')
453            | Ok(character @ b'a'..=b'f')
454            | Ok(character @ b'A'..=b'F') => {
455                if current_textual_block_size == 4 {
456                    // Blocks cannot contain more than 4 hexadecimal characters.
457                    return Err(AddrParseError);
458                }
459                if let Some(previous_character_) = previous_character {
460                    octets[octet] = (TryInto::<u8>::try_into(
461                        TryInto::<u8>::try_into(
462                            (TryInto::<char>::try_into(previous_character_)
463                                .expect("invalid character"))
464                            .to_digit(16)
465                            // Safe to unwrap because we know character is within hexadecimal bounds ([0-9a-f])
466                            .unwrap(),
467                        )
468                        .expect("invalid character"),
469                    )
470                    .expect("invalid character")
471                        << 4)
472                        | (TryInto::<u8>::try_into(
473                            TryInto::<char>::try_into(character)
474                                .expect("invalid character")
475                                .to_digit(16)
476                                // Safe to unwrap because we know character is within hexadecimal bounds ([0-9a-f])
477                                .unwrap(),
478                        )
479                        .expect("invalid character"));
480                    previous_character = None;
481                    octet += 1;
482                } else {
483                    previous_character = Some(character);
484                }
485                current_textual_block_size += 1;
486            }
487            _ => {
488                return Err(AddrParseError);
489            }
490        }
491        is_first_byte = false;
492
493        if ip_address.at_end() {
494            break;
495        }
496    }
497    if colon_count != 7 {
498        return Err(AddrParseError);
499    }
500    Ok(IpAddrRef::V6(ip_address_, octets))
501}
502
503#[cfg(test)]
504mod tests {
505    use super::*;
506
507    const fn ipv4_address(
508        ip_address: &[u8],
509        octets: [u8; 4],
510    ) -> (&[u8], Result<IpAddrRef, AddrParseError>) {
511        (ip_address, Ok(IpAddrRef::V4(ip_address, octets)))
512    }
513
514    const IPV4_ADDRESSES: &[(&[u8], Result<IpAddrRef, AddrParseError>)] = &[
515        // Valid IPv4 addresses
516        ipv4_address(b"0.0.0.0", [0, 0, 0, 0]),
517        ipv4_address(b"1.1.1.1", [1, 1, 1, 1]),
518        ipv4_address(b"205.0.0.0", [205, 0, 0, 0]),
519        ipv4_address(b"0.205.0.0", [0, 205, 0, 0]),
520        ipv4_address(b"0.0.205.0", [0, 0, 205, 0]),
521        ipv4_address(b"0.0.0.205", [0, 0, 0, 205]),
522        ipv4_address(b"0.0.0.20", [0, 0, 0, 20]),
523        // Invalid IPv4 addresses
524        (b"", Err(AddrParseError)),
525        (b"...", Err(AddrParseError)),
526        (b".0.0.0.0", Err(AddrParseError)),
527        (b"0.0.0.0.", Err(AddrParseError)),
528        (b"0.0.0", Err(AddrParseError)),
529        (b"0.0.0.", Err(AddrParseError)),
530        (b"256.0.0.0", Err(AddrParseError)),
531        (b"0.256.0.0", Err(AddrParseError)),
532        (b"0.0.256.0", Err(AddrParseError)),
533        (b"0.0.0.256", Err(AddrParseError)),
534        (b"1..1.1.1", Err(AddrParseError)),
535        (b"1.1..1.1", Err(AddrParseError)),
536        (b"1.1.1..1", Err(AddrParseError)),
537        (b"025.0.0.0", Err(AddrParseError)),
538        (b"0.025.0.0", Err(AddrParseError)),
539        (b"0.0.025.0", Err(AddrParseError)),
540        (b"0.0.0.025", Err(AddrParseError)),
541        (b"1234.0.0.0", Err(AddrParseError)),
542        (b"0.1234.0.0", Err(AddrParseError)),
543        (b"0.0.1234.0", Err(AddrParseError)),
544        (b"0.0.0.1234", Err(AddrParseError)),
545    ];
546
547    #[test]
548    fn parse_ipv4_address_test() {
549        for &(ip_address, expected_result) in IPV4_ADDRESSES {
550            assert_eq!(parse_ipv4_address(ip_address), expected_result,);
551        }
552    }
553
554    const fn ipv6_address(
555        ip_address: &[u8],
556        octets: [u8; 16],
557    ) -> (&[u8], Result<IpAddrRef, AddrParseError>) {
558        (ip_address, Ok(IpAddrRef::V6(ip_address, octets)))
559    }
560
561    const IPV6_ADDRESSES: &[(&[u8], Result<IpAddrRef, AddrParseError>)] = &[
562        // Valid IPv6 addresses
563        ipv6_address(
564            b"2a05:d018:076c:b685:e8ab:afd3:af51:3aed",
565            [
566                0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
567                0x3a, 0xed,
568            ],
569        ),
570        ipv6_address(
571            b"2A05:D018:076C:B685:E8AB:AFD3:AF51:3AED",
572            [
573                0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x85, 0xe8, 0xab, 0xaf, 0xd3, 0xaf, 0x51,
574                0x3a, 0xed,
575            ],
576        ),
577        ipv6_address(
578            b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
579            [
580                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
581                0xff, 0xff,
582            ],
583        ),
584        ipv6_address(
585            b"FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF",
586            [
587                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
588                0xff, 0xff,
589            ],
590        ),
591        ipv6_address(
592            b"FFFF:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
593            [
594                0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
595                0xff, 0xff,
596            ],
597        ),
598        // Invalid IPv6 addresses
599        // Missing octets on uncompressed addresses. The unmatching letter has the violation
600        (
601            b"aaa:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
602            Err(AddrParseError),
603        ),
604        (
605            b"ffff:aaa:ffff:ffff:ffff:ffff:ffff:ffff",
606            Err(AddrParseError),
607        ),
608        (
609            b"ffff:ffff:aaa:ffff:ffff:ffff:ffff:ffff",
610            Err(AddrParseError),
611        ),
612        (
613            b"ffff:ffff:ffff:aaa:ffff:ffff:ffff:ffff",
614            Err(AddrParseError),
615        ),
616        (
617            b"ffff:ffff:ffff:ffff:aaa:ffff:ffff:ffff",
618            Err(AddrParseError),
619        ),
620        (
621            b"ffff:ffff:ffff:ffff:ffff:aaa:ffff:ffff",
622            Err(AddrParseError),
623        ),
624        (
625            b"ffff:ffff:ffff:ffff:ffff:ffff:aaa:ffff",
626            Err(AddrParseError),
627        ),
628        (
629            b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:aaa",
630            Err(AddrParseError),
631        ),
632        // Wrong hexadecimal characters on different positions
633        (
634            b"ffgf:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
635            Err(AddrParseError),
636        ),
637        (
638            b"ffff:gfff:ffff:ffff:ffff:ffff:ffff:ffff",
639            Err(AddrParseError),
640        ),
641        (
642            b"ffff:ffff:fffg:ffff:ffff:ffff:ffff:ffff",
643            Err(AddrParseError),
644        ),
645        (
646            b"ffff:ffff:ffff:ffgf:ffff:ffff:ffff:ffff",
647            Err(AddrParseError),
648        ),
649        (
650            b"ffff:ffff:ffff:ffff:gfff:ffff:ffff:ffff",
651            Err(AddrParseError),
652        ),
653        (
654            b"ffff:ffff:ffff:ffff:ffff:fgff:ffff:ffff",
655            Err(AddrParseError),
656        ),
657        (
658            b"ffff:ffff:ffff:ffff:ffff:ffff:ffgf:ffff",
659            Err(AddrParseError),
660        ),
661        (
662            b"ffff:ffff:ffff:ffff:ffff:ffff:ffgf:fffg",
663            Err(AddrParseError),
664        ),
665        // Wrong colons on uncompressed addresses
666        (
667            b":ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
668            Err(AddrParseError),
669        ),
670        (
671            b"ffff::ffff:ffff:ffff:ffff:ffff:ffff:ffff",
672            Err(AddrParseError),
673        ),
674        (
675            b"ffff:ffff::ffff:ffff:ffff:ffff:ffff:ffff",
676            Err(AddrParseError),
677        ),
678        (
679            b"ffff:ffff:ffff::ffff:ffff:ffff:ffff:ffff",
680            Err(AddrParseError),
681        ),
682        (
683            b"ffff:ffff:ffff:ffff::ffff:ffff:ffff:ffff",
684            Err(AddrParseError),
685        ),
686        (
687            b"ffff:ffff:ffff:ffff:ffff::ffff:ffff:ffff",
688            Err(AddrParseError),
689        ),
690        (
691            b"ffff:ffff:ffff:ffff:ffff:ffff::ffff:ffff",
692            Err(AddrParseError),
693        ),
694        (
695            b"ffff:ffff:ffff:ffff:ffff:ffff:ffff::ffff",
696            Err(AddrParseError),
697        ),
698        // More colons than allowed
699        (
700            b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:",
701            Err(AddrParseError),
702        ),
703        (
704            b"ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff",
705            Err(AddrParseError),
706        ),
707        // v Invalid UTF-8 encoding
708        (
709            b"\xc3\x28a05:d018:076c:b685:e8ab:afd3:af51:3aed",
710            Err(AddrParseError),
711        ),
712        // v Invalid hexadecimal
713        (
714            b"ga05:d018:076c:b685:e8ab:afd3:af51:3aed",
715            Err(AddrParseError),
716        ),
717        // Cannot start with colon
718        (
719            b":a05:d018:076c:b685:e8ab:afd3:af51:3aed",
720            Err(AddrParseError),
721        ),
722        // Cannot end with colon
723        (
724            b"2a05:d018:076c:b685:e8ab:afd3:af51:3ae:",
725            Err(AddrParseError),
726        ),
727        // Cannot have more than seven colons
728        (
729            b"2a05:d018:076c:b685:e8ab:afd3:af51:3a::",
730            Err(AddrParseError),
731        ),
732        // Cannot contain two colons in a row
733        (
734            b"2a05::018:076c:b685:e8ab:afd3:af51:3aed",
735            Err(AddrParseError),
736        ),
737        // v Textual block size is longer
738        (
739            b"2a056:d018:076c:b685:e8ab:afd3:af51:3ae",
740            Err(AddrParseError),
741        ),
742        // v Textual block size is shorter
743        (
744            b"2a0:d018:076c:b685:e8ab:afd3:af51:3aed ",
745            Err(AddrParseError),
746        ),
747        // Shorter IPv6 address
748        (b"d018:076c:b685:e8ab:afd3:af51:3aed", Err(AddrParseError)),
749        // Longer IPv6 address
750        (
751            b"2a05:d018:076c:b685:e8ab:afd3:af51:3aed3aed",
752            Err(AddrParseError),
753        ),
754        // These are valid IPv6 addresses, but we don't support compressed addresses
755        (b"0:0:0:0:0:0:0:1", Err(AddrParseError)),
756        (
757            b"2a05:d018:76c:b685:e8ab:afd3:af51:3aed",
758            Err(AddrParseError),
759        ),
760    ];
761
762    #[test]
763    fn parse_ipv6_address_test() {
764        for &(ip_address, expected_result) in IPV6_ADDRESSES {
765            assert_eq!(parse_ipv6_address(ip_address), expected_result,);
766        }
767    }
768
769    #[test]
770    fn try_from_ascii_ip_address_test() {
771        const IP_ADDRESSES: &[(&[u8], Result<IpAddrRef, AddrParseError>)] = &[
772            // Valid IPv4 addresses
773            (
774                b"127.0.0.1",
775                Ok(IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1])),
776            ),
777            // Invalid IPv4 addresses
778            (
779                // Ends with a dot; misses one octet
780                b"127.0.0.",
781                Err(AddrParseError),
782            ),
783            // Valid IPv6 addresses
784            (
785                b"0000:0000:0000:0000:0000:0000:0000:0001",
786                Ok(IpAddrRef::V6(
787                    b"0000:0000:0000:0000:0000:0000:0000:0001",
788                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
789                )),
790            ),
791            // Invalid IPv6 addresses
792            (
793                // IPv6 addresses in compressed form are not supported
794                b"0:0:0:0:0:0:0:1",
795                Err(AddrParseError),
796            ),
797            // Something else
798            (
799                // A hostname
800                b"example.com",
801                Err(AddrParseError),
802            ),
803        ];
804        for &(ip_address, expected_result) in IP_ADDRESSES {
805            assert_eq!(IpAddrRef::try_from_ascii(ip_address), expected_result)
806        }
807    }
808
809    #[test]
810    fn try_from_ascii_str_ip_address_test() {
811        const IP_ADDRESSES: &[(&str, Result<IpAddrRef, AddrParseError>)] = &[
812            // Valid IPv4 addresses
813            ("127.0.0.1", Ok(IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]))),
814            // Invalid IPv4 addresses
815            (
816                // Ends with a dot; misses one octet
817                "127.0.0.",
818                Err(AddrParseError),
819            ),
820            // Valid IPv6 addresses
821            (
822                "0000:0000:0000:0000:0000:0000:0000:0001",
823                Ok(IpAddrRef::V6(
824                    b"0000:0000:0000:0000:0000:0000:0000:0001",
825                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
826                )),
827            ),
828            // Invalid IPv6 addresses
829            (
830                // IPv6 addresses in compressed form are not supported
831                "0:0:0:0:0:0:0:1",
832                Err(AddrParseError),
833            ),
834            // Something else
835            (
836                // A hostname
837                "example.com",
838                Err(AddrParseError),
839            ),
840        ];
841        for &(ip_address, expected_result) in IP_ADDRESSES {
842            assert_eq!(IpAddrRef::try_from_ascii_str(ip_address), expected_result)
843        }
844    }
845
846    #[test]
847    fn str_from_ip_address_ref_test() {
848        let ip_addresses = vec![
849            // IPv4 addresses
850            (IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]), "127.0.0.1"),
851            // IPv6 addresses
852            (
853                IpAddrRef::V6(
854                    b"0000:0000:0000:0000:0000:0000:0000:0001",
855                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
856                ),
857                "0000:0000:0000:0000:0000:0000:0000:0001",
858            ),
859        ];
860        for (ip_address, expected_ip_address) in ip_addresses {
861            assert_eq!(Into::<&str>::into(ip_address), expected_ip_address,)
862        }
863    }
864
865    #[test]
866    fn u8_array_from_ip_address_ref_test() {
867        let ip_addresses = vec![
868            // IPv4 addresses
869            (IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]), "127.0.0.1"),
870            // IPv6 addresses
871            (
872                IpAddrRef::V6(
873                    b"0000:0000:0000:0000:0000:0000:0000:0001",
874                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
875                ),
876                "0000:0000:0000:0000:0000:0000:0000:0001",
877            ),
878        ];
879        for (ip_address, expected_ip_address) in ip_addresses {
880            assert_eq!(
881                Into::<&[u8]>::into(ip_address),
882                expected_ip_address.as_bytes()
883            )
884        }
885    }
886
887    #[test]
888    fn presented_id_matches_constraint_ipv4_test() {
889        let names_and_constraints = vec![
890            (
891                // 192.0.2.0 matches constraint 192.0.2.0/24
892                [0xC0, 0x00, 0x02, 0x00],
893                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
894                Ok(true),
895            ),
896            (
897                // 192.0.2.1 matches constraint 192.0.2.0/24
898                [0xC0, 0x00, 0x02, 0x01],
899                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
900                Ok(true),
901            ),
902            (
903                // 192.0.2.255 matches constraint 192.0.2.0/24
904                [0xC0, 0x00, 0x02, 0xFF],
905                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
906                Ok(true),
907            ),
908            (
909                // 192.0.1.255 does not match constraint 192.0.2.0/24
910                [0xC0, 0x00, 0x01, 0xFF],
911                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
912                Ok(false),
913            ),
914            (
915                // 192.0.3.0 does not match constraint 192.0.2.0/24
916                [0xC0, 0x00, 0x03, 0x00],
917                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
918                Ok(false),
919            ),
920        ];
921        for (name, constraint, match_result) in names_and_constraints {
922            assert_eq!(
923                presented_id_matches_constraint(
924                    untrusted::Input::from(&name),
925                    untrusted::Input::from(&constraint),
926                ),
927                match_result
928            )
929        }
930
931        // Invalid name length (shorter)
932        assert_eq!(
933            presented_id_matches_constraint(
934                untrusted::Input::from(&[0xC0, 0x00, 0x02]),
935                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
936            ),
937            Err(Error::BadDer),
938        );
939
940        // Invalid name length (longer)
941        assert_eq!(
942            presented_id_matches_constraint(
943                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0x00]),
944                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
945            ),
946            Err(Error::BadDer),
947        );
948
949        // Unmatching constraint size (shorter)
950        assert_eq!(
951            presented_id_matches_constraint(
952                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
953                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF]),
954            ),
955            Err(Error::InvalidNetworkMaskConstraint),
956        );
957
958        // Unmatching constraint size (longer)
959        assert_eq!(
960            presented_id_matches_constraint(
961                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
962                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00]),
963            ),
964            Err(Error::InvalidNetworkMaskConstraint),
965        );
966
967        // Unmatching constraint size (IPv6 constraint for IPv4 address)
968        assert_eq!(
969            presented_id_matches_constraint(
970                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
971                untrusted::Input::from(&[
972                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
973                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
974                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
975                ]),
976            ),
977            Ok(false),
978        );
979    }
980
981    #[test]
982    fn presented_id_matches_constraint_ipv6_test() {
983        let names_and_constraints = vec![
984            (
985                // 2001:0DB8:ABCD:0012:0000:0000:0000:0000 matches constraint
986                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
987                [
988                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
989                    0x00, 0x00, 0x00,
990                ],
991                [
992                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
993                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
994                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
995                ],
996                Ok(true),
997            ),
998            (
999                // 2001:0DB8:ABCD:0012:0000:0000:0000:0001 matches constraint
1000                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
1001                [
1002                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1003                    0x00, 0x00, 0x01,
1004                ],
1005                [
1006                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1007                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1008                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1009                ],
1010                Ok(true),
1011            ),
1012            (
1013                // 2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF matches constraint
1014                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
1015                [
1016                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
1017                    0xFF, 0xFF, 0xFF,
1018                ],
1019                [
1020                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1021                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1022                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1023                ],
1024                Ok(true),
1025            ),
1026            (
1027                // 2001:0DB8:ABCD:0011:0000:0000:0000:0000 does not match constraint
1028                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
1029                [
1030                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
1031                    0x00, 0x00, 0x00,
1032                ],
1033                [
1034                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1035                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1036                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1037                ],
1038                Ok(false),
1039            ),
1040            (
1041                // 2001:0DB8:ABCD:0013:0000:0000:0000:0000 does not match constraint
1042                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
1043                [
1044                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
1045                    0x00, 0x00, 0x00,
1046                ],
1047                [
1048                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1049                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1050                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1051                ],
1052                Ok(false),
1053            ),
1054        ];
1055        for (name, constraint, match_result) in names_and_constraints {
1056            assert_eq!(
1057                presented_id_matches_constraint(
1058                    untrusted::Input::from(&name),
1059                    untrusted::Input::from(&constraint),
1060                ),
1061                match_result
1062            )
1063        }
1064
1065        // Invalid name length (shorter)
1066        assert_eq!(
1067            presented_id_matches_constraint(
1068                untrusted::Input::from(&[
1069                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1070                    0x00, 0x00
1071                ]),
1072                untrusted::Input::from(&[
1073                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1074                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1075                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1076                ]),
1077            ),
1078            Err(Error::BadDer),
1079        );
1080
1081        // Invalid name length (longer)
1082        assert_eq!(
1083            presented_id_matches_constraint(
1084                untrusted::Input::from(&[
1085                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1086                    0x00, 0x00, 0x00, 0x00
1087                ]),
1088                untrusted::Input::from(&[
1089                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1090                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1091                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1092                ]),
1093            ),
1094            Err(Error::BadDer),
1095        );
1096
1097        // Unmatching constraint size (shorter)
1098        assert_eq!(
1099            presented_id_matches_constraint(
1100                untrusted::Input::from(&[
1101                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1102                    0x00, 0x00, 0x00,
1103                ]),
1104                untrusted::Input::from(&[
1105                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1106                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1107                    0x00, 0x00, 0x00, 0x00, 0x00
1108                ]),
1109            ),
1110            Err(Error::InvalidNetworkMaskConstraint),
1111        );
1112
1113        // Unmatching constraint size (longer)
1114        assert_eq!(
1115            presented_id_matches_constraint(
1116                untrusted::Input::from(&[
1117                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1118                    0x00, 0x00, 0x00,
1119                ]),
1120                untrusted::Input::from(&[
1121                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1122                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
1123                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
1124                ]),
1125            ),
1126            Err(Error::InvalidNetworkMaskConstraint),
1127        );
1128
1129        // Unmatching constraint size (IPv4 constraint for IPv6 address)
1130        assert_eq!(
1131            presented_id_matches_constraint(
1132                untrusted::Input::from(&[
1133                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
1134                    0x00, 0x00, 0x00,
1135                ]),
1136                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
1137            ),
1138            Ok(false),
1139        );
1140    }
1141
1142    #[test]
1143    fn test_presented_id_matches_reference_id() {
1144        assert!(!presented_id_matches_reference_id(
1145            untrusted::Input::from(&[]),
1146            untrusted::Input::from(&[]),
1147        ));
1148
1149        assert!(!presented_id_matches_reference_id(
1150            untrusted::Input::from(&[0x01]),
1151            untrusted::Input::from(&[])
1152        ));
1153
1154        assert!(!presented_id_matches_reference_id(
1155            untrusted::Input::from(&[]),
1156            untrusted::Input::from(&[0x01])
1157        ));
1158
1159        assert!(presented_id_matches_reference_id(
1160            untrusted::Input::from(&[1, 2, 3, 4]),
1161            untrusted::Input::from(&[1, 2, 3, 4])
1162        ));
1163
1164        assert!(!presented_id_matches_reference_id(
1165            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
1166            untrusted::Input::from(&[1, 2, 3, 4])
1167        ));
1168
1169        assert!(!presented_id_matches_reference_id(
1170            untrusted::Input::from(&[1, 2, 3, 4]),
1171            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
1172        ));
1173
1174        assert!(presented_id_matches_reference_id(
1175            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
1176            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
1177        ));
1178    }
1179
1180    #[test]
1181    fn presented_id_matches_constraint_rejects_incorrect_length_arguments() {
1182        // wrong length names
1183        assert_eq!(
1184            presented_id_matches_constraint(
1185                untrusted::Input::from(b"\x00\x00\x00"),
1186                untrusted::Input::from(b"")
1187            ),
1188            Err(Error::BadDer)
1189        );
1190        assert_eq!(
1191            presented_id_matches_constraint(
1192                untrusted::Input::from(b"\x00\x00\x00\x00\x00"),
1193                untrusted::Input::from(b"")
1194            ),
1195            Err(Error::BadDer)
1196        );
1197
1198        assert_eq!(
1199            presented_id_matches_constraint(
1200                untrusted::Input::from(
1201                    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1202                ),
1203                untrusted::Input::from(b"")
1204            ),
1205            Err(Error::BadDer)
1206        );
1207        assert_eq!(
1208            presented_id_matches_constraint(
1209                untrusted::Input::from(
1210                    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1211                ),
1212                untrusted::Input::from(b"")
1213            ),
1214            Err(Error::BadDer)
1215        );
1216
1217        // wrong length constraints
1218        assert_eq!(
1219            presented_id_matches_constraint(
1220                untrusted::Input::from(b"\x00\x00\x00\x00"),
1221                untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff")
1222            ),
1223            Err(Error::InvalidNetworkMaskConstraint)
1224        );
1225        assert_eq!(
1226            presented_id_matches_constraint(
1227                untrusted::Input::from(b"\x00\x00\x00\x00"),
1228                untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff\xff\x00")
1229            ),
1230            Err(Error::InvalidNetworkMaskConstraint)
1231        );
1232        assert_eq!(
1233            presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
1234                                            untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1235                                                                     \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
1236            Err(Error::InvalidNetworkMaskConstraint)
1237        );
1238        assert_eq!(
1239            presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
1240                                            untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1241                                                                     \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
1242            Err(Error::InvalidNetworkMaskConstraint)
1243        );
1244
1245        // ipv4-length not considered for ipv6-length name, and vv
1246        assert_eq!(
1247            presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00"),
1248                                            untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
1249                                                                     \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
1250            Ok(false)
1251        );
1252        assert_eq!(
1253            presented_id_matches_constraint(
1254                untrusted::Input::from(
1255                    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
1256                ),
1257                untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff\xff")
1258            ),
1259            Ok(false)
1260        );
1261    }
1262}
1263
1264#[cfg(all(test, feature = "alloc"))]
1265mod alloc_tests {
1266    use super::*;
1267
1268    #[test]
1269    fn as_ref_ip_address_test() {
1270        assert_eq!(
1271            IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]).as_ref(),
1272            "127.0.0.1",
1273        );
1274        assert_eq!(
1275            IpAddr::V6(
1276                String::from("0000:0000:0000:0000:0000:0000:0000:0001"),
1277                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
1278            )
1279            .as_ref(),
1280            "0000:0000:0000:0000:0000:0000:0000:0001",
1281        );
1282    }
1283
1284    #[test]
1285    fn from_ip_address_ref_for_ip_address_test() {
1286        {
1287            let (ip_address, ip_address_octets) = ("127.0.0.1", [127, 0, 0, 1]);
1288            assert_eq!(
1289                IpAddr::from(IpAddrRef::V4(ip_address.as_bytes(), ip_address_octets)),
1290                IpAddr::V4(String::from(ip_address), ip_address_octets),
1291            )
1292        }
1293        {
1294            let (ip_address, ip_address_octets) = (
1295                "0000:0000:0000:0000:0000:0000:0000:0001",
1296                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1297            );
1298            assert_eq!(
1299                IpAddr::from(IpAddrRef::V6(ip_address.as_bytes(), ip_address_octets)),
1300                IpAddr::V6(String::from(ip_address), ip_address_octets),
1301            )
1302        }
1303    }
1304
1305    #[test]
1306    fn from_ip_address_for_ip_address_ref_test() {
1307        {
1308            let ip_address = IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]);
1309            assert_eq!(
1310                IpAddrRef::from(&ip_address),
1311                IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]),
1312            )
1313        }
1314        {
1315            let ip_address = IpAddr::V6(
1316                String::from("0000:0000:0000:0000:0000:0000:0000:0001"),
1317                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1318            );
1319            assert_eq!(
1320                IpAddrRef::from(&ip_address),
1321                IpAddrRef::V6(
1322                    b"0000:0000:0000:0000:0000:0000:0000:0001",
1323                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
1324                ),
1325            )
1326        }
1327    }
1328
1329    #[test]
1330    fn display_invalid_ip_address_error_test() {
1331        assert_eq!(AddrParseError.to_string(), String::from("AddrParseError"),)
1332    }
1333
1334    #[test]
1335    fn ip_address_ref_to_owned_test() {
1336        {
1337            assert_eq!(
1338                IpAddrRef::V4(b"127.0.0.1", [127, 0, 0, 1]).to_owned(),
1339                IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]),
1340            )
1341        }
1342        {
1343            assert_eq!(
1344                IpAddrRef::V6(
1345                    b"0000:0000:0000:0000:0000:0000:0000:0001",
1346                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1347                )
1348                .to_owned(),
1349                IpAddr::V6(
1350                    String::from("0000:0000:0000:0000:0000:0000:0000:0001"),
1351                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1352                ),
1353            )
1354        }
1355    }
1356
1357    #[test]
1358    fn ip_address_from_std_net_ipaddr_test() {
1359        let ip_addresses = vec![
1360            (
1361                std::net::IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)),
1362                IpAddr::V4(String::from("127.0.0.1"), [127, 0, 0, 1]),
1363            ),
1364            (
1365                std::net::IpAddr::V6(std::net::Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1)),
1366                IpAddr::V6(
1367                    String::from("0000:0000:0000:0000:0000:0000:0000:0001"),
1368                    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1369                ),
1370            ),
1371        ];
1372        for (ip_address, expected_ip_address) in ip_addresses {
1373            assert_eq!(IpAddr::from(ip_address), expected_ip_address,)
1374        }
1375    }
1376
1377    #[test]
1378    fn ipv6_to_uncompressed_string_test() {
1379        let ip_addresses = vec![
1380            (
1381                [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1],
1382                String::from("0000:0000:0000:0000:0000:0000:0000:0001"),
1383            ),
1384            (
1385                [
1386                    0x2a, 0x05, 0xd0, 0x18, 0x07, 0x6c, 0xb6, 0x84, 0x8e, 0x48, 0x47, 0xc9, 0x84,
1387                    0xaa, 0xb3, 0x4d,
1388                ],
1389                String::from("2a05:d018:076c:b684:8e48:47c9:84aa:b34d"),
1390            ),
1391        ];
1392        for (ip_address_octets, expected_result) in ip_addresses {
1393            assert_eq!(
1394                ipv6_to_uncompressed_string(ip_address_octets),
1395                expected_result,
1396            )
1397        }
1398    }
1399
1400    // (presented_address, constraint_address, constraint_mask, expected_result)
1401    const PRESENTED_MATCHES_CONSTRAINT: &[(&str, &str, &str, Result<bool, Error>)] = &[
1402        // Cannot mix IpV4 with IpV6 and viceversa
1403        ("2001:db8::", "8.8.8.8", "255.255.255.255", Ok(false)),
1404        ("8.8.8.8", "2001:db8::", "ffff::", Ok(false)),
1405        // IpV4 non-contiguous masks
1406        (
1407            "8.8.8.8",
1408            "8.8.8.8",
1409            "255.255.255.1",
1410            Err(Error::InvalidNetworkMaskConstraint),
1411        ),
1412        (
1413            "8.8.8.8",
1414            "8.8.8.8",
1415            "255.255.0.255",
1416            Err(Error::InvalidNetworkMaskConstraint),
1417        ),
1418        (
1419            "8.8.8.8",
1420            "8.8.8.8",
1421            "255.0.255.255",
1422            Err(Error::InvalidNetworkMaskConstraint),
1423        ),
1424        (
1425            "8.8.8.8",
1426            "8.8.8.8",
1427            "0.255.255.255",
1428            Err(Error::InvalidNetworkMaskConstraint),
1429        ),
1430        (
1431            "8.8.8.8",
1432            "8.8.8.8",
1433            "1.255.255.255",
1434            Err(Error::InvalidNetworkMaskConstraint),
1435        ),
1436        (
1437            "8.8.8.8",
1438            "8.8.8.8",
1439            "128.128.128.128",
1440            Err(Error::InvalidNetworkMaskConstraint),
1441        ),
1442        // IpV4
1443        ("8.8.8.8", "8.8.8.8", "255.255.255.255", Ok(true)),
1444        ("8.8.8.9", "8.8.8.8", "255.255.255.255", Ok(false)),
1445        ("8.8.8.9", "8.8.8.8", "255.255.255.254", Ok(true)),
1446        ("8.8.8.10", "8.8.8.8", "255.255.255.254", Ok(false)),
1447        ("8.8.8.10", "8.8.8.8", "255.255.255.0", Ok(true)),
1448        ("8.8.15.10", "8.8.8.8", "255.255.248.0", Ok(true)),
1449        ("8.8.16.10", "8.8.8.8", "255.255.248.0", Ok(false)),
1450        ("8.8.16.10", "8.8.8.8", "255.255.0.0", Ok(true)),
1451        ("8.31.16.10", "8.8.8.8", "255.224.0.0", Ok(true)),
1452        ("8.32.16.10", "8.8.8.8", "255.224.0.0", Ok(false)),
1453        ("8.32.16.10", "8.8.8.8", "255.0.0.0", Ok(true)),
1454        ("63.32.16.10", "8.8.8.8", "192.0.0.0", Ok(true)),
1455        ("64.32.16.10", "8.8.8.8", "192.0.0.0", Ok(false)),
1456        ("64.32.16.10", "8.8.8.8", "0.0.0.0", Ok(true)),
1457        // IpV6 non-contiguous masks
1458        (
1459            "2001:db8::",
1460            "2001:db8::",
1461            "fffe:ffff::",
1462            Err(Error::InvalidNetworkMaskConstraint),
1463        ),
1464        (
1465            "2001:db8::",
1466            "2001:db8::",
1467            "ffff:fdff::",
1468            Err(Error::InvalidNetworkMaskConstraint),
1469        ),
1470        (
1471            "2001:db8::",
1472            "2001:db8::",
1473            "ffff:feff::",
1474            Err(Error::InvalidNetworkMaskConstraint),
1475        ),
1476        (
1477            "2001:db8::",
1478            "2001:db8::",
1479            "ffff:fcff::",
1480            Err(Error::InvalidNetworkMaskConstraint),
1481        ),
1482        (
1483            "2001:db8::",
1484            "2001:db8::",
1485            "7fff:ffff::",
1486            Err(Error::InvalidNetworkMaskConstraint),
1487        ),
1488        // IpV6
1489        ("2001:db8::", "2001:db8::", "ffff:ffff::", Ok(true)),
1490        ("2001:db9::", "2001:db8::", "ffff:ffff::", Ok(false)),
1491        ("2001:db9::", "2001:db8::", "ffff:fffe::", Ok(true)),
1492        ("2001:dba::", "2001:db8::", "ffff:fffe::", Ok(false)),
1493        ("2001:dba::", "2001:db8::", "ffff:ff00::", Ok(true)),
1494        ("2001:dca::", "2001:db8::", "ffff:fe00::", Ok(true)),
1495        ("2001:fca::", "2001:db8::", "ffff:fe00::", Ok(false)),
1496        ("2001:fca::", "2001:db8::", "ffff:0000::", Ok(true)),
1497        ("2000:fca::", "2001:db8::", "fffe:0000::", Ok(true)),
1498        ("2003:fca::", "2001:db8::", "fffe:0000::", Ok(false)),
1499        ("2003:fca::", "2001:db8::", "ff00:0000::", Ok(true)),
1500        ("1003:fca::", "2001:db8::", "e000:0000::", Ok(false)),
1501        ("1003:fca::", "2001:db8::", "0000:0000::", Ok(true)),
1502    ];
1503
1504    #[cfg(feature = "std")]
1505    #[test]
1506    fn presented_matches_constraint_test() {
1507        use std::boxed::Box;
1508        use std::net::IpAddr;
1509
1510        for &(presented, constraint_address, constraint_mask, expected_result) in
1511            PRESENTED_MATCHES_CONSTRAINT
1512        {
1513            let presented_bytes: Box<[u8]> = match presented.parse::<IpAddr>().unwrap() {
1514                IpAddr::V4(p) => Box::new(p.octets()),
1515                IpAddr::V6(p) => Box::new(p.octets()),
1516            };
1517            let ca_bytes: Box<[u8]> = match constraint_address.parse::<IpAddr>().unwrap() {
1518                IpAddr::V4(ca) => Box::new(ca.octets()),
1519                IpAddr::V6(ca) => Box::new(ca.octets()),
1520            };
1521            let cm_bytes: Box<[u8]> = match constraint_mask.parse::<IpAddr>().unwrap() {
1522                IpAddr::V4(cm) => Box::new(cm.octets()),
1523                IpAddr::V6(cm) => Box::new(cm.octets()),
1524            };
1525            let constraint_bytes = [ca_bytes, cm_bytes].concat();
1526            let actual_result = presented_id_matches_constraint(
1527                untrusted::Input::from(&presented_bytes),
1528                untrusted::Input::from(&constraint_bytes),
1529            );
1530            assert_eq!(
1531                actual_result, expected_result,
1532                "presented_id_matches_constraint(\"{:?}\", \"{:?}\")",
1533                presented_bytes, constraint_bytes
1534            );
1535        }
1536    }
1537}