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: Reader<'_> = untrusted::Reader::new(input:presented_id);
89 let mut reference_ip_address: Reader<'_> = untrusted::Reader::new(input:reference_id);
90 while !presented_ip_address.at_end() {
91 let presented_ip_address_byte: u8 = presented_ip_address.read_byte().unwrap();
92 let reference_ip_address_byte: u8 = 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}
709