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