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
15use pki_types::IpAddr;
16
17use super::verify::{GeneralName, NameIterator};
18use crate::Error;
19
20pub(crate) fn verify_ip_address_names(
21    reference: &IpAddr,
22    mut names: NameIterator<'_>,
23) -> Result<(), Error> {
24    let ip_address = match reference {
25        IpAddr::V4(ip) => untrusted::Input::from(ip.as_ref()),
26        IpAddr::V6(ip) => untrusted::Input::from(ip.as_ref()),
27    };
28
29    names
30        .find_map(|result| {
31            let name = match result {
32                Ok(name) => name,
33                Err(err) => return Some(Err(err)),
34            };
35
36            let presented_id = match name {
37                GeneralName::IpAddress(presented) => presented,
38                _ => return None,
39            };
40
41            match presented_id_matches_reference_id(presented_id, ip_address) {
42                true => Some(Ok(())),
43                false => None,
44            }
45        })
46        .unwrap_or(Err(Error::CertNotValidForName))
47}
48
49// https://tools.ietf.org/html/rfc5280#section-4.2.1.6 says:
50//   When the subjectAltName extension contains an iPAddress, the address
51//   MUST be stored in the octet string in "network byte order", as
52//   specified in [RFC791].  The least significant bit (LSB) of each octet
53//   is the LSB of the corresponding byte in the network address.  For IP
54//   version 4, as specified in [RFC791], the octet string MUST contain
55//   exactly four octets.  For IP version 6, as specified in
56//   [RFC2460], the octet string MUST contain exactly sixteen octets.
57fn presented_id_matches_reference_id(
58    presented_id: untrusted::Input<'_>,
59    reference_id: untrusted::Input<'_>,
60) -> bool {
61    match (presented_id.len(), reference_id.len()) {
62        (4, 4) => (),
63        (16, 16) => (),
64        _ => {
65            return false;
66        }
67    };
68
69    let mut presented_ip_address = untrusted::Reader::new(presented_id);
70    let mut reference_ip_address = untrusted::Reader::new(reference_id);
71    while !presented_ip_address.at_end() {
72        let presented_ip_address_byte = presented_ip_address.read_byte().unwrap();
73        let reference_ip_address_byte = reference_ip_address.read_byte().unwrap();
74        if presented_ip_address_byte != reference_ip_address_byte {
75            return false;
76        }
77    }
78
79    true
80}
81
82// https://tools.ietf.org/html/rfc5280#section-4.2.1.10 says:
83//
84//     For IPv4 addresses, the iPAddress field of GeneralName MUST contain
85//     eight (8) octets, encoded in the style of RFC 4632 (CIDR) to represent
86//     an address range [RFC4632].  For IPv6 addresses, the iPAddress field
87//     MUST contain 32 octets similarly encoded.  For example, a name
88//     constraint for "class C" subnet 192.0.2.0 is represented as the
89//     octets C0 00 02 00 FF FF FF 00, representing the CIDR notation
90//     192.0.2.0/24 (mask 255.255.255.0).
91pub(super) fn presented_id_matches_constraint(
92    name: untrusted::Input<'_>,
93    constraint: untrusted::Input<'_>,
94) -> Result<bool, Error> {
95    match (name.len(), constraint.len()) {
96        (4, 8) => (),
97        (16, 32) => (),
98
99        // an IPv4 address never matches an IPv6 constraint, and vice versa.
100        (4, 32) | (16, 8) => {
101            return Ok(false);
102        }
103
104        // invalid constraint length
105        (4, _) | (16, _) => {
106            return Err(Error::InvalidNetworkMaskConstraint);
107        }
108
109        // invalid name length, or anything else
110        _ => {
111            return Err(Error::BadDer);
112        }
113    };
114
115    let (constraint_address, constraint_mask) = constraint.read_all(Error::BadDer, |value| {
116        let address = value.read_bytes(constraint.len() / 2).unwrap();
117        let mask = value.read_bytes(constraint.len() / 2).unwrap();
118        Ok((address, mask))
119    })?;
120
121    let mut name = untrusted::Reader::new(name);
122    let mut constraint_address = untrusted::Reader::new(constraint_address);
123    let mut constraint_mask = untrusted::Reader::new(constraint_mask);
124    let mut seen_zero_bit = false;
125
126    loop {
127        // Iterate through the name, constraint address, and constraint mask
128        // a byte at a time.
129        let name_byte = name.read_byte().unwrap();
130        let constraint_address_byte = constraint_address.read_byte().unwrap();
131        let constraint_mask_byte = constraint_mask.read_byte().unwrap();
132
133        // A valid mask consists of a sequence of 1 bits, followed by a
134        // sequence of 0 bits.  Either sequence could be empty.
135
136        let leading = constraint_mask_byte.leading_ones();
137        let trailing = constraint_mask_byte.trailing_zeros();
138
139        // At the resolution of a single octet, a valid mask is one where
140        // leading_ones() and trailing_zeros() sums to 8.
141        // This includes all-ones and all-zeroes.
142        if leading + trailing != 8 {
143            return Err(Error::InvalidNetworkMaskConstraint);
144        }
145
146        // There should be no bits set after the first octet with a zero bit is seen.
147        if seen_zero_bit && constraint_mask_byte != 0x00 {
148            return Err(Error::InvalidNetworkMaskConstraint);
149        }
150
151        // Note when a zero bit is seen for later octets.
152        if constraint_mask_byte != 0xff {
153            seen_zero_bit = true;
154        }
155
156        if ((name_byte ^ constraint_address_byte) & constraint_mask_byte) != 0 {
157            return Ok(false);
158        }
159        if name.at_end() {
160            break;
161        }
162    }
163
164    Ok(true)
165}
166
167#[cfg(test)]
168mod tests {
169    use super::*;
170
171    #[test]
172    fn presented_id_matches_constraint_ipv4_test() {
173        let names_and_constraints = vec![
174            (
175                // 192.0.2.0 matches constraint 192.0.2.0/24
176                [0xC0, 0x00, 0x02, 0x00],
177                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
178                Ok(true),
179            ),
180            (
181                // 192.0.2.1 matches constraint 192.0.2.0/24
182                [0xC0, 0x00, 0x02, 0x01],
183                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
184                Ok(true),
185            ),
186            (
187                // 192.0.2.255 matches constraint 192.0.2.0/24
188                [0xC0, 0x00, 0x02, 0xFF],
189                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
190                Ok(true),
191            ),
192            (
193                // 192.0.1.255 does not match constraint 192.0.2.0/24
194                [0xC0, 0x00, 0x01, 0xFF],
195                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
196                Ok(false),
197            ),
198            (
199                // 192.0.3.0 does not match constraint 192.0.2.0/24
200                [0xC0, 0x00, 0x03, 0x00],
201                [0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00],
202                Ok(false),
203            ),
204        ];
205        for (name, constraint, match_result) in names_and_constraints {
206            assert_eq!(
207                presented_id_matches_constraint(
208                    untrusted::Input::from(&name),
209                    untrusted::Input::from(&constraint),
210                ),
211                match_result
212            )
213        }
214
215        // Invalid name length (shorter)
216        assert_eq!(
217            presented_id_matches_constraint(
218                untrusted::Input::from(&[0xC0, 0x00, 0x02]),
219                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
220            ),
221            Err(Error::BadDer),
222        );
223
224        // Invalid name length (longer)
225        assert_eq!(
226            presented_id_matches_constraint(
227                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0x00]),
228                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
229            ),
230            Err(Error::BadDer),
231        );
232
233        // Unmatching constraint size (shorter)
234        assert_eq!(
235            presented_id_matches_constraint(
236                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
237                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF]),
238            ),
239            Err(Error::InvalidNetworkMaskConstraint),
240        );
241
242        // Unmatching constraint size (longer)
243        assert_eq!(
244            presented_id_matches_constraint(
245                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
246                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00]),
247            ),
248            Err(Error::InvalidNetworkMaskConstraint),
249        );
250
251        // Unmatching constraint size (IPv6 constraint for IPv4 address)
252        assert_eq!(
253            presented_id_matches_constraint(
254                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00]),
255                untrusted::Input::from(&[
256                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
257                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
258                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
259                ]),
260            ),
261            Ok(false),
262        );
263    }
264
265    #[test]
266    fn presented_id_matches_constraint_ipv6_test() {
267        let names_and_constraints = vec![
268            (
269                // 2001:0DB8:ABCD:0012:0000:0000:0000:0000 matches constraint
270                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
271                [
272                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
273                    0x00, 0x00, 0x00,
274                ],
275                [
276                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
277                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
278                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
279                ],
280                Ok(true),
281            ),
282            (
283                // 2001:0DB8:ABCD:0012:0000:0000:0000:0001 matches constraint
284                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
285                [
286                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
287                    0x00, 0x00, 0x01,
288                ],
289                [
290                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
291                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
292                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
293                ],
294                Ok(true),
295            ),
296            (
297                // 2001:0DB8:ABCD:0012:FFFF:FFFF:FFFF:FFFF matches constraint
298                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
299                [
300                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
301                    0xFF, 0xFF, 0xFF,
302                ],
303                [
304                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
305                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
306                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
307                ],
308                Ok(true),
309            ),
310            (
311                // 2001:0DB8:ABCD:0011:0000:0000:0000:0000 does not match constraint
312                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
313                [
314                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00,
315                    0x00, 0x00, 0x00,
316                ],
317                [
318                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
319                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
320                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
321                ],
322                Ok(false),
323            ),
324            (
325                // 2001:0DB8:ABCD:0013:0000:0000:0000:0000 does not match constraint
326                //   2001:0DB8:ABCD:0012:0000:0000:0000:0000/64
327                [
328                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00,
329                    0x00, 0x00, 0x00,
330                ],
331                [
332                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
333                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
334                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
335                ],
336                Ok(false),
337            ),
338        ];
339        for (name, constraint, match_result) in names_and_constraints {
340            assert_eq!(
341                presented_id_matches_constraint(
342                    untrusted::Input::from(&name),
343                    untrusted::Input::from(&constraint),
344                ),
345                match_result
346            )
347        }
348
349        // Invalid name length (shorter)
350        assert_eq!(
351            presented_id_matches_constraint(
352                untrusted::Input::from(&[
353                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
354                    0x00, 0x00
355                ]),
356                untrusted::Input::from(&[
357                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
358                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
359                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
360                ]),
361            ),
362            Err(Error::BadDer),
363        );
364
365        // Invalid name length (longer)
366        assert_eq!(
367            presented_id_matches_constraint(
368                untrusted::Input::from(&[
369                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
370                    0x00, 0x00, 0x00, 0x00
371                ]),
372                untrusted::Input::from(&[
373                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
374                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
375                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00
376                ]),
377            ),
378            Err(Error::BadDer),
379        );
380
381        // Unmatching constraint size (shorter)
382        assert_eq!(
383            presented_id_matches_constraint(
384                untrusted::Input::from(&[
385                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
386                    0x00, 0x00, 0x00,
387                ]),
388                untrusted::Input::from(&[
389                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
390                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
391                    0x00, 0x00, 0x00, 0x00, 0x00
392                ]),
393            ),
394            Err(Error::InvalidNetworkMaskConstraint),
395        );
396
397        // Unmatching constraint size (longer)
398        assert_eq!(
399            presented_id_matches_constraint(
400                untrusted::Input::from(&[
401                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
402                    0x00, 0x00, 0x00,
403                ]),
404                untrusted::Input::from(&[
405                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
406                    0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00,
407                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
408                ]),
409            ),
410            Err(Error::InvalidNetworkMaskConstraint),
411        );
412
413        // Unmatching constraint size (IPv4 constraint for IPv6 address)
414        assert_eq!(
415            presented_id_matches_constraint(
416                untrusted::Input::from(&[
417                    0x20, 0x01, 0x0D, 0xB8, 0xAB, 0xCD, 0x00, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00,
418                    0x00, 0x00, 0x00,
419                ]),
420                untrusted::Input::from(&[0xC0, 0x00, 0x02, 0x00, 0xFF, 0xFF, 0xFF, 0x00]),
421            ),
422            Ok(false),
423        );
424    }
425
426    #[test]
427    fn test_presented_id_matches_reference_id() {
428        assert!(!presented_id_matches_reference_id(
429            untrusted::Input::from(&[]),
430            untrusted::Input::from(&[]),
431        ));
432
433        assert!(!presented_id_matches_reference_id(
434            untrusted::Input::from(&[0x01]),
435            untrusted::Input::from(&[])
436        ));
437
438        assert!(!presented_id_matches_reference_id(
439            untrusted::Input::from(&[]),
440            untrusted::Input::from(&[0x01])
441        ));
442
443        assert!(presented_id_matches_reference_id(
444            untrusted::Input::from(&[1, 2, 3, 4]),
445            untrusted::Input::from(&[1, 2, 3, 4])
446        ));
447
448        assert!(!presented_id_matches_reference_id(
449            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
450            untrusted::Input::from(&[1, 2, 3, 4])
451        ));
452
453        assert!(!presented_id_matches_reference_id(
454            untrusted::Input::from(&[1, 2, 3, 4]),
455            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
456        ));
457
458        assert!(presented_id_matches_reference_id(
459            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]),
460            untrusted::Input::from(&[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16])
461        ));
462    }
463
464    #[test]
465    fn presented_id_matches_constraint_rejects_incorrect_length_arguments() {
466        // wrong length names
467        assert_eq!(
468            presented_id_matches_constraint(
469                untrusted::Input::from(b"\x00\x00\x00"),
470                untrusted::Input::from(b"")
471            ),
472            Err(Error::BadDer)
473        );
474        assert_eq!(
475            presented_id_matches_constraint(
476                untrusted::Input::from(b"\x00\x00\x00\x00\x00"),
477                untrusted::Input::from(b"")
478            ),
479            Err(Error::BadDer)
480        );
481
482        assert_eq!(
483            presented_id_matches_constraint(
484                untrusted::Input::from(
485                    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
486                ),
487                untrusted::Input::from(b"")
488            ),
489            Err(Error::BadDer)
490        );
491        assert_eq!(
492            presented_id_matches_constraint(
493                untrusted::Input::from(
494                    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
495                ),
496                untrusted::Input::from(b"")
497            ),
498            Err(Error::BadDer)
499        );
500
501        // wrong length constraints
502        assert_eq!(
503            presented_id_matches_constraint(
504                untrusted::Input::from(b"\x00\x00\x00\x00"),
505                untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff")
506            ),
507            Err(Error::InvalidNetworkMaskConstraint)
508        );
509        assert_eq!(
510            presented_id_matches_constraint(
511                untrusted::Input::from(b"\x00\x00\x00\x00"),
512                untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff\xff\x00")
513            ),
514            Err(Error::InvalidNetworkMaskConstraint)
515        );
516        assert_eq!(
517            presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
518                                            untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
519                                                                     \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
520            Err(Error::InvalidNetworkMaskConstraint)
521        );
522        assert_eq!(
523            presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
524                                            untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
525                                                                     \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
526            Err(Error::InvalidNetworkMaskConstraint)
527        );
528
529        // ipv4-length not considered for ipv6-length name, and vv
530        assert_eq!(
531            presented_id_matches_constraint(untrusted::Input::from(b"\x00\x00\x00\x00"),
532                                            untrusted::Input::from(b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
533                                                                     \xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff")),
534            Ok(false)
535        );
536        assert_eq!(
537            presented_id_matches_constraint(
538                untrusted::Input::from(
539                    b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
540                ),
541                untrusted::Input::from(b"\x00\x00\x00\x00\xff\xff\xff\xff")
542            ),
543            Ok(false)
544        );
545    }
546}
547
548#[cfg(all(test, feature = "alloc"))]
549mod alloc_tests {
550    use super::*;
551
552    // (presented_address, constraint_address, constraint_mask, expected_result)
553    const PRESENTED_MATCHES_CONSTRAINT: &[(&str, &str, &str, Result<bool, Error>)] = &[
554        // Cannot mix IpV4 with IpV6 and viceversa
555        ("2001:db8::", "8.8.8.8", "255.255.255.255", Ok(false)),
556        ("8.8.8.8", "2001:db8::", "ffff::", Ok(false)),
557        // IpV4 non-contiguous masks
558        (
559            "8.8.8.8",
560            "8.8.8.8",
561            "255.255.255.1",
562            Err(Error::InvalidNetworkMaskConstraint),
563        ),
564        (
565            "8.8.8.8",
566            "8.8.8.8",
567            "255.255.0.255",
568            Err(Error::InvalidNetworkMaskConstraint),
569        ),
570        (
571            "8.8.8.8",
572            "8.8.8.8",
573            "255.0.255.255",
574            Err(Error::InvalidNetworkMaskConstraint),
575        ),
576        (
577            "8.8.8.8",
578            "8.8.8.8",
579            "0.255.255.255",
580            Err(Error::InvalidNetworkMaskConstraint),
581        ),
582        (
583            "8.8.8.8",
584            "8.8.8.8",
585            "1.255.255.255",
586            Err(Error::InvalidNetworkMaskConstraint),
587        ),
588        (
589            "8.8.8.8",
590            "8.8.8.8",
591            "128.128.128.128",
592            Err(Error::InvalidNetworkMaskConstraint),
593        ),
594        // IpV4
595        ("8.8.8.8", "8.8.8.8", "255.255.255.255", Ok(true)),
596        ("8.8.8.9", "8.8.8.8", "255.255.255.255", Ok(false)),
597        ("8.8.8.9", "8.8.8.8", "255.255.255.254", Ok(true)),
598        ("8.8.8.10", "8.8.8.8", "255.255.255.254", Ok(false)),
599        ("8.8.8.10", "8.8.8.8", "255.255.255.0", Ok(true)),
600        ("8.8.15.10", "8.8.8.8", "255.255.248.0", Ok(true)),
601        ("8.8.16.10", "8.8.8.8", "255.255.248.0", Ok(false)),
602        ("8.8.16.10", "8.8.8.8", "255.255.0.0", Ok(true)),
603        ("8.31.16.10", "8.8.8.8", "255.224.0.0", Ok(true)),
604        ("8.32.16.10", "8.8.8.8", "255.224.0.0", Ok(false)),
605        ("8.32.16.10", "8.8.8.8", "255.0.0.0", Ok(true)),
606        ("63.32.16.10", "8.8.8.8", "192.0.0.0", Ok(true)),
607        ("64.32.16.10", "8.8.8.8", "192.0.0.0", Ok(false)),
608        ("64.32.16.10", "8.8.8.8", "0.0.0.0", Ok(true)),
609        // IpV6 non-contiguous masks
610        (
611            "2001:db8::",
612            "2001:db8::",
613            "fffe:ffff::",
614            Err(Error::InvalidNetworkMaskConstraint),
615        ),
616        (
617            "2001:db8::",
618            "2001:db8::",
619            "ffff:fdff::",
620            Err(Error::InvalidNetworkMaskConstraint),
621        ),
622        (
623            "2001:db8::",
624            "2001:db8::",
625            "ffff:feff::",
626            Err(Error::InvalidNetworkMaskConstraint),
627        ),
628        (
629            "2001:db8::",
630            "2001:db8::",
631            "ffff:fcff::",
632            Err(Error::InvalidNetworkMaskConstraint),
633        ),
634        (
635            "2001:db8::",
636            "2001:db8::",
637            "7fff:ffff::",
638            Err(Error::InvalidNetworkMaskConstraint),
639        ),
640        // IpV6
641        ("2001:db8::", "2001:db8::", "ffff:ffff::", Ok(true)),
642        ("2001:db9::", "2001:db8::", "ffff:ffff::", Ok(false)),
643        ("2001:db9::", "2001:db8::", "ffff:fffe::", Ok(true)),
644        ("2001:dba::", "2001:db8::", "ffff:fffe::", Ok(false)),
645        ("2001:dba::", "2001:db8::", "ffff:ff00::", Ok(true)),
646        ("2001:dca::", "2001:db8::", "ffff:fe00::", Ok(true)),
647        ("2001:fca::", "2001:db8::", "ffff:fe00::", Ok(false)),
648        ("2001:fca::", "2001:db8::", "ffff:0000::", Ok(true)),
649        ("2000:fca::", "2001:db8::", "fffe:0000::", Ok(true)),
650        ("2003:fca::", "2001:db8::", "fffe:0000::", Ok(false)),
651        ("2003:fca::", "2001:db8::", "ff00:0000::", Ok(true)),
652        ("1003:fca::", "2001:db8::", "e000:0000::", Ok(false)),
653        ("1003:fca::", "2001:db8::", "0000:0000::", Ok(true)),
654    ];
655
656    #[cfg(feature = "std")]
657    #[test]
658    fn presented_matches_constraint_test() {
659        use std::boxed::Box;
660        use std::net::IpAddr;
661
662        for &(presented, constraint_address, constraint_mask, expected_result) in
663            PRESENTED_MATCHES_CONSTRAINT
664        {
665            let presented_bytes: Box<[u8]> = match presented.parse::<IpAddr>().unwrap() {
666                IpAddr::V4(p) => Box::new(p.octets()),
667                IpAddr::V6(p) => Box::new(p.octets()),
668            };
669            let ca_bytes: Box<[u8]> = match constraint_address.parse::<IpAddr>().unwrap() {
670                IpAddr::V4(ca) => Box::new(ca.octets()),
671                IpAddr::V6(ca) => Box::new(ca.octets()),
672            };
673            let cm_bytes: Box<[u8]> = match constraint_mask.parse::<IpAddr>().unwrap() {
674                IpAddr::V4(cm) => Box::new(cm.octets()),
675                IpAddr::V6(cm) => Box::new(cm.octets()),
676            };
677            let constraint_bytes = [ca_bytes, cm_bytes].concat();
678            let actual_result = presented_id_matches_constraint(
679                untrusted::Input::from(&presented_bytes),
680                untrusted::Input::from(&constraint_bytes),
681            );
682            assert_eq!(
683                actual_result, expected_result,
684                "presented_id_matches_constraint(\"{:?}\", \"{:?}\")",
685                presented_bytes, constraint_bytes
686            );
687        }
688    }
689}