Expose the RC4 cipher in the `insecure` module for legacy applications

This change exposes the RC4 cipher and also bumps the version of bindgen
from 0.53.2 to 0.54.0.

Change-Id: I23b957ad49666055c6e2bac7693b34014c150ee2
Reviewed-on: https://fuchsia-review.googlesource.com/c/mundane/+/404473
Reviewed-by: Joshua Liebow-Feeser <joshlf@google.com>
diff --git a/boringssl/bindgen.h b/boringssl/bindgen.h
index 6119132..d4b95d4 100644
--- a/boringssl/bindgen.h
+++ b/boringssl/bindgen.h
@@ -16,5 +16,6 @@
 #include <openssl/md5.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
+#include <openssl/rc4.h>
 #include <openssl/rsa.h>
 #include <openssl/sha.h>
diff --git a/boringssl/bindgen.sh b/boringssl/bindgen.sh
index bea5af2..11f40fa 100755
--- a/boringssl/bindgen.sh
+++ b/boringssl/bindgen.sh
@@ -98,6 +98,8 @@
 MD5_Transform|\
 MD5_Update|\
 RAND_bytes|\
+RC4|\
+RC4_set_key|\
 RSA_bits|\
 RSA_free|\
 RSA_generate_key_ex|\
@@ -145,6 +147,7 @@
 NID_sha256|\
 NID_sha384|\
 NID_sha512|\
+RC4_KEY|\
 RSA|\
 RSA_F4|\
 SHA_CTX|\
@@ -163,7 +166,7 @@
 # changing the Rust types that are generated for particular C types). If a more
 # recent version of bindgen is available, "roll" bindgen by updating the
 # `BINDGEN_EXPECTED_VERSION` variable here.
-BINDGEN_EXPECTED_VERSION="bindgen 0.53.2"
+BINDGEN_EXPECTED_VERSION="bindgen 0.54.0"
 BINDGEN_GOT_VERSION="$(bindgen --version)"
 if [ "$BINDGEN_GOT_VERSION" != "$BINDGEN_EXPECTED_VERSION" ]; then
     echo "Unexpected version of bindgen: got $BINDGEN_GOT_VERSION; wanted $BINDGEN_EXPECTED_VERSION.
diff --git a/boringssl/boringssl.rs b/boringssl/boringssl.rs
index 7d1fe73..ccc1abe 100644
--- a/boringssl/boringssl.rs
+++ b/boringssl/boringssl.rs
@@ -103,44 +103,9 @@
 pub const SHA384_DIGEST_LENGTH: u32 = 48;
 pub const SHA512_DIGEST_LENGTH: u32 = 64;
 pub type size_t = ::std::os::raw::c_ulong;
-#[repr(C)]
-#[derive(Copy, Clone)]
-pub struct _opaque_pthread_rwlock_t {
-    pub __sig: ::std::os::raw::c_long,
-    pub __opaque: [::std::os::raw::c_char; 192usize],
-}
-#[test]
-fn bindgen_test_layout__opaque_pthread_rwlock_t() {
-    assert_eq!(
-        ::std::mem::size_of::<_opaque_pthread_rwlock_t>(),
-        200usize,
-        concat!("Size of: ", stringify!(_opaque_pthread_rwlock_t))
-    );
-    assert_eq!(
-        ::std::mem::align_of::<_opaque_pthread_rwlock_t>(),
-        8usize,
-        concat!("Alignment of ", stringify!(_opaque_pthread_rwlock_t))
-    );
-    assert_eq!(
-        unsafe { &(*(::std::ptr::null::<_opaque_pthread_rwlock_t>())).__sig as *const _ as usize },
-        0usize,
-        concat!("Offset of field: ", stringify!(_opaque_pthread_rwlock_t), "::", stringify!(__sig))
-    );
-    assert_eq!(
-        unsafe {
-            &(*(::std::ptr::null::<_opaque_pthread_rwlock_t>())).__opaque as *const _ as usize
-        },
-        8usize,
-        concat!(
-            "Offset of field: ",
-            stringify!(_opaque_pthread_rwlock_t),
-            "::",
-            stringify!(__opaque)
-        )
-    );
-}
-pub type __darwin_pthread_rwlock_t = _opaque_pthread_rwlock_t;
-pub type pthread_rwlock_t = __darwin_pthread_rwlock_t;
+pub type __uint8_t = ::std::os::raw::c_uchar;
+pub type __uint32_t = ::std::os::raw::c_uint;
+pub type __uint64_t = ::std::os::raw::c_ulong;
 pub type BIGNUM = bignum_st;
 pub type BN_GENCB = bn_gencb_st;
 pub type BN_MONT_CTX = bn_mont_ctx_st;
