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" )] |
16 | use alloc::format; |
17 | |
18 | use pki_types::IpAddr; |
19 | #[cfg (feature = "alloc" )] |
20 | use pki_types::ServerName; |
21 | |
22 | use super::{GeneralName, NameIterator}; |
23 | use crate::cert::Cert; |
24 | use crate::error::{Error, InvalidNameContext}; |
25 | |
26 | pub(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. |
76 | fn 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). |
110 | pub(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)] |
187 | mod 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" ))] |
568 | mod 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 | |