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 | use pki_types::IpAddr; |
16 | |
17 | use super::verify::{GeneralName, NameIterator}; |
18 | use crate::Error; |
19 | |
20 | pub(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. |
57 | fn 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). |
91 | pub(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)] |
168 | mod 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" ))] |
549 | mod 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 | |