@@ -198,12 +163,43 @@
 pub type EVP_PKEY = evp_pkey_st;
 pub type HMAC_CTX = hmac_ctx_st;
 pub type MD5_CTX = md5_state_st;
+pub type RC4_KEY = rc4_key_st;
 pub type RSA_METHOD = rsa_meth_st;
 pub type RSA = rsa_st;
 pub type SHA256_CTX = sha256_state_st;
 pub type SHA512_CTX = sha512_state_st;
 pub type SHA_CTX = sha_state_st;
-pub type CRYPTO_MUTEX = pthread_rwlock_t;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub union crypto_mutex_st {
+    pub alignment: f64,
+    pub padding: [u8; 56usize],
+    _bindgen_union_align: [u64; 7usize],
+}
+#[test]
+fn bindgen_test_layout_crypto_mutex_st() {
+    assert_eq!(
+        ::std::mem::size_of::<crypto_mutex_st>(),
+        56usize,
+        concat!("Size of: ", stringify!(crypto_mutex_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<crypto_mutex_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(crypto_mutex_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<crypto_mutex_st>())).alignment as *const _ as usize },
+        0usize,
+        concat!("Offset of field: ", stringify!(crypto_mutex_st), "::", stringify!(alignment))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<crypto_mutex_st>())).padding as *const _ as usize },
+        0usize,
+        concat!("Offset of field: ", stringify!(crypto_mutex_st), "::", stringify!(padding))
+    );
+}
+pub type CRYPTO_MUTEX = crypto_mutex_st;
 pub type CRYPTO_refcount_t = u32;
 extern "C" {
     #[link_name = "__RUST_MUNDANE_0_4_3_BN_init"]
@@ -1050,6 +1046,49 @@
     #[link_name = "__RUST_MUNDANE_0_4_3_RAND_bytes"]
     pub fn RAND_bytes(buf: *mut u8, len: size_t) -> ::std::os::raw::c_int;
 }
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct rc4_key_st {
+    pub x: u32,
+    pub y: u32,
+    pub data: [u32; 256usize],
+}
+#[test]
+fn bindgen_test_layout_rc4_key_st() {
+    assert_eq!(
+        ::std::mem::size_of::<rc4_key_st>(),
+        1032usize,
+        concat!("Size of: ", stringify!(rc4_key_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<rc4_key_st>(),
+        4usize,
+        concat!("Alignment of ", stringify!(rc4_key_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rc4_key_st>())).x as *const _ as usize },
+        0usize,
+        concat!("Offset of field: ", stringify!(rc4_key_st), "::", stringify!(x))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rc4_key_st>())).y as *const _ as usize },
+        4usize,
+        concat!("Offset of field: ", stringify!(rc4_key_st), "::", stringify!(y))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rc4_key_st>())).data as *const _ as usize },
+        8usize,
+        concat!("Offset of field: ", stringify!(rc4_key_st), "::", stringify!(data))
+    );
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_4_3_RC4_set_key"]
+    pub fn RC4_set_key(rc4key: *mut RC4_KEY, len: ::std::os::raw::c_uint, key: *const u8);
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_4_3_RC4"]
+    pub fn RC4(key: *mut RC4_KEY, len: size_t, in_: *const u8, out: *mut u8);
+}
 extern "C" {
     #[link_name = "__RUST_MUNDANE_0_4_3_RSA_new"]
     pub fn RSA_new() -> *mut RSA;
@@ -1286,7 +1325,7 @@
 }
 #[test]
 fn bindgen_test_layout_rsa_st() {
-    assert_eq!(::std::mem::size_of::<rsa_st>(), 376usize, concat!("Size of: ", stringify!(rsa_st)));
+    assert_eq!(::std::mem::size_of::<rsa_st>(), 232usize, concat!("Size of: ", stringify!(rsa_st)));
     assert_eq!(
         ::std::mem::align_of::<rsa_st>(),
         8usize,
@@ -1359,37 +1398,37 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).mont_n as *const _ as usize },
-        288usize,
+        144usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(mont_n))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).mont_p as *const _ as usize },
-        296usize,
+        152usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(mont_p))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).mont_q as *const _ as usize },
-        304usize,
+        160usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(mont_q))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).d_fixed as *const _ as usize },
-        312usize,
+        168usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(d_fixed))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).dmp1_fixed as *const _ as usize },
-        320usize,
+        176usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(dmp1_fixed))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).dmq1_fixed as *const _ as usize },
-        328usize,
+        184usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(dmq1_fixed))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).inv_small_mod_large_mont as *const _ as usize },
-        336usize,
+        192usize,
         concat!(
             "Offset of field: ",
             stringify!(rsa_st),
@@ -1399,17 +1438,17 @@
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).num_blindings as *const _ as usize },
-        344usize,
+        200usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(num_blindings))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).blindings as *const _ as usize },
-        352usize,
+        208usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(blindings))
     );
     assert_eq!(
         unsafe { &(*(::std::ptr::null::<rsa_st>())).blindings_inuse as *const _ as usize },
-        360usize,
+        216usize,
         concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(blindings_inuse))
     );
 }
