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: Reader<'_> = untrusted::Reader::new(input:presented_id);
70 let mut reference_ip_address: Reader<'_> = untrusted::Reader::new(input:reference_id);
71 while !presented_ip_address.at_end() {
72 let presented_ip_address_byte: u8 = presented_ip_address.read_byte().unwrap();
73 let reference_ip_address_byte: u8 = 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}
690