Skip to main content

internet_checksum/
lib.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5//! RFC 1071 "internet checksum" computation.
6//!
7//! This crate implements the "internet checksum" defined in [RFC 1071] and
8//! updated in [RFC 1141] and [RFC 1624], which is used by many different
9//! protocols' packet formats. The checksum operates by computing the 1s
10//! complement of the 1s complement sum of successive 16-bit words of the input.
11//!
12//! [RFC 1071]: https://tools.ietf.org/html/rfc1071
13//! [RFC 1141]: https://tools.ietf.org/html/rfc1141
14//! [RFC 1624]: https://tools.ietf.org/html/rfc1624
15
16#![no_std]
17
18#[cfg(test)]
19extern crate alloc;
20
21// Optimizations applied:
22//
23// 0. Byteorder independence: as described in RFC 1071 section 2.(B)
24//    The sum of 16-bit integers can be computed in either byte order,
25//    so this actually saves us from the unnecessary byte swapping on
26//    an LE machine. As perfed on a gLinux workstation, that swapping
27//    can account for ~20% of the runtime.
28//
29// 1. Widen the accumulator: doing so enables us to process a bigger
30//    chunk of data once at a time, achieving some kind of poor man's
31//    SIMD. Currently a u128 accumulator is used.
32//
33// 2. Process more at a time: we add in increments of u64 rather than u16.
34//
35// 3. Induce the compiler to produce `adc` instruction: this is a very
36//    useful instruction to implement 1's complement addition and available
37//    on both x86 and ARM. The functions `adc_uXX` are for this use.
38
39/// Compute the checksum of "bytes".
40///
41/// `checksum(bytes)` is shorthand for:
42///
43/// ```rust
44/// # use internet_checksum::Checksum;
45/// # let bytes = &[];
46/// # let _ = {
47/// let mut c = Checksum::new();
48/// c.add_bytes(bytes);
49/// c.checksum()
50/// # };
51/// ```
52#[inline]
53pub fn checksum(bytes: &[u8]) -> [u8; 2] {
54    let mut c = Checksum::new();
55    c.add_bytes(bytes);
56    c.checksum()
57}
58
59/// Updates bytes in an existing checksum.
60///
61/// `update` updates a checksum to reflect that the already-checksummed bytes
62/// `old` have been removed and replaced with the bytes in `new`, which may not
63/// have the same length as `old`. It implements the algorithm described in
64/// Equation 3 in [RFC 1624]. The first byte must be at an even number offset in
65/// the original input. If an odd number offset byte needs to be updated, the
66/// caller should simply include the preceding byte as well. If an odd number of
67/// bytes is given, it is assumed that these are the last bytes of the input. If
68/// an odd number of bytes in the middle of the input needs to be updated, the
69/// preceding or following byte of the input should be added to make an even
70/// number of bytes.
71///
72/// [RFC 1624]: https://tools.ietf.org/html/rfc1624
73#[inline]
74fn update_internal(checksum: [u8; 2], old: &[u8], new: &[u8]) -> [u8; 2] {
75    // We compute on the sum, not the one's complement of the sum. checksum
76    // is the one's complement of the sum, so we need to get back to the
77    // sum. Thus, we negate checksum.
78    // HC' = ~HC
79    let mut sum = !u16::from_ne_bytes(checksum);
80
81    // Let's reuse `Checksum::add_bytes` to update our checksum
82    // so that we can get the speedup for free. Using
83    // [RFC 1071 Eqn. 3], we can efficiently update our new checksum.
84    let mut c1 = Checksum::new();
85    let mut c2 = Checksum::new();
86    c1.add_bytes(old);
87    c2.add_bytes(new);
88
89    // Note, `c1.checksum_inner()` is actually ~m in [Eqn. 3]
90    // `c2.checksum_inner()` is actually ~m' in [Eqn. 3]
91    // so we have to negate `c2.checksum_inner()` first to get m'.
92    // HC' += ~m, c1.checksum_inner() == ~m.
93    sum = adc_u16(sum, c1.checksum_inner());
94    // HC' += m', c2.checksum_inner() == ~m'.
95    sum = adc_u16(sum, !c2.checksum_inner());
96    // HC' = ~HC.
97    (!sum).to_ne_bytes()
98}
99
100/// Updates bytes in an existing checksum.
101///
102/// `update` updates a checksum to reflect that the already-checksummed bytes
103/// `old` have been updated to contain the values in `new`. It implements the
104/// algorithm described in Equation 3 in [RFC 1624]. The first byte must be at
105/// an even number offset in the original input. If an odd number offset byte
106/// needs to be updated, the caller should simply include the preceding byte as
107/// well. If an odd number of bytes is given, it is assumed that these are the
108/// last bytes of the input. If an odd number of bytes in the middle of the
109/// input needs to be updated, the preceding or following byte of the input
110/// should be added to make an even number of bytes.
111///
112/// # Panics
113///
114/// `update` panics if `old.len() != new.len()`.
115///
116/// [RFC 1624]: https://tools.ietf.org/html/rfc1624
117#[inline]
118pub fn update(checksum: [u8; 2], old: &[u8], new: &[u8]) -> [u8; 2] {
119    assert_eq!(old.len(), new.len());
120    update_internal(checksum, old, new)
121}
122
123/// Updates a checksum to reflect that the already-checksummed bytes `bytes`
124/// have been removed.
125///
126/// `remove` implements the algorithm described in [RFC 1624 Eqn. 3] for the
127/// special case where the replacement data is all zeroes. The first byte must
128/// be at an even number offset in the original input. If an odd number offset
129/// byte needs to be removed, the caller should include a preceding zero byte.
130/// If an odd number of bytes in the middle of the input needs to be removed, a
131/// preceding or following zero byte should be added to make an even number of
132/// bytes.
133///
134/// [RFC 1624]: https://tools.ietf.org/html/rfc1624
135#[inline]
136pub fn remove(checksum: [u8; 2], bytes: &[u8]) -> [u8; 2] {
137    update_internal(checksum, bytes, &[])
138}
139
140/// Updates a checksum to reflect that the bytes `bytes` have been added.
141///
142/// `add` implements the algorithm described in [RFC 1624 Eqn. 3] for the
143/// special case where the previous data was all zeroes. The first byte must be
144/// at an even number offset in the checksummed data, or else a zero byte must
145/// be prepended.
146///
147/// [RFC 1624]: https://tools.ietf.org/html/rfc1624
148#[inline]
149pub fn add(checksum: [u8; 2], bytes: &[u8]) -> [u8; 2] {
150    update_internal(checksum, &[], bytes)
151}
152
153/// RFC 1071 "internet checksum" computation.
154///
155/// `Checksum` implements the "internet checksum" defined in [RFC 1071] and
156/// updated in [RFC 1141] and [RFC 1624], which is used by many different
157/// protocols' packet formats. The checksum operates by computing the 1s
158/// complement of the 1s complement sum of successive 16-bit words of the input.
159///
160/// [RFC 1071]: https://tools.ietf.org/html/rfc1071
161/// [RFC 1141]: https://tools.ietf.org/html/rfc1141
162/// [RFC 1624]: https://tools.ietf.org/html/rfc1624
163#[derive(Default)]
164pub struct Checksum {
165    // Accumulate the sum into a u128, despite the fact that the `Checksum`
166    // implementation adds 8-byte or smaller chunks at a time. This effectively
167    // allows us to ignore overflow, which has been demonstrated to improve
168    // performance.
169    //
170    // Adding an 8-byte chunk to a u128 can be done safely without overflow up
171    // to u64::MAX times. Thus, we need not worry about overflow unless we were
172    // to checksum more than 8 * 2^64 bytes, or ~147 exabytes. We ignore this
173    // possibility.
174    sum: u128,
175    // Since odd-length inputs are treated specially, we store the trailing byte
176    // for use in future calls to add_bytes(), and only treat it as a true
177    // trailing byte in checksum().
178    trailing_byte: Option<u8>,
179}
180
181impl Checksum {
182    /// Initialize a new checksum.
183    #[inline]
184    pub const fn new() -> Self {
185        Checksum { sum: 0, trailing_byte: None }
186    }
187
188    /// Add bytes to the checksum.
189    ///
190    /// If `bytes` does not contain an even number of bytes, a single zero byte
191    /// will be added to the end before updating the checksum.
192    ///
193    /// Note that `add_bytes` has some fixed overhead regardless of the size of
194    /// `bytes`. Where performance is a concern, prefer fewer calls to
195    /// `add_bytes` with larger input over more calls with smaller input.
196    #[inline]
197    pub fn add_bytes(&mut self, mut bytes: &[u8]) {
198        if bytes.is_empty() {
199            return;
200        }
201
202        let mut sum = self.sum;
203
204        // Deal with previous trailing byte, if we have one.
205        // NB: Don't use `if let Some(t) = self.trailing_byte.take()`. It slows
206        // down the fast path (i.e. the `None` case).
207        if self.trailing_byte.is_some() {
208            let trailing = self.trailing_byte.take().unwrap();
209            sum += u16::from_ne_bytes([trailing, bytes[0]]) as u128;
210            bytes = &bytes[1..];
211        }
212
213        // NB: Even though our accumulator is 16 bytes, summing in 8 byte chunks
214        // (rather than 16 byte chunks) leads to better optimized machine code
215        // on 64 bit platforms.
216        while let Some(chunk) = bytes.first_chunk::<8>() {
217            sum += u64::from_ne_bytes(*chunk) as u128;
218            bytes = &bytes[8..];
219        }
220
221        // Handle the tail.
222        if let Some(chunk) = bytes.first_chunk::<4>() {
223            sum += u32::from_ne_bytes(*chunk) as u128;
224            bytes = &bytes[4..];
225        }
226        if let Some(chunk) = bytes.first_chunk::<2>() {
227            sum += u16::from_ne_bytes(*chunk) as u128;
228            bytes = &bytes[2..];
229        }
230        if bytes.len() == 1 {
231            // Stash the trailing byte.
232            self.trailing_byte = Some(bytes[0]);
233        }
234
235        self.sum = sum;
236    }
237
238    /// Computes the checksum, but in big endian byte order.
239    fn checksum_inner(&self) -> u16 {
240        let mut sum = self.sum;
241        if let Some(byte) = self.trailing_byte {
242            sum += u16::from_ne_bytes([byte, 0]) as u128;
243        }
244        !normalize(sum)
245    }
246
247    /// Computes the one's complement sum and returns the array representation.
248    ///
249    /// `partial_checksum` returns the one's complement sum of all data added
250    /// using `add_bytes` so far. Calling `partial_checksum` does *not* reset
251    /// the checksum. More bytes may be added after calling `partial_checksum`,
252    /// and they will be added to the checksum as expected.
253    ///
254    /// `partial_checksum` will return `None` if an odd number of bytes have
255    /// been added so far.
256    pub fn partial_checksum(&self) -> Option<[u8; 2]> {
257        if self.trailing_byte.is_some() {
258            return None;
259        }
260        Some(normalize(self.sum).to_ne_bytes())
261    }
262
263    /// Computes the checksum, and returns the array representation.
264    ///
265    /// `checksum` returns the checksum of all data added using `add_bytes` so
266    /// far. Calling `checksum` does *not* reset the checksum. More bytes may be
267    /// added after calling `checksum`, and they will be added to the checksum
268    /// as expected.
269    ///
270    /// If an odd number of bytes have been added so far, the checksum will be
271    /// computed as though a single 0 byte had been added at the end in order to
272    /// even out the length of the input.
273    #[inline]
274    pub fn checksum(&self) -> [u8; 2] {
275        self.checksum_inner().to_ne_bytes()
276    }
277}
278
279macro_rules! impl_adc {
280    ($name: ident, $t: ty) => {
281        /// implements 1's complement addition for $t,
282        /// exploiting the carry flag on a 2's complement machine.
283        /// In practice, the adc instruction will be generated.
284        fn $name(a: $t, b: $t) -> $t {
285            let (s, c) = a.overflowing_add(b);
286            s + (c as $t)
287        }
288    };
289}
290
291impl_adc!(adc_u16, u16);
292impl_adc!(adc_u32, u32);
293impl_adc!(adc_u64, u64);
294
295/// Normalizes the accumulator by mopping up the
296/// overflow until it fits in a `u16`.
297fn normalize(a: u128) -> u16 {
298    let t = adc_u64(a as u64, (a >> 64) as u64);
299    let t = adc_u32(t as u32, (t >> 32) as u32);
300    adc_u16(t as u16, (t >> 16) as u16)
301}
302
303#[cfg(test)]
304mod tests {
305    use alloc::vec::Vec;
306
307    use rand::{RngExt as _, SeedableRng as _};
308
309    use rand_xorshift::XorShiftRng;
310
311    use super::*;
312
313    /// Create a new deterministic RNG from a seed.
314    fn new_rng(mut seed: u128) -> XorShiftRng {
315        if seed == 0 {
316            // XorShiftRng can't take 0 seeds
317            seed = 1;
318        }
319        XorShiftRng::from_seed(seed.to_ne_bytes())
320    }
321
322    #[test]
323    fn test_checksum() {
324        for buf in IPV4_HEADERS {
325            // compute the checksum as normal
326            let mut c = Checksum::new();
327            c.add_bytes(&buf);
328            assert_eq!(c.checksum(), [0u8; 2]);
329            // compute the checksum one byte at a time to make sure our
330            // trailing_byte logic works
331            let mut c = Checksum::new();
332            for byte in *buf {
333                c.add_bytes(&[*byte]);
334            }
335            assert_eq!(c.checksum(), [0u8; 2]);
336
337            // Make sure that it works even if we overflow u32. Performing this
338            // loop 2 * 2^16 times is guaranteed to cause such an overflow
339            // because 0xFFFF + 0xFFFF > 2^16, and we're effectively adding
340            // (0xFFFF + 0xFFFF) 2^16 times. We verify the overflow as well by
341            // making sure that, at least once, the sum gets smaller from one
342            // loop iteration to the next.
343            let mut c = Checksum::new();
344            c.add_bytes(&[0xFF, 0xFF]);
345            for _ in 0..((2 * (1 << 16)) - 1) {
346                c.add_bytes(&[0xFF, 0xFF]);
347            }
348            assert_eq!(c.checksum(), [0u8; 2]);
349        }
350    }
351
352    #[test]
353    fn test_partial_checksum() {
354        for buf in IPV4_HEADERS {
355            // Partial checksum should compute for even length slices.
356            for i in (0..buf.len()).step_by(2) {
357                let mut part = Checksum::new();
358                part.add_bytes(&buf[..i]);
359
360                let mut c = Checksum::new();
361                c.add_bytes(
362                    &part
363                        .partial_checksum()
364                        .expect("partial checksum should compute for even length slices"),
365                );
366                c.add_bytes(&buf[i..]);
367                assert_eq!(c.checksum(), [0u8; 2]);
368            }
369            // Partial checksum should not compute for odd length slices.
370            for i in (1..buf.len()).step_by(2) {
371                let mut part = Checksum::new();
372                part.add_bytes(&buf[..i]);
373                assert_eq!(part.partial_checksum(), None);
374            }
375            // Partial checksum should be the complement of the checksum.
376            let mut c = Checksum::new();
377            c.add_bytes(buf);
378            assert_eq!(c.partial_checksum(), Some([0xFF; 2]));
379        }
380    }
381
382    #[test]
383    fn test_update() {
384        for b in IPV4_HEADERS {
385            let mut buf = Vec::new();
386            buf.extend_from_slice(b);
387
388            let mut c = Checksum::new();
389            c.add_bytes(&buf);
390            assert_eq!(c.checksum(), [0u8; 2]);
391
392            // replace the destination IP with the loopback address
393            let old = [buf[16], buf[17], buf[18], buf[19]];
394            (&mut buf[16..20]).copy_from_slice(&[127, 0, 0, 1]);
395            let updated = update(c.checksum(), &old, &[127, 0, 0, 1]);
396            let from_scratch = {
397                let mut c = Checksum::new();
398                c.add_bytes(&buf);
399                c.checksum()
400            };
401            assert_eq!(updated, from_scratch);
402        }
403    }
404
405    #[test]
406    fn test_update_noop() {
407        for b in IPV4_HEADERS {
408            let mut buf = Vec::new();
409            buf.extend_from_slice(b);
410
411            let mut c = Checksum::new();
412            c.add_bytes(&buf);
413            assert_eq!(c.checksum(), [0u8; 2]);
414
415            // Replace the destination IP with the same address. I.e. this
416            // update should be a no-op.
417            let old = [buf[16], buf[17], buf[18], buf[19]];
418            let updated = update(c.checksum(), &old, &old);
419            let from_scratch = {
420                let mut c = Checksum::new();
421                c.add_bytes(&buf);
422                c.checksum()
423            };
424            assert_eq!(updated, from_scratch);
425        }
426    }
427
428    #[test]
429    fn test_remove() {
430        for b in IPV4_HEADERS {
431            let mut buf = Vec::new();
432            buf.extend_from_slice(b);
433
434            let mut c = Checksum::new();
435            c.add_bytes(&buf);
436            let original_csum = c.checksum();
437
438            let removed = remove(original_csum, &buf[16..]);
439
440            let mut c2 = Checksum::new();
441            c2.add_bytes(&buf[..16]);
442            let expected_csum = c2.checksum();
443            assert_eq!(removed, expected_csum);
444        }
445    }
446
447    #[test]
448    fn test_add() {
449        for b in IPV4_HEADERS {
450            let mut buf = Vec::new();
451            buf.extend_from_slice(b);
452
453            let mut c = Checksum::new();
454            c.add_bytes(&buf);
455            let original_csum = c.checksum();
456
457            let new_bytes = [127, 0, 0, 1];
458            let added = add(original_csum, &new_bytes);
459
460            let mut c2 = Checksum::new();
461            c2.add_bytes(&buf);
462            c2.add_bytes(&new_bytes);
463            let expected_csum = c2.checksum();
464            assert_eq!(added, expected_csum);
465        }
466    }
467
468    #[test]
469    fn test_smoke_update() {
470        let mut rng = new_rng(70_812_476_915_813);
471
472        for _ in 0..2048 {
473            // use an odd length so we test the odd length logic
474            const BUF_LEN: usize = 31;
475            let buf: [u8; BUF_LEN] = rng.random();
476            let mut c = Checksum::new();
477            c.add_bytes(&buf);
478
479            let (begin, end) = loop {
480                let begin = rng.random_range(0..BUF_LEN);
481                let end = begin + (rng.random_range(0..(BUF_LEN + 1 - begin)));
482                // update requires that begin is even and end is either even or
483                // the end of the input
484                if begin % 2 == 0 && (end % 2 == 0 || end == BUF_LEN) {
485                    break (begin, end);
486                }
487            };
488
489            let mut new_buf = buf;
490            for i in begin..end {
491                new_buf[i] = rng.random();
492            }
493            let updated = update(c.checksum(), &buf[begin..end], &new_buf[begin..end]);
494            let from_scratch = {
495                let mut c = Checksum::new();
496                c.add_bytes(&new_buf);
497                c.checksum()
498            };
499            assert_eq!(updated, from_scratch);
500        }
501    }
502
503    /// IPv4 headers.
504    ///
505    /// This data was obtained by capturing live network traffic.
506    const IPV4_HEADERS: &[&[u8]] = &[
507        &[
508            0x45, 0x00, 0x00, 0x34, 0x00, 0x00, 0x40, 0x00, 0x40, 0x06, 0xae, 0xea, 0xc0, 0xa8,
509            0x01, 0x0f, 0xc0, 0xb8, 0x09, 0x6a,
510        ],
511        &[
512            0x45, 0x20, 0x00, 0x74, 0x5b, 0x6e, 0x40, 0x00, 0x37, 0x06, 0x5c, 0x1c, 0xc0, 0xb8,
513            0x09, 0x6a, 0xc0, 0xa8, 0x01, 0x0f,
514        ],
515        &[
516            0x45, 0x20, 0x02, 0x8f, 0x00, 0x00, 0x40, 0x00, 0x3b, 0x11, 0xc9, 0x3f, 0xac, 0xd9,
517            0x05, 0x6e, 0xc0, 0xa8, 0x01, 0x0f,
518        ],
519    ];
520
521    // This test checks that an input, found by a fuzzer, no longer causes a crash due to addition
522    // overflow.
523    #[test]
524    fn test_large_buffer_addition_overflow() {
525        let mut sum = Checksum { sum: 0, trailing_byte: None };
526        let bytes = [
527            0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
528            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
529            255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
530        ];
531        sum.add_bytes(&bytes[..]);
532    }
533
534    // Regression test for https://fxbug.dev/515774797.
535    //
536    // Verify that checksum calculations produce the same result, no matter if
537    // the bytes are added at once, or in odd-length chunks.
538    #[test]
539    fn test_odd_length_checksum() {
540        // Determine the expected value. Per RFC 1071, an odd length of bytes
541        // should be padded at the end with a 0.
542        let mut c = Checksum::new();
543        c.add_bytes(&[1, 2, 3, 0]);
544        let expected_checksum = c.checksum();
545
546        // Add the bytes all at once.
547        let mut c = Checksum::new();
548        c.add_bytes(&[1, 2, 3]);
549        assert_eq!(c.checksum(), expected_checksum);
550
551        // Add the bytes in two passes (first pass uses an odd number of bytes).
552        let mut c = Checksum::new();
553        c.add_bytes(&[1]);
554        c.add_bytes(&[2, 3]);
555        assert_eq!(c.checksum(), expected_checksum);
556    }
557
558    // Verify that we properly perform bounds checks against the byte buffer.
559    // Failure to do so would result in index-out-of-bounds panics.
560    #[test]
561    fn test_add_zero_bytes() {
562        let mut c = Checksum::new();
563        c.add_bytes(&[]);
564        assert_eq!(c.checksum(), [255, 255]);
565
566        // Try again, but this time set a trailing_byte.
567        let mut c = Checksum::new();
568        c.add_bytes(&[0]);
569        c.add_bytes(&[]);
570        assert_eq!(c.checksum(), [255, 255]);
571
572        // Try once more, but now complete the trailing byte exactly (no remainder).
573        let mut c = Checksum::new();
574        c.add_bytes(&[0]);
575        c.add_bytes(&[0]);
576        assert_eq!(c.checksum(), [255, 255]);
577    }
578
579    // Regression test for https://fxbug.dev/515753165.
580    //
581    // The checksum implementation manually tracks the carry bit during
582    // arithmetic overflows. Verify that we correctly handle the edge case where
583    // adding the carry bit from a previous overflow causes a second overflow to
584    // occur.
585    #[test]
586    fn test_carry_loss() {
587        const MAX: [u8; 16] = u128::MAX.to_ne_bytes();
588        const ONE: [u8; 16] = 1u128.to_ne_bytes();
589
590        let mut c1 = Checksum::new();
591        c1.add_bytes(&MAX);
592        c1.add_bytes(&ONE);
593        c1.add_bytes(&MAX);
594
595        let mut c2 = Checksum::new();
596        let bytes = [MAX, ONE, MAX].concat();
597        c2.add_bytes(&bytes);
598
599        assert_eq!(c1.checksum(), c2.checksum());
600    }
601}