diff --git a/src/boringssl/mod.rs b/src/boringssl/mod.rs
index 516dd56..4fb552a 100644
--- a/src/boringssl/mod.rs
+++ b/src/boringssl/mod.rs
@@ -64,9 +64,12 @@
 //   that property doesn't hold, it may cause UB - you are not guaranteed that
 //   it will be detected and an error will be returned.
 // - If a pointer parameter is const, the function does NOT take ownership of
-//   the object. If the pointer parameter is not const, it MAY take ownership
-//   depending on documentation. Generally, ownership is only taken if
-//   explicitly documented, but documentation bugs may exist, so be careful.
+//   the object and does NOT retain a reference to the object (in Rust
+//   terminology, the object need only live as long as the function call). If
+//   the pointer parameter is not const, it MAY take ownership or hold a
+//   reference depending on the documentation. Generally, ownership is only
+//   taken if explicitly documented, but documentation bugs may exist, so be
+//   careful.
 
 // NOTE(joshlf): It's important to define this module before the abort module,
 // or else all of the assertions that are auto-generated by bindgen would result
@@ -81,7 +84,7 @@
 
 // C types
 pub use boringssl::ffi::{
-    BIGNUM, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, MD5_CTX, RSA, RSA_F4,
+    BIGNUM, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, MD5_CTX, RC4_KEY, RSA, RSA_F4,
     SHA256_CTX, SHA512_CTX, SHA_CTX,
 };
 // C constants
@@ -100,7 +103,7 @@
 use std::mem::MaybeUninit;
 use std::num::NonZeroUsize;
 use std::os::raw::{c_char, c_int, c_uint, c_void};
-use std::{ptr, slice};
+use std::{cmp, ptr, slice};
 
 use boringssl::abort::UnwrapAbort;
 use boringssl::raw::{
@@ -111,7 +114,7 @@
     ED25519_verify, ERR_print_errors_cb, EVP_PBE_scrypt, EVP_PKEY_assign_EC_KEY,
     EVP_PKEY_assign_RSA, EVP_PKEY_get1_EC_KEY, EVP_PKEY_get1_RSA, EVP_marshal_public_key,
     EVP_parse_public_key, HMAC_CTX_copy, HMAC_CTX_init, HMAC_Final, HMAC_Init_ex, HMAC_Update,
-    HMAC_size, IntoSizeT, IntoUsize, RAND_bytes, RSA_bits, RSA_generate_key_ex,
+    HMAC_size, IntoSizeT, IntoUsize, RAND_bytes, RC4, RC4_set_key, RSA_bits, RSA_generate_key_ex,
     RSA_marshal_private_key, RSA_parse_private_key, RSA_sign_pss_mgf1, RSA_size,
     RSA_verify_pss_mgf1, SHA384_Init,
 };
