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)]