File
Blob: firmware/vendor/sctp-proto/src/util.rs
| 1 | use crate::shared::AssociationId; |
| 2 | |
| 3 | use alloc::borrow::ToOwned; |
| 4 | use bytes::Bytes; |
| 5 | use core::time::Duration; |
| 6 | use crc::{CRC_32_ISCSI, Crc, Table}; |
| 7 | |
| 8 | /// This function is non-inline to prevent the optimizer from looking inside it. |
| 9 | #[inline(never)] |
| 10 | fn constant_time_ne(a: &[u8], b: &[u8]) -> u8 { |
| 11 | assert!(a.len() == b.len()); |
| 12 | |
| 13 | // These useless slices make the optimizer elide the bounds checks. |
| 14 | // See the comment in clone_from_slice() added on Rust commit 6a7bc47. |
| 15 | let len = a.len(); |
| 16 | let a = &a[..len]; |
| 17 | let b = &b[..len]; |
| 18 | |
| 19 | let mut tmp = 0; |
| 20 | for i in 0..len { |
| 21 | tmp |= a[i] ^ b[i]; |
| 22 | } |
| 23 | tmp // The compare with 0 must happen outside this function. |
| 24 | } |
| 25 | |
| 26 | /// Compares byte strings in constant time. |
| 27 | pub fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { |
| 28 | a.len() == b.len() && constant_time_ne(a, b) == 0 |
| 29 | } |
| 30 | |
| 31 | /// Generates association id for incoming associations |
| 32 | pub trait AssociationIdGenerator: Send + Sync { |
| 33 | /// Generates a new AID |
| 34 | /// |
| 35 | /// Association IDs MUST NOT contain any information that can be used by |
| 36 | /// an external observer (that is, one that does not cooperate with the |
| 37 | /// issuer) to correlate them with other Association IDs for the same |
| 38 | /// Association. |
| 39 | fn generate_aid(&mut self) -> AssociationId; |
| 40 | |
| 41 | /// Returns the lifetime of generated Association IDs |
| 42 | /// |
| 43 | /// Association IDs will be retired after the returned `Duration`, if any. Assumed to be constant. |
| 44 | fn aid_lifetime(&self) -> Option<Duration>; |
| 45 | } |
| 46 | |
| 47 | /// Generates purely random Association IDs of a certain length |
| 48 | #[derive(Default, Debug, Clone, Copy)] |
| 49 | pub struct RandomAssociationIdGenerator { |
| 50 | lifetime: Option<Duration>, |
| 51 | } |
| 52 | |
| 53 | impl RandomAssociationIdGenerator { |
| 54 | /// Initialize Random AID generator |
| 55 | pub fn new() -> Self { |
| 56 | RandomAssociationIdGenerator::default() |
| 57 | } |
| 58 | |
| 59 | /// Set the lifetime of CIDs created by this generator |
| 60 | pub fn set_lifetime(&mut self, d: Duration) -> &mut Self { |
| 61 | self.lifetime = Some(d); |
| 62 | self |
| 63 | } |
| 64 | } |
| 65 | |
| 66 | impl AssociationIdGenerator for RandomAssociationIdGenerator { |
| 67 | fn generate_aid(&mut self) -> AssociationId { |
| 68 | rand::random::<u32>() |
| 69 | } |
| 70 | |
| 71 | fn aid_lifetime(&self) -> Option<Duration> { |
| 72 | self.lifetime |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | pub(crate) const PADDING_MULTIPLE: usize = 4; |
| 77 | |
| 78 | pub(crate) fn get_padding_size(len: usize) -> usize { |
| 79 | (PADDING_MULTIPLE - (len % PADDING_MULTIPLE)) % PADDING_MULTIPLE |
| 80 | } |
| 81 | |
| 82 | /// Allocate and zero this data once. |
| 83 | /// We need to use it for the checksum and don't want to allocate/clear each time. |
| 84 | pub(crate) static FOUR_ZEROES: Bytes = Bytes::from_static(&[0, 0, 0, 0]); |
| 85 | pub(crate) const ISCSI_CRC: Crc<u32, Table<16>> = Crc::<u32, Table<16>>::new(&CRC_32_ISCSI); |
| 86 | |
| 87 | /// Fastest way to do a crc32 without allocating. |
| 88 | pub(crate) fn generate_packet_checksum(raw: &Bytes) -> u32 { |
| 89 | let mut digest = ISCSI_CRC.digest(); |
| 90 | digest.update(&raw[0..8]); |
| 91 | digest.update(&FOUR_ZEROES[..]); |
| 92 | digest.update(&raw[12..]); |
| 93 | digest.finalize() |
| 94 | } |
| 95 | |
| 96 | /// A [`BytesSource`] implementation for `&'a mut [Bytes]` |
| 97 | /// |
| 98 | /// The type allows to dequeue [`Bytes`] chunks from an array of chunks, up to |
| 99 | /// a configured limit. |
| 100 | pub struct BytesArray<'a> { |
| 101 | /// The wrapped slice of `Bytes` |
| 102 | chunks: &'a mut [Bytes], |
| 103 | /// The amount of chunks consumed from this source |
| 104 | consumed: usize, |
| 105 | length: usize, |
| 106 | } |
| 107 | |
| 108 | impl<'a> BytesArray<'a> { |
| 109 | pub fn from_chunks(chunks: &'a mut [Bytes]) -> Self { |
| 110 | let mut length = 0; |
| 111 | for chunk in chunks.iter() { |
| 112 | length += chunk.len(); |
| 113 | } |
| 114 | |
| 115 | Self { |
| 116 | chunks, |
| 117 | consumed: 0, |
| 118 | length, |
| 119 | } |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | impl<'a> BytesSource for BytesArray<'a> { |
| 124 | fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize) { |
| 125 | // The loop exists to skip empty chunks while still marking them as |
| 126 | // consumed |
| 127 | let mut chunks_consumed = 0; |
| 128 | |
| 129 | while self.consumed < self.chunks.len() { |
| 130 | let chunk = &mut self.chunks[self.consumed]; |
| 131 | |
| 132 | if chunk.len() <= limit { |
| 133 | let chunk = core::mem::take(chunk); |
| 134 | self.consumed += 1; |
| 135 | chunks_consumed += 1; |
| 136 | if chunk.is_empty() { |
| 137 | continue; |
| 138 | } |
| 139 | return (chunk, chunks_consumed); |
| 140 | } else if limit > 0 { |
| 141 | let chunk = chunk.split_to(limit); |
| 142 | return (chunk, chunks_consumed); |
| 143 | } else { |
| 144 | break; |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | (Bytes::new(), chunks_consumed) |
| 149 | } |
| 150 | |
| 151 | fn has_remaining(&self) -> bool { |
| 152 | self.consumed < self.length |
| 153 | } |
| 154 | |
| 155 | fn remaining(&self) -> usize { |
| 156 | self.length - self.consumed |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | /// A [`BytesSource`] implementation for `&[u8]` |
| 161 | /// |
| 162 | /// The type allows to dequeue a single [`Bytes`] chunk, which will be lazily |
| 163 | /// created from a reference. This allows to defer the allocation until it is |
| 164 | /// known how much data needs to be copied. |
| 165 | pub struct ByteSlice<'a> { |
| 166 | /// The wrapped byte slice |
| 167 | data: &'a [u8], |
| 168 | } |
| 169 | |
| 170 | impl<'a> ByteSlice<'a> { |
| 171 | pub fn from_slice(data: &'a [u8]) -> Self { |
| 172 | Self { data } |
| 173 | } |
| 174 | } |
| 175 | |
| 176 | impl<'a> BytesSource for ByteSlice<'a> { |
| 177 | fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize) { |
| 178 | let limit = limit.min(self.data.len()); |
| 179 | if limit == 0 { |
| 180 | return (Bytes::new(), 0); |
| 181 | } |
| 182 | |
| 183 | let chunk = Bytes::from(self.data[..limit].to_owned()); |
| 184 | self.data = &self.data[chunk.len()..]; |
| 185 | |
| 186 | let chunks_consumed = if self.data.is_empty() { 1 } else { 0 }; |
| 187 | (chunk, chunks_consumed) |
| 188 | } |
| 189 | |
| 190 | fn has_remaining(&self) -> bool { |
| 191 | !self.data.is_empty() |
| 192 | } |
| 193 | |
| 194 | fn remaining(&self) -> usize { |
| 195 | self.data.len() |
| 196 | } |
| 197 | } |
| 198 | |
| 199 | /// A source of one or more buffers which can be converted into `Bytes` buffers on demand |
| 200 | /// |
| 201 | /// The purpose of this data type is to defer conversion as long as possible, |
| 202 | /// so that no heap allocation is required in case no data is writable. |
| 203 | pub trait BytesSource { |
| 204 | /// Returns the next chunk from the source of owned chunks. |
| 205 | /// |
| 206 | /// This method will consume parts of the source. |
| 207 | /// Calling it will yield `Bytes` elements up to the configured `limit`. |
| 208 | /// |
| 209 | /// The method returns a tuple: |
| 210 | /// - The first item is the yielded `Bytes` element. The element will be |
| 211 | /// empty if the limit is zero or no more data is available. |
| 212 | /// - The second item returns how many complete chunks inside the source had |
| 213 | /// had been consumed. This can be less than 1, if a chunk inside the |
| 214 | /// source had been truncated in order to adhere to the limit. It can also |
| 215 | /// be more than 1, if zero-length chunks had been skipped. |
| 216 | fn pop_chunk(&mut self, limit: usize) -> (Bytes, usize); |
| 217 | |
| 218 | fn has_remaining(&self) -> bool; |
| 219 | |
| 220 | fn remaining(&self) -> usize; |
| 221 | } |
| 222 | |
| 223 | /// Serial Number Arithmetic (RFC 1982) |
| 224 | #[inline] |
| 225 | pub(crate) fn sna32lt(i1: u32, i2: u32) -> bool { |
| 226 | (i1 < i2 && i2 - i1 < 1 << 31) || (i1 > i2 && i1 - i2 > 1 << 31) |
| 227 | } |
| 228 | |
| 229 | #[inline] |
| 230 | pub(crate) fn sna32lte(i1: u32, i2: u32) -> bool { |
| 231 | i1 == i2 || sna32lt(i1, i2) |
| 232 | } |
| 233 | |
| 234 | #[inline] |
| 235 | pub(crate) fn sna32gt(i1: u32, i2: u32) -> bool { |
| 236 | (i1 < i2 && (i2 - i1) >= 1 << 31) || (i1 > i2 && (i1 - i2) <= 1 << 31) |
| 237 | } |
| 238 | |
| 239 | #[inline] |
| 240 | pub(crate) fn sna32gte(i1: u32, i2: u32) -> bool { |
| 241 | i1 == i2 || sna32gt(i1, i2) |
| 242 | } |
| 243 | |
| 244 | #[inline] |
| 245 | pub(crate) fn sna32eq(i1: u32, i2: u32) -> bool { |
| 246 | i1 == i2 |
| 247 | } |
| 248 | |
| 249 | #[inline] |
| 250 | pub(crate) fn sna16lt(i1: u16, i2: u16) -> bool { |
| 251 | (i1 < i2 && (i2 - i1) < 1 << 15) || (i1 > i2 && (i1 - i2) > 1 << 15) |
| 252 | } |
| 253 | |
| 254 | #[inline] |
| 255 | pub(crate) fn sna16lte(i1: u16, i2: u16) -> bool { |
| 256 | i1 == i2 || sna16lt(i1, i2) |
| 257 | } |
| 258 | |
| 259 | #[inline] |
| 260 | pub(crate) fn sna16gt(i1: u16, i2: u16) -> bool { |
| 261 | (i1 < i2 && (i2 - i1) >= 1 << 15) || (i1 > i2 && (i1 - i2) <= 1 << 15) |
| 262 | } |
| 263 | |
| 264 | #[inline] |
| 265 | pub(crate) fn sna16gte(i1: u16, i2: u16) -> bool { |
| 266 | i1 == i2 || sna16gt(i1, i2) |
| 267 | } |
| 268 | |
| 269 | #[inline] |
| 270 | pub(crate) fn sna16eq(i1: u16, i2: u16) -> bool { |
| 271 | i1 == i2 |
| 272 | } |
| 273 | |
| 274 | #[cfg(test)] |
| 275 | mod test { |
| 276 | use crate::error::Result; |
| 277 | |
| 278 | use super::*; |
| 279 | |
| 280 | const DIV: isize = 16; |
| 281 | |
| 282 | #[test] |
| 283 | fn test_serial_number_arithmetic32bit() -> Result<()> { |
| 284 | const SERIAL_BITS: u32 = 32; |
| 285 | const INTERVAL: u32 = ((1u64 << (SERIAL_BITS as u64)) / (DIV as u64)) as u32; |
| 286 | const MAX_FORWARD_DISTANCE: u32 = 1 << ((SERIAL_BITS - 1) - 1); |
| 287 | const MAX_BACKWARD_DISTANCE: u32 = 1 << (SERIAL_BITS - 1); |
| 288 | |
| 289 | for i in 0..DIV as u32 { |
| 290 | let s1 = i * INTERVAL; |
| 291 | let s2f = s1.checked_add(MAX_FORWARD_DISTANCE); |
| 292 | let s2b = s1.checked_add(MAX_BACKWARD_DISTANCE); |
| 293 | |
| 294 | if let (Some(s2f), Some(s2b)) = (s2f, s2b) { |
| 295 | assert!( |
| 296 | sna32lt(s1, s2f), |
| 297 | "s1 < s2 should be true: s1={} s2={}", |
| 298 | s1, |
| 299 | s2f |
| 300 | ); |
| 301 | assert!( |
| 302 | !sna32lt(s1, s2b), |
| 303 | "s1 < s2 should be false: s1={} s2={}", |
| 304 | s1, |
| 305 | s2b |
| 306 | ); |
| 307 | |
| 308 | assert!( |
| 309 | !sna32gt(s1, s2f), |
| 310 | "s1 > s2 should be false: s1={} s2={}", |
| 311 | s1, |
| 312 | s2f |
| 313 | ); |
| 314 | assert!( |
| 315 | sna32gt(s1, s2b), |
| 316 | "s1 > s2 should be true: s1={} s2={}", |
| 317 | s1, |
| 318 | s2b |
| 319 | ); |
| 320 | |
| 321 | assert!( |
| 322 | sna32lte(s1, s2f), |
| 323 | "s1 <= s2 should be true: s1={} s2={}", |
| 324 | s1, |
| 325 | s2f |
| 326 | ); |
| 327 | assert!( |
| 328 | !sna32lte(s1, s2b), |
| 329 | "s1 <= s2 should be false: s1={} s2={}", |
| 330 | s1, |
| 331 | s2b |
| 332 | ); |
| 333 | |
| 334 | assert!( |
| 335 | !sna32gte(s1, s2f), |
| 336 | "s1 >= s2 should be fales: s1={} s2={}", |
| 337 | s1, |
| 338 | s2f |
| 339 | ); |
| 340 | assert!( |
| 341 | sna32gte(s1, s2b), |
| 342 | "s1 >= s2 should be true: s1={} s2={}", |
| 343 | s1, |
| 344 | s2b |
| 345 | ); |
| 346 | |
| 347 | assert!( |
| 348 | sna32eq(s2b, s2b), |
| 349 | "s2 == s2 should be true: s2={} s2={}", |
| 350 | s2b, |
| 351 | s2b |
| 352 | ); |
| 353 | assert!( |
| 354 | sna32lte(s2b, s2b), |
| 355 | "s2 == s2 should be true: s2={} s2={}", |
| 356 | s2b, |
| 357 | s2b |
| 358 | ); |
| 359 | assert!( |
| 360 | sna32gte(s2b, s2b), |
| 361 | "s2 == s2 should be true: s2={} s2={}", |
| 362 | s2b, |
| 363 | s2b |
| 364 | ); |
| 365 | } |
| 366 | |
| 367 | if let Some(s1add1) = s1.checked_add(1) { |
| 368 | assert!( |
| 369 | !sna32eq(s1, s1add1), |
| 370 | "s1 == s1+1 should be false: s1={} s1+1={}", |
| 371 | s1, |
| 372 | s1add1 |
| 373 | ); |
| 374 | } |
| 375 | |
| 376 | if let Some(s1sub1) = s1.checked_sub(1) { |
| 377 | assert!( |
| 378 | !sna32eq(s1, s1sub1), |
| 379 | "s1 == s1-1 hould be false: s1={} s1-1={}", |
| 380 | s1, |
| 381 | s1sub1 |
| 382 | ); |
| 383 | } |
| 384 | |
| 385 | assert!( |
| 386 | sna32eq(s1, s1), |
| 387 | "s1 == s1 should be true: s1={} s2={}", |
| 388 | s1, |
| 389 | s1 |
| 390 | ); |
| 391 | assert!( |
| 392 | sna32lte(s1, s1), |
| 393 | "s1 == s1 should be true: s1={} s2={}", |
| 394 | s1, |
| 395 | s1 |
| 396 | ); |
| 397 | |
| 398 | assert!( |
| 399 | sna32gte(s1, s1), |
| 400 | "s1 == s1 should be true: s1={} s2={}", |
| 401 | s1, |
| 402 | s1 |
| 403 | ); |
| 404 | } |
| 405 | |
| 406 | Ok(()) |
| 407 | } |
| 408 | |
| 409 | #[test] |
| 410 | fn test_serial_number_arithmetic16bit() -> Result<()> { |
| 411 | const SERIAL_BITS: u16 = 16; |
| 412 | const INTERVAL: u16 = ((1u64 << (SERIAL_BITS as u64)) / (DIV as u64)) as u16; |
| 413 | const MAX_FORWARD_DISTANCE: u16 = 1 << ((SERIAL_BITS - 1) - 1); |
| 414 | const MAX_BACKWARD_DISTANCE: u16 = 1 << (SERIAL_BITS - 1); |
| 415 | |
| 416 | for i in 0..DIV as u16 { |
| 417 | let s1 = i * INTERVAL; |
| 418 | let s2f = s1.checked_add(MAX_FORWARD_DISTANCE); |
| 419 | let s2b = s1.checked_add(MAX_BACKWARD_DISTANCE); |
| 420 | |
| 421 | if let (Some(s2f), Some(s2b)) = (s2f, s2b) { |
| 422 | assert!( |
| 423 | sna16lt(s1, s2f), |
| 424 | "s1 < s2 should be true: s1={} s2={}", |
| 425 | s1, |
| 426 | s2f |
| 427 | ); |
| 428 | assert!( |
| 429 | !sna16lt(s1, s2b), |
| 430 | "s1 < s2 should be false: s1={} s2={}", |
| 431 | s1, |
| 432 | s2b |
| 433 | ); |
| 434 | |
| 435 | assert!( |
| 436 | !sna16gt(s1, s2f), |
| 437 | "s1 > s2 should be fales: s1={} s2={}", |
| 438 | s1, |
| 439 | s2f |
| 440 | ); |
| 441 | assert!( |
| 442 | sna16gt(s1, s2b), |
| 443 | "s1 > s2 should be true: s1={} s2={}", |
| 444 | s1, |
| 445 | s2b |
| 446 | ); |
| 447 | |
| 448 | assert!( |
| 449 | sna16lte(s1, s2f), |
| 450 | "s1 <= s2 should be true: s1={} s2={}", |
| 451 | s1, |
| 452 | s2f |
| 453 | ); |
| 454 | assert!( |
| 455 | !sna16lte(s1, s2b), |
| 456 | "s1 <= s2 should be false: s1={} s2={}", |
| 457 | s1, |
| 458 | s2b |
| 459 | ); |
| 460 | |
| 461 | assert!( |
| 462 | !sna16gte(s1, s2f), |
| 463 | "s1 >= s2 should be fales: s1={} s2={}", |
| 464 | s1, |
| 465 | s2f |
| 466 | ); |
| 467 | assert!( |
| 468 | sna16gte(s1, s2b), |
| 469 | "s1 >= s2 should be true: s1={} s2={}", |
| 470 | s1, |
| 471 | s2b |
| 472 | ); |
| 473 | |
| 474 | assert!( |
| 475 | sna16eq(s2b, s2b), |
| 476 | "s2 == s2 should be true: s2={} s2={}", |
| 477 | s2b, |
| 478 | s2b |
| 479 | ); |
| 480 | assert!( |
| 481 | sna16lte(s2b, s2b), |
| 482 | "s2 == s2 should be true: s2={} s2={}", |
| 483 | s2b, |
| 484 | s2b |
| 485 | ); |
| 486 | assert!( |
| 487 | sna16gte(s2b, s2b), |
| 488 | "s2 == s2 should be true: s2={} s2={}", |
| 489 | s2b, |
| 490 | s2b |
| 491 | ); |
| 492 | } |
| 493 | |
| 494 | assert!( |
| 495 | sna16eq(s1, s1), |
| 496 | "s1 == s1 should be true: s1={} s2={}", |
| 497 | s1, |
| 498 | s1 |
| 499 | ); |
| 500 | |
| 501 | if let Some(s1add1) = s1.checked_add(1) { |
| 502 | assert!( |
| 503 | !sna16eq(s1, s1add1), |
| 504 | "s1 == s1+1 should be false: s1={} s1+1={}", |
| 505 | s1, |
| 506 | s1add1 |
| 507 | ); |
| 508 | } |
| 509 | if let Some(s1sub1) = s1.checked_sub(1) { |
| 510 | assert!( |
| 511 | !sna16eq(s1, s1sub1), |
| 512 | "s1 == s1-1 hould be false: s1={} s1-1={}", |
| 513 | s1, |
| 514 | s1sub1 |
| 515 | ); |
| 516 | } |
| 517 | |
| 518 | assert!( |
| 519 | sna16lte(s1, s1), |
| 520 | "s1 == s1 should be true: s1={} s2={}", |
| 521 | s1, |
| 522 | s1 |
| 523 | ); |
| 524 | assert!( |
| 525 | sna16gte(s1, s1), |
| 526 | "s1 == s1 should be true: s1={} s2={}", |
| 527 | s1, |
| 528 | s1 |
| 529 | ); |
| 530 | } |
| 531 | |
| 532 | Ok(()) |
| 533 | } |
| 534 | } |