@@ -270,9 +273,9 @@
 ///
 /// `ecdsa_sign` returns the number of bytes written to `sig`.
 ///
-/// # Panics
+/// # Aborts
 ///
-/// `ecdsa_sign` panics if `sig` is shorter than the minimum required signature
+/// `ecdsa_sign` aborts if `sig` is shorter than the minimum required signature
 /// size given by `ecdsa_size`, or if `key` doesn't have a group set.
 #[must_use]
 pub fn ecdsa_sign(
@@ -564,9 +567,9 @@
 
     /// The `HMAC_Final` function.
     ///
-    /// # Panics
+    /// # Aborts
     ///
-    /// `hmac_final` panics if `out` is not exactly the right length (as defined
+    /// `hmac_final` aborts if `out` is not exactly the right length (as defined
     /// by `HMAC_size`).
     pub fn hmac_final(&mut self, out: &mut [u8]) {
         unsafe {
@@ -606,6 +609,39 @@
     }
 }
 
+impl CStackWrapper<RC4_KEY> {
+    /// The `RC4_set_key` function.
+    ///
+    /// # Aborts
+    ///
+    /// `RC4_set_key` encodes the key length with `u32`, which may differ from
+    /// the target platform's word size (`usize`). This function aborts if the
+    /// length of the `key` slice exceeds `u32::MAX`.
+    pub fn rc4_set_key(key: &[u8]) -> Self {
+        let mut rc4 = RC4_KEY {
+            x: 0,
+            y: 0,
+            data: [0; 256],
+        };
+        unsafe {
+            // `RC4_set_key` reads `key` and writes into `rc4`. It does not take
+            // ownership of `key` and `key` need not live as long as `rc4`.
+            RC4_set_key(&mut rc4, key.len().try_into().unwrap_abort(), key.as_ptr());
+            CStackWrapper::new(rc4)
+        }
+    }
+
+    /// The `RC4` function.
+    pub fn rc4(&mut self, input: &[u8], output: &mut [u8]) {
+        let len = cmp::min(input.len(), output.len());
+        let input = &input[..len];
+        let output = &mut output[..len];
+        unsafe {
+            RC4(self.as_mut(), len.into_size_t(), input.as_ptr(), output.as_mut_ptr());
+        }
+    }
+}
+
 impl CHeapWrapper<RSA> {
     /// The `RSA_bits` function.
     #[must_use]
@@ -654,9 +690,9 @@
 
 /// The `RSA_sign` function.
 ///
-/// # Panics
+/// # Aborts
 ///
-/// `rsa_sign` panics if `sig` is shorter than the minimum required signature
+/// `rsa_sign` aborts if `sig` is shorter than the minimum required signature
 /// size given by `rsa_size`.
 #[cfg(feature = "rsa-pkcs1v15")]
 pub fn rsa_sign(
diff --git a/src/boringssl/raw.rs b/src/boringssl/raw.rs
index c51aed0..dbcc445 100644
--- a/src/boringssl/raw.rs
+++ b/src/boringssl/raw.rs
@@ -17,8 +17,8 @@
 // Infallible functions and the `size_t` type.
 pub use boringssl::ffi::{
     size_t, CBB_cleanup, CBB_len, CBS_init, CBS_len, CRYPTO_memcmp, EC_GROUP_get_curve_name,
-    ED25519_keypair, ED25519_keypair_from_seed, ERR_print_errors_cb, HMAC_CTX_init, HMAC_size,
-    RSA_bits,
+    ED25519_keypair, ED25519_keypair_from_seed, ERR_print_errors_cb, HMAC_CTX_init, HMAC_size, RC4,
+    RC4_set_key, RSA_bits,
 };
 
 use std::convert::TryInto;
@@ -492,6 +492,10 @@
     }
 }
 
+// rc4.h
+
+impl_traits!(RC4_KEY, CDestruct => _);
+
 // md5.h and sha.h
 
 unsafe impl CInit for ffi::MD5_CTX {
diff --git a/src/insecure.rs b/src/insecure.rs
index 4180115..d707bd8 100644
--- a/src/insecure.rs
+++ b/src/insecure.rs
@@ -28,3 +28,7 @@
 #[allow(deprecated)]
 #[cfg(all(feature = "kdf", feature = "insecure"))]
 pub use kdf::insecure_pbkdf2_hmac_sha1::insecure_pbkdf2_hmac_sha1;
+
+#[allow(deprecated)]
+#[cfg(feature = "insecure")]
+pub use insecure_rc4::InsecureRc4Key;
diff --git a/src/insecure_rc4.rs b/src/insecure_rc4.rs
new file mode 100644
index 0000000..2fb7f15
--- /dev/null
+++ b/src/insecure_rc4.rs
@@ -0,0 +1,87 @@
+#![cfg(feature = "insecure")]
+
+use boringssl::{self, CStackWrapper};
+
+/// INSECURE: The RC4 cipher.
+///
+/// # Security
+///
+/// RC4 is considered insecure and should only be used for compatibility with
+/// legacy applications.
+#[deprecated(note = "RC4 is considered insecure")]
+#[allow(deprecated)] // Work-around until Rust issue #56195 is resolved
+pub struct InsecureRc4Key {
+    ctx: CStackWrapper<boringssl::RC4_KEY>,
+}
+
+#[allow(deprecated)] // Work-around until Rust issue #56195 is resolved
+impl InsecureRc4Key {
+    /// INSECURE: Constructs an RC4 cipher from the given key data.
+    ///
+    /// The data used to construct an RC4 cipher can be of arbitrary length
+    /// (within the bounds of `u32`; see below). This includes zero-length keys,
+    /// for which care should be taken to avoid.
+    ///
+    /// # Security
+    ///
+    /// RC4 is considered insecure and should only be used for compatibility
+    /// with legacy applications.
+    ///
+    /// # Aborts
+    ///
+    /// This function aborts if the length of the `key` slice exceeds
+    /// `u32::MAX`.
+    #[deprecated(note = "RC4 is considered insecure")]
+    pub fn insecure_new(key: &[u8]) -> Self {
+        InsecureRc4Key {
+            ctx: CStackWrapper::rc4_set_key(key.as_ref()),
+        }
+    }
+
+    /// INSECURE: Encrypts or decrypts a byte slice into another byte slice.
+    ///
+    /// RC4 is a symmetrical streaming cipher; there is no distinction between
+    /// encryption and decryption.
+    ///
+    /// The minimum of the input and output slice lengths determines how much
+    /// data is read from `input` and written to `output`.
+    ///
+    /// # Security
+    ///
+    /// RC4 is considered insecure and should only be used for compatibility
+    /// with legacy applications.
+    #[deprecated(note = "RC4 is considered insecure")]
+    pub fn insecure_xor_stream(&mut self, input: &[u8], output: &mut [u8]) {
+        self.ctx.rc4(input, output);
+    }
+}
+
+#[allow(deprecated)] // Work-around until Rust issue #56195 is resolved
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // Compliments `rc4_decrypt`.
+    #[test]
+    fn rc4_encrypt() {
+        let mut rc4 = InsecureRc4Key::insecure_new(b"Key");
+
+        let plaintext = b"Plaintext";
+        let mut ciphertext = vec![0u8; 9];
+
+        rc4.insecure_xor_stream(plaintext, &mut ciphertext);
+        assert_eq!(&ciphertext, b"\xBB\xF3\x16\xE8\xD9\x40\xAF\x0A\xD3");
+    }
+
+    // Compliments `rc4_encrypt`.
+    #[test]
+    fn rc4_decrypt() {
+        let mut rc4 = InsecureRc4Key::insecure_new(b"Key");
+
+        let plaintext = b"\xBB\xF3\x16\xE8\xD9\x40\xAF\x0A\xD3";
+        let mut ciphertext = vec![0u8; 9];
+
+        rc4.insecure_xor_stream(plaintext, &mut ciphertext);
+        assert_eq!(&ciphertext, b"Plaintext");
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 3de2032..f8025a3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -62,6 +62,9 @@
 pub mod hmac;
 #[cfg(any(doc, feature = "insecure"))]
 #[forbid(unsafe_code)]
+mod insecure_rc4;
+#[cfg(any(doc, feature = "insecure"))]
+#[forbid(unsafe_code)]
 pub mod insecure;
 #[cfg(any(doc, feature = "kdf"))]
 #[forbid(unsafe_code)]