[rsa] Initial commit of rsa module

Change-Id: Iaea22a9e16701f3b73ce47ddac51115c5dc71a8e
diff --git a/boringssl/bindgen.h b/boringssl/bindgen.h
index 8297e68..16617c8 100644
--- a/boringssl/bindgen.h
+++ b/boringssl/bindgen.h
@@ -4,6 +4,7 @@
 // license that can be found in the LICENSE file or at
 // https://opensource.org/licenses/MIT.
 
+#include <openssl/bn.h>
 #include <openssl/bytestring.h>
 #include <openssl/curve25519.h>
 #include <openssl/ec.h>
@@ -14,4 +15,5 @@
 #include <openssl/hmac.h>
 #include <openssl/mem.h>
 #include <openssl/rand.h>
+#include <openssl/rsa.h>
 #include <openssl/sha.h>
diff --git a/boringssl/bindgen.sh b/boringssl/bindgen.sh
index 57e62b1..a8262b8 100755
--- a/boringssl/bindgen.sh
+++ b/boringssl/bindgen.sh
@@ -41,7 +41,10 @@
 # former for a consistency check of the postprocessing step which adds the
 # #[link_name...] attributes. Any change of the whitelist must be made to the
 # appropriate sub-list.
-WHITELIST_FUNCS="CBS_init|\
+WHITELIST_FUNCS="BN_free|\
+BN_init|\
+BN_set_u64|\
+CBS_init|\
 CBS_len|\
 CBB_init|\
 CBB_cleanup|\
@@ -74,7 +77,9 @@
 EVP_PKEY_free|\
 EVP_PKEY_up_ref|\
 EVP_PKEY_assign_EC_KEY|\
+EVP_PKEY_assign_RSA|\
 EVP_PKEY_get1_EC_KEY|\
+EVP_PKEY_get1_RSA|\
 EVP_parse_public_key|\
 EVP_marshal_public_key|\
 PKCS5_PBKDF2_HMAC|\
@@ -87,6 +92,18 @@
 HMAC_size|\
 CRYPTO_memcmp|\
 RAND_bytes|\
+RSA_bits|\
+RSA_free|\
+RSA_generate_key_ex|\
+RSA_marshal_private_key|\
+RSA_new|\
+RSA_parse_private_key|\
+RSA_sign|\
+RSA_sign_pss_mgf1|\
+RSA_size|\
+RSA_up_ref|\
+RSA_verify|\
+RSA_verify_pss_mgf1|\
 SHA1_Init|\
 SHA1_Update|\
 SHA1_Final|\
@@ -100,7 +117,9 @@
 SHA512_Update|\
 SHA512_Final"
 
-WHITELIST_OTHERS="CBB|\
+WHITELIST_OTHERS="BIGNUM|\
+BN_GENCB|\
+CBB|\
 CBS|\
 EC_GROUP|\
 EC_KEY|\
@@ -113,6 +132,12 @@
 NID_X9_62_prime256v1|\
 NID_secp384r1|\
 NID_secp521r1|\
+NID_sha1|\
+NID_sha256|\
+NID_sha384|\
+NID_sha512|\
+RSA|\
+RSA_F4|\
 SHA_CTX|\
 SHA_DIGEST_LENGTH|\
 SHA256_CTX|\
diff --git a/boringssl/boringssl.rs b/boringssl/boringssl.rs
index 4ae4e0b..36b54eb 100644
--- a/boringssl/boringssl.rs
+++ b/boringssl/boringssl.rs
@@ -19,16 +19,165 @@
 
 /* automatically generated by rust-bindgen */
 
+#[repr(C)]
+#[derive(Copy, Clone, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
+pub struct __BindgenBitfieldUnit<Storage, Align>
+where
+    Storage: AsRef<[u8]> + AsMut<[u8]>,
+{
+    storage: Storage,
+    align: [Align; 0],
+}
+
+impl<Storage, Align> __BindgenBitfieldUnit<Storage, Align>
+where
+    Storage: AsRef<[u8]> + AsMut<[u8]>,
+{
+    #[inline]
+    pub fn new(storage: Storage) -> Self {
+        Self { storage, align: [] }
+    }
+
+    #[inline]
+    pub fn get_bit(&self, index: usize) -> bool {
+        debug_assert!(index / 8 < self.storage.as_ref().len());
+
+        let byte_index = index / 8;
+        let byte = self.storage.as_ref()[byte_index];
+
+        let bit_index = if cfg!(target_endian = "big") {
+            7 - (index % 8)
+        } else {
+            index % 8
+        };
+
+        let mask = 1 << bit_index;
+
+        byte & mask == mask
+    }
+
+    #[inline]
+    pub fn set_bit(&mut self, index: usize, val: bool) {
+        debug_assert!(index / 8 < self.storage.as_ref().len());
+
+        let byte_index = index / 8;
+        let byte = &mut self.storage.as_mut()[byte_index];
+
+        let bit_index = if cfg!(target_endian = "big") {
+            7 - (index % 8)
+        } else {
+            index % 8
+        };
+
+        let mask = 1 << bit_index;
+        if val {
+            *byte |= mask;
+        } else {
+            *byte &= !mask;
+        }
+    }
+
+    #[inline]
+    pub fn get(&self, bit_offset: usize, bit_width: u8) -> u64 {
+        debug_assert!(bit_width <= 64);
+        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
+        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
+
+        let mut val = 0;
+
+        for i in 0..(bit_width as usize) {
+            if self.get_bit(i + bit_offset) {
+                let index = if cfg!(target_endian = "big") {
+                    bit_width as usize - 1 - i
+                } else {
+                    i
+                };
+                val |= 1 << index;
+            }
+        }
+
+        val
+    }
+
+    #[inline]
+    pub fn set(&mut self, bit_offset: usize, bit_width: u8, val: u64) {
+        debug_assert!(bit_width <= 64);
+        debug_assert!(bit_offset / 8 < self.storage.as_ref().len());
+        debug_assert!((bit_offset + (bit_width as usize)) / 8 <= self.storage.as_ref().len());
+
+        for i in 0..(bit_width as usize) {
+            let mask = 1 << i;
+            let val_bit_is_set = val & mask == mask;
+            let index = if cfg!(target_endian = "big") {
+                bit_width as usize - 1 - i
+            } else {
+                i
+            };
+            self.set_bit(index + bit_offset, val_bit_is_set);
+        }
+    }
+}
 pub const ED25519_PRIVATE_KEY_LEN: u32 = 64;
 pub const ED25519_PUBLIC_KEY_LEN: u32 = 32;
 pub const ED25519_SIGNATURE_LEN: u32 = 64;
+pub const NID_sha1: u32 = 64;
 pub const NID_X9_62_prime256v1: u32 = 415;
+pub const NID_sha256: u32 = 672;
+pub const NID_sha384: u32 = 673;
+pub const NID_sha512: u32 = 674;
 pub const NID_secp384r1: u32 = 715;
 pub const NID_secp521r1: u32 = 716;
+pub const RSA_F4: u32 = 65537;
 pub const SHA_DIGEST_LENGTH: u32 = 20;
 pub const SHA256_DIGEST_LENGTH: u32 = 32;
 pub const SHA384_DIGEST_LENGTH: u32 = 48;
 pub const SHA512_DIGEST_LENGTH: u32 = 64;
+#[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 BIGNUM = bignum_st;
+pub type BN_GENCB = bn_gencb_st;
+pub type BN_MONT_CTX = bn_mont_ctx_st;
 pub type CBB = cbb_st;
 pub type CBS = cbs_st;
 #[repr(C)]
@@ -82,15 +231,192 @@
 pub type EVP_PKEY_CTX = evp_pkey_ctx_st;
 pub type EVP_PKEY = evp_pkey_st;
 pub type HMAC_CTX = hmac_ctx_st;
-#[repr(C)]
-#[derive(Debug, Copy, Clone)]
-pub struct rsa_st {
-    _unused: [u8; 0],
-}
+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;
+pub type CRYPTO_refcount_t = u32;
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_BN_init"]
+    pub fn BN_init(bn: *mut BIGNUM);
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_BN_free"]
+    pub fn BN_free(bn: *mut BIGNUM);
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_BN_set_u64"]
+    pub fn BN_set_u64(bn: *mut BIGNUM, value: u64) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct bn_gencb_st {
+    pub arg: *mut ::std::os::raw::c_void,
+    pub callback: ::std::option::Option<
+        unsafe extern "C" fn(
+            event: ::std::os::raw::c_int,
+            n: ::std::os::raw::c_int,
+            arg1: *mut bn_gencb_st,
+        ) -> ::std::os::raw::c_int,
+    >,
+}
+#[test]
+fn bindgen_test_layout_bn_gencb_st() {
+    assert_eq!(
+        ::std::mem::size_of::<bn_gencb_st>(),
+        16usize,
+        concat!("Size of: ", stringify!(bn_gencb_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<bn_gencb_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(bn_gencb_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bn_gencb_st>())).arg as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bn_gencb_st),
+            "::",
+            stringify!(arg)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bn_gencb_st>())).callback as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bn_gencb_st),
+            "::",
+            stringify!(callback)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct bignum_st {
+    pub d: *mut u64,
+    pub width: ::std::os::raw::c_int,
+    pub dmax: ::std::os::raw::c_int,
+    pub neg: ::std::os::raw::c_int,
+    pub flags: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_bignum_st() {
+    assert_eq!(
+        ::std::mem::size_of::<bignum_st>(),
+        24usize,
+        concat!("Size of: ", stringify!(bignum_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<bignum_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(bignum_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bignum_st>())).d as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bignum_st),
+            "::",
+            stringify!(d)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bignum_st>())).width as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bignum_st),
+            "::",
+            stringify!(width)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bignum_st>())).dmax as *const _ as usize },
+        12usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bignum_st),
+            "::",
+            stringify!(dmax)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bignum_st>())).neg as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bignum_st),
+            "::",
+            stringify!(neg)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bignum_st>())).flags as *const _ as usize },
+        20usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bignum_st),
+            "::",
+            stringify!(flags)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct bn_mont_ctx_st {
+    pub RR: BIGNUM,
+    pub N: BIGNUM,
+    pub n0: [u64; 2usize],
+}
+#[test]
+fn bindgen_test_layout_bn_mont_ctx_st() {
+    assert_eq!(
+        ::std::mem::size_of::<bn_mont_ctx_st>(),
+        64usize,
+        concat!("Size of: ", stringify!(bn_mont_ctx_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<bn_mont_ctx_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(bn_mont_ctx_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bn_mont_ctx_st>())).RR as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bn_mont_ctx_st),
+            "::",
+            stringify!(RR)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bn_mont_ctx_st>())).N as *const _ as usize },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bn_mont_ctx_st),
+            "::",
+            stringify!(N)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<bn_mont_ctx_st>())).n0 as *const _ as usize },
+        48usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(bn_mont_ctx_st),
+            "::",
+            stringify!(n0)
+        )
+    );
+}
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
 pub struct cbs_st {
@@ -351,6 +677,83 @@
     #[link_name = "__RUST_MUNDANE_0_2_2_EC_curve_nid2nist"]
     pub fn EC_curve_nid2nist(nid: ::std::os::raw::c_int) -> *const ::std::os::raw::c_char;
 }
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct openssl_method_common_st {
+    pub references: ::std::os::raw::c_int,
+    pub is_static: ::std::os::raw::c_char,
+}
+#[test]
+fn bindgen_test_layout_openssl_method_common_st() {
+    assert_eq!(
+        ::std::mem::size_of::<openssl_method_common_st>(),
+        8usize,
+        concat!("Size of: ", stringify!(openssl_method_common_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<openssl_method_common_st>(),
+        4usize,
+        concat!("Alignment of ", stringify!(openssl_method_common_st))
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<openssl_method_common_st>())).references as *const _ as usize
+        },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(openssl_method_common_st),
+            "::",
+            stringify!(references)
+        )
+    );
+    assert_eq!(
+        unsafe {
+            &(*(::std::ptr::null::<openssl_method_common_st>())).is_static as *const _ as usize
+        },
+        4usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(openssl_method_common_st),
+            "::",
+            stringify!(is_static)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct stack_st_void {
+    _unused: [u8; 0],
+}
+pub type CRYPTO_EX_DATA = crypto_ex_data_st;
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct crypto_ex_data_st {
+    pub sk: *mut stack_st_void,
+}
+#[test]
+fn bindgen_test_layout_crypto_ex_data_st() {
+    assert_eq!(
+        ::std::mem::size_of::<crypto_ex_data_st>(),
+        8usize,
+        concat!("Size of: ", stringify!(crypto_ex_data_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<crypto_ex_data_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(crypto_ex_data_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<crypto_ex_data_st>())).sk as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(crypto_ex_data_st),
+            "::",
+            stringify!(sk)
+        )
+    );
+}
 extern "C" {
     #[link_name = "__RUST_MUNDANE_0_2_2_EC_KEY_new"]
     pub fn EC_KEY_new() -> *mut EC_KEY;
@@ -427,7 +830,6 @@
         ctx: *mut ::std::os::raw::c_void,
     );
 }
-pub type CRYPTO_refcount_t = u32;
 extern "C" {
     #[link_name = "__RUST_MUNDANE_0_2_2_EVP_sha1"]
     pub fn EVP_sha1() -> *const EVP_MD;
@@ -523,6 +925,14 @@
     pub fn EVP_PKEY_up_ref(pkey: *mut EVP_PKEY) -> ::std::os::raw::c_int;
 }
 extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_EVP_PKEY_assign_RSA"]
+    pub fn EVP_PKEY_assign_RSA(pkey: *mut EVP_PKEY, key: *mut RSA) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_EVP_PKEY_get1_RSA"]
+    pub fn EVP_PKEY_get1_RSA(pkey: *const EVP_PKEY) -> *mut RSA;
+}
+extern "C" {
     #[link_name = "__RUST_MUNDANE_0_2_2_EVP_PKEY_assign_EC_KEY"]
     pub fn EVP_PKEY_assign_EC_KEY(pkey: *mut EVP_PKEY, key: *mut EC_KEY) -> ::std::os::raw::c_int;
 }
@@ -812,6 +1222,533 @@
     pub fn RAND_bytes(buf: *mut u8, len: usize) -> ::std::os::raw::c_int;
 }
 extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_new"]
+    pub fn RSA_new() -> *mut RSA;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_free"]
+    pub fn RSA_free(rsa: *mut RSA);
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_up_ref"]
+    pub fn RSA_up_ref(rsa: *mut RSA) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_bits"]
+    pub fn RSA_bits(rsa: *const RSA) -> ::std::os::raw::c_uint;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_generate_key_ex"]
+    pub fn RSA_generate_key_ex(
+        rsa: *mut RSA,
+        bits: ::std::os::raw::c_int,
+        e: *const BIGNUM,
+        cb: *mut BN_GENCB,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_sign"]
+    pub fn RSA_sign(
+        hash_nid: ::std::os::raw::c_int,
+        in_: *const u8,
+        in_len: ::std::os::raw::c_uint,
+        out: *mut u8,
+        out_len: *mut ::std::os::raw::c_uint,
+        rsa: *mut RSA,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_sign_pss_mgf1"]
+    pub fn RSA_sign_pss_mgf1(
+        rsa: *mut RSA,
+        out_len: *mut usize,
+        out: *mut u8,
+        max_out: usize,
+        in_: *const u8,
+        in_len: usize,
+        md: *const EVP_MD,
+        mgf1_md: *const EVP_MD,
+        salt_len: ::std::os::raw::c_int,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_verify"]
+    pub fn RSA_verify(
+        hash_nid: ::std::os::raw::c_int,
+        msg: *const u8,
+        msg_len: usize,
+        sig: *const u8,
+        sig_len: usize,
+        rsa: *mut RSA,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_verify_pss_mgf1"]
+    pub fn RSA_verify_pss_mgf1(
+        rsa: *mut RSA,
+        msg: *const u8,
+        msg_len: usize,
+        md: *const EVP_MD,
+        mgf1_md: *const EVP_MD,
+        salt_len: ::std::os::raw::c_int,
+        sig: *const u8,
+        sig_len: usize,
+    ) -> ::std::os::raw::c_int;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_size"]
+    pub fn RSA_size(rsa: *const RSA) -> ::std::os::raw::c_uint;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_parse_private_key"]
+    pub fn RSA_parse_private_key(cbs: *mut CBS) -> *mut RSA;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_2_2_RSA_marshal_private_key"]
+    pub fn RSA_marshal_private_key(cbb: *mut CBB, rsa: *const RSA) -> ::std::os::raw::c_int;
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct rsa_meth_st {
+    pub common: openssl_method_common_st,
+    pub app_data: *mut ::std::os::raw::c_void,
+    pub init: ::std::option::Option<unsafe extern "C" fn(rsa: *mut RSA) -> ::std::os::raw::c_int>,
+    pub finish: ::std::option::Option<unsafe extern "C" fn(rsa: *mut RSA) -> ::std::os::raw::c_int>,
+    pub size: ::std::option::Option<unsafe extern "C" fn(rsa: *const RSA) -> usize>,
+    pub sign: ::std::option::Option<
+        unsafe extern "C" fn(
+            type_: ::std::os::raw::c_int,
+            m: *const u8,
+            m_length: ::std::os::raw::c_uint,
+            sigret: *mut u8,
+            siglen: *mut ::std::os::raw::c_uint,
+            rsa: *const RSA,
+        ) -> ::std::os::raw::c_int,
+    >,
+    pub sign_raw: ::std::option::Option<
+        unsafe extern "C" fn(
+            rsa: *mut RSA,
+            out_len: *mut usize,
+            out: *mut u8,
+            max_out: usize,
+            in_: *const u8,
+            in_len: usize,
+            padding: ::std::os::raw::c_int,
+        ) -> ::std::os::raw::c_int,
+    >,
+    pub decrypt: ::std::option::Option<
+        unsafe extern "C" fn(
+            rsa: *mut RSA,
+            out_len: *mut usize,
+            out: *mut u8,
+            max_out: usize,
+            in_: *const u8,
+            in_len: usize,
+            padding: ::std::os::raw::c_int,
+        ) -> ::std::os::raw::c_int,
+    >,
+    pub private_transform: ::std::option::Option<
+        unsafe extern "C" fn(
+            rsa: *mut RSA,
+            out: *mut u8,
+            in_: *const u8,
+            len: usize,
+        ) -> ::std::os::raw::c_int,
+    >,
+    pub flags: ::std::os::raw::c_int,
+}
+#[test]
+fn bindgen_test_layout_rsa_meth_st() {
+    assert_eq!(
+        ::std::mem::size_of::<rsa_meth_st>(),
+        80usize,
+        concat!("Size of: ", stringify!(rsa_meth_st))
+    );
+    assert_eq!(
+        ::std::mem::align_of::<rsa_meth_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(rsa_meth_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).common as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(common)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).app_data as *const _ as usize },
+        8usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(app_data)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).init as *const _ as usize },
+        16usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(init)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).finish as *const _ as usize },
+        24usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(finish)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).size as *const _ as usize },
+        32usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(size)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).sign as *const _ as usize },
+        40usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(sign)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).sign_raw as *const _ as usize },
+        48usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(sign_raw)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).decrypt as *const _ as usize },
+        56usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(decrypt)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).private_transform as *const _ as usize },
+        64usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(private_transform)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_meth_st>())).flags as *const _ as usize },
+        72usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_meth_st),
+            "::",
+            stringify!(flags)
+        )
+    );
+}
+#[repr(C)]
+#[derive(Debug, Copy, Clone)]
+pub struct bn_blinding_st {
+    _unused: [u8; 0],
+}
+pub type BN_BLINDING = bn_blinding_st;
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct rsa_st {
+    pub meth: *mut RSA_METHOD,
+    pub n: *mut BIGNUM,
+    pub e: *mut BIGNUM,
+    pub d: *mut BIGNUM,
+    pub p: *mut BIGNUM,
+    pub q: *mut BIGNUM,
+    pub dmp1: *mut BIGNUM,
+    pub dmq1: *mut BIGNUM,
+    pub iqmp: *mut BIGNUM,
+    pub ex_data: CRYPTO_EX_DATA,
+    pub references: CRYPTO_refcount_t,
+    pub flags: ::std::os::raw::c_int,
+    pub lock: CRYPTO_MUTEX,
+    pub mont_n: *mut BN_MONT_CTX,
+    pub mont_p: *mut BN_MONT_CTX,
+    pub mont_q: *mut BN_MONT_CTX,
+    pub d_fixed: *mut BIGNUM,
+    pub dmp1_fixed: *mut BIGNUM,
+    pub dmq1_fixed: *mut BIGNUM,
+    pub inv_small_mod_large_mont: *mut BIGNUM,
+    pub num_blindings: ::std::os::raw::c_uint,
+    pub blindings: *mut *mut BN_BLINDING,
+    pub blindings_inuse: *mut ::std::os::raw::c_uchar,
+    pub _bitfield_1: __BindgenBitfieldUnit<[u8; 1usize], u8>,
+    pub __bindgen_padding_0: [u8; 7usize],
+}
+#[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::align_of::<rsa_st>(),
+        8usize,
+        concat!("Alignment of ", stringify!(rsa_st))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).meth as *const _ as usize },
+        0usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(meth)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).n as *const _ as usize },
+        8usize,
+        concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(n))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).e as *const _ as usize },
+        16usize,
+        concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(e))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).d as *const _ as usize },
+        24usize,
+        concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(d))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).p as *const _ as usize },
+        32usize,
+        concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(p))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).q as *const _ as usize },
+        40usize,
+        concat!("Offset of field: ", stringify!(rsa_st), "::", stringify!(q))
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).dmp1 as *const _ as usize },
+        48usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(dmp1)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).dmq1 as *const _ as usize },
+        56usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(dmq1)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).iqmp as *const _ as usize },
+        64usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(iqmp)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).ex_data as *const _ as usize },
+        72usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(ex_data)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).references as *const _ as usize },
+        80usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(references)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).flags as *const _ as usize },
+        84usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(flags)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).lock as *const _ as usize },
+        88usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(lock)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).mont_n as *const _ as usize },
+        288usize,
+        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,
+        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,
+        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,
+        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,
+        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,
+        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,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(inv_small_mod_large_mont)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).num_blindings as *const _ as usize },
+        344usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(num_blindings)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).blindings as *const _ as usize },
+        352usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(blindings)
+        )
+    );
+    assert_eq!(
+        unsafe { &(*(::std::ptr::null::<rsa_st>())).blindings_inuse as *const _ as usize },
+        360usize,
+        concat!(
+            "Offset of field: ",
+            stringify!(rsa_st),
+            "::",
+            stringify!(blindings_inuse)
+        )
+    );
+}
+impl rsa_st {
+    #[inline]
+    pub fn private_key_frozen(&self) -> ::std::os::raw::c_uint {
+        unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 1u8) as u32) }
+    }
+    #[inline]
+    pub fn set_private_key_frozen(&mut self, val: ::std::os::raw::c_uint) {
+        unsafe {
+            let val: u32 = ::std::mem::transmute(val);
+            self._bitfield_1.set(0usize, 1u8, val as u64)
+        }
+    }
+    #[inline]
+    pub fn new_bitfield_1(
+        private_key_frozen: ::std::os::raw::c_uint,
+    ) -> __BindgenBitfieldUnit<[u8; 1usize], u8> {
+        let mut __bindgen_bitfield_unit: __BindgenBitfieldUnit<[u8; 1usize], u8> =
+            Default::default();
+        __bindgen_bitfield_unit.set(0usize, 1u8, {
+            let private_key_frozen: u32 = unsafe { ::std::mem::transmute(private_key_frozen) };
+            private_key_frozen as u64
+        });
+        __bindgen_bitfield_unit
+    }
+}
+extern "C" {
     #[link_name = "__RUST_MUNDANE_0_2_2_SHA1_Init"]
     pub fn SHA1_Init(sha: *mut SHA_CTX) -> ::std::os::raw::c_int;
 }
diff --git a/src/boringssl/mod.rs b/src/boringssl/mod.rs
index 56d280d..3b95135 100644
--- a/src/boringssl/mod.rs
+++ b/src/boringssl/mod.rs
@@ -63,6 +63,10 @@
 // - Any BoringSSL documentation that says "x property must hold" means that, if
 //   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.
 
 // 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
@@ -77,17 +81,19 @@
 
 // C types
 pub use boringssl::ffi::{
-    CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, SHA256_CTX, SHA512_CTX, SHA_CTX,
+    BIGNUM, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, RSA, RSA_F4, SHA256_CTX,
+    SHA512_CTX, SHA_CTX,
 };
 // C constants
 pub use boringssl::ffi::{
-    NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, ED25519_PRIVATE_KEY_LEN,
-    ED25519_PUBLIC_KEY_LEN, ED25519_SIGNATURE_LEN, SHA256_DIGEST_LENGTH, SHA384_DIGEST_LENGTH,
-    SHA512_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
+    NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1, NID_sha1, NID_sha256, NID_sha384,
+    NID_sha512, ED25519_PRIVATE_KEY_LEN, ED25519_PUBLIC_KEY_LEN, ED25519_SIGNATURE_LEN,
+    SHA256_DIGEST_LENGTH, SHA384_DIGEST_LENGTH, SHA512_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
 };
 // wrapper types
 pub use boringssl::wrapper::{CHeapWrapper, CRef, CStackWrapper};
 
+use std::convert::TryInto;
 use std::ffi::CStr;
 use std::fmt::{self, Debug, Display, Formatter};
 use std::num::NonZeroUsize;
@@ -96,15 +102,25 @@
 
 use boringssl::abort::UnwrapAbort;
 use boringssl::raw::{
-    CBB_data, CBB_init, CBB_len, CBS_init, CBS_len, CRYPTO_memcmp, ECDSA_sign, ECDSA_size,
-    ECDSA_verify, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_generate_key,
-    EC_KEY_get0_group, EC_KEY_marshal_private_key, EC_KEY_parse_private_key, EC_KEY_set_group,
-    EC_curve_nid2nist, ED25519_keypair, ED25519_keypair_from_seed, ED25519_sign, ED25519_verify,
-    ERR_print_errors_cb, EVP_PBE_scrypt, EVP_PKEY_assign_EC_KEY, EVP_PKEY_get1_EC_KEY,
-    EVP_marshal_public_key, EVP_parse_public_key, HMAC_CTX_init, HMAC_Final, HMAC_Init_ex,
-    HMAC_Update, HMAC_size, RAND_bytes, SHA384_Init,
+    BN_set_u64, CBB_data, CBB_init, CBB_len, CBS_init, CBS_len, CRYPTO_memcmp, ECDSA_sign,
+    ECDSA_size, ECDSA_verify, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name,
+    EC_KEY_generate_key, EC_KEY_get0_group, EC_KEY_marshal_private_key, EC_KEY_parse_private_key,
+    EC_KEY_set_group, EC_curve_nid2nist, ED25519_keypair, ED25519_keypair_from_seed, ED25519_sign,
+    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_init, HMAC_Final, HMAC_Init_ex, HMAC_Update, HMAC_size,
+    RAND_bytes, RSA_bits, RSA_generate_key_ex, RSA_marshal_private_key, RSA_parse_private_key,
+    RSA_size, SHA384_Init,
 };
 
+impl CStackWrapper<BIGNUM> {
+    /// The `BN_set_u64` function.
+    #[must_use]
+    pub fn bn_set_u64(&mut self, value: u64) -> Result<(), BoringError> {
+        unsafe { BN_set_u64(self.as_mut(), value) }
+    }
+}
+
 impl CStackWrapper<CBB> {
     /// Creates a new `CBB` and initializes it with `CBB_init`.
     ///
@@ -276,8 +292,9 @@
         )?;
         // ECDSA_sign guarantees that it only needs ECDSA_size bytes for the
         // signature.
-        assert_abort!(sig_len as usize <= min_size.get());
-        Ok(sig_len as usize)
+        let sig_len = sig_len.try_into().unwrap_abort();
+        assert_abort!(sig_len <= min_size.get());
+        Ok(sig_len)
     }
 }
 
@@ -373,6 +390,19 @@
         }
     }
 
+    /// The `EVP_PKEY_assign_RSA` function.
+    pub fn evp_pkey_assign_rsa(&mut self, rsa: CHeapWrapper<RSA>) {
+        unsafe {
+            // NOTE: It's very important that we use 'into_mut' here so that
+            // rsa's refcount is not decremented. That's because
+            // EVP_PKEY_assign_RSA doesn't increment the refcount of its
+            // argument.
+            let key = rsa.into_mut();
+            // EVP_PKEY_assign_RSA only fails if key is NULL.
+            EVP_PKEY_assign_RSA(self.as_mut(), key).unwrap_abort()
+        }
+    }
+
     /// The `EVP_PKEY_get1_EC_KEY` function.
     #[must_use]
     pub fn evp_pkey_get1_ec_key(&mut self) -> Result<CHeapWrapper<EC_KEY>, BoringError> {
@@ -380,6 +410,14 @@
         // refcount of the EC_KEY before returning a pointer to it.
         unsafe { Ok(CHeapWrapper::new_from(EVP_PKEY_get1_EC_KEY(self.as_mut())?)) }
     }
+
+    /// The `EVP_PKEY_get1_RSA` function.
+    #[must_use]
+    pub fn evp_pkey_get1_rsa(&mut self) -> Result<CHeapWrapper<RSA>, BoringError> {
+        // NOTE: It's important that we use get1 here, as it increments the
+        // refcount of the RSA key before returning a pointer to it.
+        unsafe { Ok(CHeapWrapper::new_from(EVP_PKEY_get1_RSA(self.as_mut())?)) }
+    }
 }
 
 /// The `EVP_PBE_scrypt` function.
@@ -543,6 +581,52 @@
     }
 }
 
+impl CHeapWrapper<RSA> {
+    /// The `RSA_bits` function.
+    #[must_use]
+    pub fn rsa_bits(&self) -> c_uint {
+        // RSA_bits does not mutate its argument but, for
+        // backwards-compatibility reasons, continues to take a normal
+        // (non-const) pointer.
+        unsafe { RSA_bits(self.as_const() as *mut _) }
+    }
+
+    /// The `RSA_generate_key_ex` function.
+    #[must_use]
+    pub fn rsa_generate_key_ex(
+        &mut self,
+        bits: c_int,
+        e: &CRef<BIGNUM>,
+    ) -> Result<(), BoringError> {
+        unsafe {
+            // NOTE: It's very important that we use 'into_mut' here so that e's
+            // refcount is not decremented. That's because RSA_generate_key_ex
+            // takes ownership of e, and thus doesn't increment its refcount.
+            RSA_generate_key_ex(self.as_mut(), bits, e.as_const(), ptr::null_mut())
+        }
+    }
+
+    /// The `RSA_marshal_private_key` function.
+    #[must_use]
+    pub fn rsa_marshal_private_key(&self, cbb: &mut CStackWrapper<CBB>) -> Result<(), BoringError> {
+        unsafe { RSA_marshal_private_key(cbb.as_mut(), self.as_const()) }
+    }
+
+    /// The `RSA_parse_private_key` function.
+    #[must_use]
+    pub fn rsa_parse_private_key(
+        cbs: &mut CStackWrapper<CBS>,
+    ) -> Result<CHeapWrapper<RSA>, BoringError> {
+        unsafe { Ok(CHeapWrapper::new_from(RSA_parse_private_key(cbs.as_mut())?)) }
+    }
+
+    /// The `RSA_size` function.
+    #[must_use]
+    pub fn rsa_size(&self) -> Result<NonZeroUsize, BoringError> {
+        unsafe { RSA_size(self.as_const()) }
+    }
+}
+
 /// Implements `CStackWrapper` for a hash context type.
 ///
 /// The caller provides doc comments, a public method name, and a private
diff --git a/src/boringssl/raw.rs b/src/boringssl/raw.rs
index d8b212b..0251597 100644
--- a/src/boringssl/raw.rs
+++ b/src/boringssl/raw.rs
@@ -18,17 +18,35 @@
 pub use boringssl::ffi::{
     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,
 };
 
+use std::convert::TryInto;
 use std::num::NonZeroUsize;
 use std::os::raw::{c_char, c_int, c_uint, c_void};
 use std::ptr::{self, NonNull};
 
-use boringssl::ffi::{self, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, SHA512_CTX};
+use boringssl::ffi::{
+    self, BIGNUM, BN_GENCB, CBB, CBS, EC_GROUP, EC_KEY, EVP_MD, EVP_PKEY, HMAC_CTX, RSA, SHA512_CTX,
+};
 
+use boringssl::abort::UnwrapAbort;
 use boringssl::wrapper::CInit;
 use boringssl::BoringError;
 
+// bn.h
+
+// BIGNUMs can be either heap- or stack-allocated, and they keep track of which
+// they are internally so that BN_free does the right thing - freeing the object
+// itself if heap-allocated, and only freeing its internal state otherwise.
+impl_traits!(BIGNUM, CInit => BN_init, CDestruct => BN_free);
+
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn BN_set_u64(bn: *mut BIGNUM, value: u64) -> Result<(), BoringError> {
+    one_or_err("BN_set_u64", ffi::BN_set_u64(bn, value))
+}
+
 // bytestring.h
 
 impl_traits!(CBB, CDestruct => CBB_cleanup);
@@ -252,11 +270,23 @@
 
 #[allow(non_snake_case)]
 #[must_use]
+pub unsafe fn EVP_PKEY_assign_RSA(pkey: *mut EVP_PKEY, key: *mut RSA) -> Result<(), BoringError> {
+    one_or_err("EVP_PKEY_assign_RSA", ffi::EVP_PKEY_assign_RSA(pkey, key))
+}
+
+#[allow(non_snake_case)]
+#[must_use]
 pub unsafe fn EVP_PKEY_get1_EC_KEY(pkey: *mut EVP_PKEY) -> Result<NonNull<EC_KEY>, BoringError> {
     ptr_or_err("EVP_PKEY_get1_EC_KEY", ffi::EVP_PKEY_get1_EC_KEY(pkey))
 }
 
 #[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn EVP_PKEY_get1_RSA(pkey: *mut EVP_PKEY) -> Result<NonNull<RSA>, BoringError> {
+    ptr_or_err("EVP_PKEY_get1_RSA", ffi::EVP_PKEY_get1_RSA(pkey))
+}
+
+#[allow(non_snake_case)]
 #[allow(clippy::too_many_arguments)]
 #[must_use]
 pub unsafe fn EVP_PBE_scrypt(
@@ -365,6 +395,46 @@
     assert_abort_eq!(ffi::RAND_bytes(buf, len), 1);
 }
 
+// rsa.h
+
+impl_traits!(RSA, CNew => RSA_new, CUpRef => RSA_up_ref, CFree => RSA_free);
+
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn RSA_generate_key_ex(
+    rsa: *mut RSA,
+    bits: c_int,
+    e: *const BIGNUM,
+    cb: *mut BN_GENCB,
+) -> Result<(), BoringError> {
+    one_or_err(
+        "RSA_generate_key_ex",
+        ffi::RSA_generate_key_ex(rsa, bits, e, cb),
+    )
+}
+
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn RSA_marshal_private_key(cbb: *mut CBB, rsa: *const RSA) -> Result<(), BoringError> {
+    one_or_err(
+        "RSA_marshal_private_key",
+        ffi::RSA_marshal_private_key(cbb, rsa),
+    )
+}
+
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn RSA_parse_private_key(cbs: *mut CBS) -> Result<NonNull<RSA>, BoringError> {
+    ptr_or_err("RSA_parse_private_key", ffi::RSA_parse_private_key(cbs))
+}
+
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn RSA_size(key: *const RSA) -> Result<NonZeroUsize, BoringError> {
+    NonZeroUsize::new(ffi::RSA_size(key).try_into().unwrap_abort())
+        .ok_or_else(|| BoringError::consume_stack("RSA_size"))
+}
+
 // sha.h
 
 #[allow(non_snake_case)]
diff --git a/src/boringssl/wrapper.rs b/src/boringssl/wrapper.rs
index 864a8b0..91ff668 100644
--- a/src/boringssl/wrapper.rs
+++ b/src/boringssl/wrapper.rs
@@ -322,6 +322,11 @@
     }
 
     #[must_use]
+    pub fn as_c_ref(&mut self) -> CRef<C> {
+        unsafe { CRef::new(NonNull::new_unchecked(&mut self.obj as *mut C)) }
+    }
+
+    #[must_use]
     pub fn as_mut(&mut self) -> *mut C {
         &mut self.obj
     }
diff --git a/src/hash.rs b/src/hash.rs
index 39ecef6..a42da6b 100644
--- a/src/hash.rs
+++ b/src/hash.rs
@@ -11,10 +11,13 @@
 use boringssl::{self, CStackWrapper};
 
 pub(crate) mod inner {
+    use std::os::raw::c_int;
+
     use boringssl::{CRef, EVP_MD};
 
     pub trait Hasher {
         fn evp_md() -> CRef<'static, EVP_MD>;
+        fn nid() -> c_int;
     }
 
     pub trait Digest {
@@ -176,7 +179,7 @@
 /// For the digest type, the traits `PartialEq`, `Eq`, `Display`, and `Debug`
 /// are also implemented.
 macro_rules! impl_hash {
-    ($name:ident, $digest_name:path, $digest_len:path, $update:ident, $final:ident, $evp_md:ident) => {
+    ($name:ident, $digest_name:path, $digest_len:path, $update:ident, $final:ident, $evp_md:ident, $nid:ident) => {
         #[allow(deprecated)]
         impl ::util::Sealed for $name {}
         #[allow(deprecated)]
@@ -196,6 +199,10 @@
             fn evp_md() -> ::boringssl::CRef<'static, ::boringssl::EVP_MD> {
                 ::boringssl::CRef::$evp_md()
             }
+            fn nid() -> ::std::os::raw::c_int {
+                use std::convert::TryInto;
+                ::boringssl::$nid.try_into().unwrap()
+            }
         }
         #[allow(deprecated)]
         impl self::inner::Digest for $digest_name {
@@ -254,7 +261,8 @@
     boringssl::SHA_DIGEST_LENGTH,
     sha1_update,
     sha1_final,
-    evp_sha1
+    evp_sha1,
+    NID_sha1
 );
 impl_hash!(
     Sha256,
@@ -262,7 +270,8 @@
     boringssl::SHA256_DIGEST_LENGTH,
     sha256_update,
     sha256_final,
-    evp_sha256
+    evp_sha256,
+    NID_sha256
 );
 impl_hash!(
     Sha384,
@@ -270,7 +279,8 @@
     boringssl::SHA384_DIGEST_LENGTH,
     sha384_update,
     sha384_final,
-    evp_sha384
+    evp_sha384,
+    NID_sha384
 );
 impl_hash!(
     Sha512,
@@ -278,7 +288,8 @@
     boringssl::SHA512_DIGEST_LENGTH,
     sha512_update,
     sha512_final,
-    evp_sha512
+    evp_sha512,
+    NID_sha512
 );
 
 #[cfg(test)]
diff --git a/src/lib.rs b/src/lib.rs
index b4e71e1..a58c679 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -46,6 +46,11 @@
 // just in case we forget to add #[forbid(unsafe_code)] on new module
 // definitions
 #![deny(unsafe_code)]
+// NOTE(joshlf): This is the only feature we allow. It keeps us from working on
+// stable Rust, but that's justified by the fact that it's important for
+// ensuring the soundness of our unsafe code. Once this feature is stabilized,
+// we will promise to work on stable going forward.
+#![feature(try_from)]
 
 #[macro_use]
 mod macros;
diff --git a/src/public/mod.rs b/src/public/mod.rs
index 171e9d4..7ec6643 100644
--- a/src/public/mod.rs
+++ b/src/public/mod.rs
@@ -8,6 +8,7 @@
 
 pub mod ec;
 pub mod ed25519;
+pub mod rsa;
 
 use boringssl::{CHeapWrapper, CStackWrapper};
 use public::inner::BoringDerKey;
diff --git a/src/public/rsa/bits.rs b/src/public/rsa/bits.rs
new file mode 100644
index 0000000..e0a8d7c
--- /dev/null
+++ b/src/public/rsa/bits.rs
@@ -0,0 +1,77 @@
+// Copyright 2018 Google LLC
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+use std::fmt::{self, Debug, Display, Formatter};
+
+use util::Sealed;
+
+/// The bit length of an RSA key.
+///
+/// The [`RsaPrivKey`] and [`RsaPubKey`] types take a `B: RsaKeyBits` type
+/// parameter indicating the key's length in bits.
+///
+/// We only support bit lengths of 2048 or greater, as smaller bit lengths are
+/// considered insecure. If 2048 is considered insecure at some point in the
+/// future, then we will remove support for it, which will be a breaking change.
+///
+/// [`RsaPrivKey`]: ::public::rsa::RsaPrivKey
+/// [`RsaPubKey`]: ::public::rsa::RsaPubKey
+pub trait RsaKeyBits: Sized + Copy + Clone + Default + Display + Debug + Sealed {
+    /// The number of bits.
+    const BITS: usize;
+}
+
+/// 2048 bits.
+///
+/// `B2048` indicates a 2048-bit RSA key; it implements [`RsaKeyBits`].
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
+pub struct B2048;
+
+/// 3072 bits.
+///
+/// `B3072` indicates a 3072-bit RSA key; it implements [`RsaKeyBits`].
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
+pub struct B3072;
+
+/// 4096 bits.
+///
+/// `B4096` indicates a 4096-bit RSA key; it implements [`RsaKeyBits`].
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
+pub struct B4096;
+
+/// 6144 bits.
+///
+/// `B6144` indicates a 6144-bit RSA key; it implements [`RsaKeyBits`].
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
+pub struct B6144;
+
+/// 8192 bits.
+///
+/// `B8192` indicates a 8192-bit RSA key; it implements [`RsaKeyBits`].
+#[derive(Copy, Clone, Default, Debug, Eq, PartialEq, Hash)]
+pub struct B8192;
+
+macro_rules! impl_bits {
+    ($name:ident, $bits:expr) => {
+        impl RsaKeyBits for $name {
+            const BITS: usize = $bits;
+        }
+
+        impl Display for $name {
+            fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+                write!(f, "{} bits", $bits)
+            }
+        }
+
+        impl Sealed for $name {}
+    };
+}
+
+impl_bits!(B2048, 2048);
+impl_bits!(B3072, 3072);
+impl_bits!(B4096, 4096);
+impl_bits!(B6144, 6144);
+impl_bits!(B8192, 8192);
diff --git a/src/public/rsa/mod.rs b/src/public/rsa/mod.rs
new file mode 100644
index 0000000..548615a
--- /dev/null
+++ b/src/public/rsa/mod.rs
@@ -0,0 +1,554 @@
+// Copyright 2018 Google LLC
+//
+// Use of this source code is governed by an MIT-style
+// license that can be found in the LICENSE file or at
+// https://opensource.org/licenses/MIT.
+
+//! The RSA public-key cryptosystem.
+
+mod bits;
+
+pub use public::rsa::bits::{RsaKeyBits, B2048, B3072, B4096, B6144, B8192};
+
+use std::convert::TryInto;
+use std::fmt::{self, Debug, Formatter};
+
+use boringssl::{CHeapWrapper, CStackWrapper};
+use public::rsa::inner::RsaKey;
+use public::{inner::DerKey, DerPrivateKey, DerPublicKey, PrivateKey, PublicKey};
+use util::Sealed;
+use Error;
+
+mod inner {
+    use std::convert::TryInto;
+    use std::marker::PhantomData;
+    use std::os::raw::c_uint;
+
+    use boringssl::{self, BoringError, CHeapWrapper, CStackWrapper};
+    use public::inner::BoringDerKey;
+    use public::rsa::RsaKeyBits;
+    use Error;
+
+    // A convenience wrapper around boringssl::RSA.
+    //
+    // RsaKey maintains the following invariants:
+    // - The key is valid.
+    // - The key has B bits.
+    //
+    // This is marked pub and put in this (non-public) module so that using it in impls of
+    // the Key trait don't result in public-in-private errors.
+    #[derive(Clone)]
+    pub struct RsaKey<B: RsaKeyBits> {
+        pub key: CHeapWrapper<boringssl::RSA>,
+        _marker: PhantomData<B>,
+    }
+
+    impl<B: RsaKeyBits> RsaKey<B> {
+        pub fn generate() -> Result<RsaKey<B>, BoringError> {
+            let mut key = CHeapWrapper::default();
+            let mut e = CStackWrapper::default();
+            // BN_set_u64 can only fail due to OOM.
+            e.bn_set_u64(boringssl::RSA_F4.into()).unwrap();
+            key.rsa_generate_key_ex(B::BITS.try_into().unwrap(), &e.as_c_ref())?;
+            Ok(RsaKey {
+                key,
+                _marker: PhantomData,
+            })
+        }
+
+        /// Creates an `RsaKey` from a BoringSSL `RSA`.
+        ///
+        /// `from_RSA` validates that `key` has `B` bits.
+        #[allow(non_snake_case)]
+        pub fn from_RSA(key: CHeapWrapper<boringssl::RSA>) -> Result<RsaKey<B>, Error> {
+            B::validate_bits(key.rsa_bits())?;
+            Ok(RsaKey {
+                key,
+                _marker: PhantomData,
+            })
+        }
+    }
+
+    impl<B: RsaKeyBits> BoringDerKey for RsaKey<B> {
+        fn pkey_assign(&self, pkey: &mut CHeapWrapper<boringssl::EVP_PKEY>) {
+            pkey.evp_pkey_assign_rsa(self.key.clone())
+        }
+
+        fn pkey_get(pkey: &mut CHeapWrapper<boringssl::EVP_PKEY>) -> Result<Self, Error> {
+            let key = pkey.evp_pkey_get1_rsa()?;
+            RsaKey::from_RSA(key)
+        }
+
+        fn parse_private_key(cbs: &mut CStackWrapper<boringssl::CBS>) -> Result<RsaKey<B>, Error> {
+            let key = CHeapWrapper::rsa_parse_private_key(cbs)?;
+            RsaKey::from_RSA(key)
+        }
+
+        fn marshal_private_key(
+            &self,
+            cbb: &mut CStackWrapper<boringssl::CBB>,
+        ) -> Result<(), Error> {
+            self.key.rsa_marshal_private_key(cbb).map_err(From::from)
+        }
+    }
+
+    trait RsaKeyBitsExt: RsaKeyBits {
+        fn validate_bits(bits: c_uint) -> Result<(), Error> {
+            if <c_uint as TryInto<usize>>::try_into(bits).unwrap() != Self::BITS {
+                return Err(Error::new(format!(
+                    "unexpected RSA key bit length: got {}; want {}",
+                    bits,
+                    Self::BITS
+                )));
+            }
+            Ok(())
+        }
+    }
+
+    impl<B: RsaKeyBits> RsaKeyBitsExt for B {}
+
+    #[cfg(test)]
+    mod tests {
+        use std::mem;
+
+        use super::*;
+        use public::rsa::{B2048, B3072, B4096, B6144, B8192};
+
+        #[test]
+        fn test_refcount() {
+            // Test that we refcount properly by creating many clones and then
+            // freeing them all. If we decrement the recount below 0, a test in
+            // BoringSSL will catch it and crash the program. This test cannot
+            // currently detect not decrementing the refcount enough (thus
+            // leaking resources).
+            //
+            // TODO(joshlf): Figure out a way to also test that we decrement the
+            // refcount enough.
+
+            fn test<B: RsaKeyBits>() {
+                let key = RsaKey::<B>::generate().unwrap();
+                for i in 0..8 {
+                    // make i clones and then free them all
+                    let mut keys = Vec::new();
+                    for _ in 0..i {
+                        keys.push(key.clone());
+                    }
+                    mem::drop(keys);
+                }
+                mem::drop(key);
+            }
+
+            test::<B2048>();
+            test::<B3072>();
+            test::<B4096>();
+            test::<B6144>();
+            test::<B8192>();
+        }
+    }
+}
+
+/// A `B`-bit RSA public key.
+///
+/// `RsaPubKey` is an RSA public key which is `B` bits long.
+pub struct RsaPubKey<B: RsaKeyBits> {
+    inner: RsaKey<B>,
+}
+
+impl<B: RsaKeyBits> Sealed for RsaPubKey<B> {}
+impl<B: RsaKeyBits> DerPublicKey for RsaPubKey<B> {}
+
+impl<B: RsaKeyBits> DerKey for RsaPubKey<B> {
+    type Boring = RsaKey<B>;
+    fn boring(&self) -> &RsaKey<B> {
+        &self.inner
+    }
+    fn from_boring(inner: RsaKey<B>) -> RsaPubKey<B> {
+        RsaPubKey { inner }
+    }
+}
+
+impl<B: RsaKeyBits> PublicKey for RsaPubKey<B> {
+    type Private = RsaPrivKey<B>;
+}
+
+impl<B: RsaKeyBits> Debug for RsaPubKey<B> {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+        write!(f, "RsaPubKey")
+    }
+}
+
+/// A `B`-bit RSA private key.
+///
+/// `RsaPrivKey` is an RSA private key which is `B` bits long.
+pub struct RsaPrivKey<B: RsaKeyBits> {
+    inner: RsaKey<B>,
+}
+
+impl<B: RsaKeyBits> RsaPrivKey<B> {
+    /// Generates a new private key.
+    #[must_use]
+    pub fn generate() -> Result<RsaPrivKey<B>, Error> {
+        Ok(RsaPrivKey {
+            inner: RsaKey::generate()?,
+        })
+    }
+}
+
+impl<B: RsaKeyBits> Sealed for RsaPrivKey<B> {}
+impl<B: RsaKeyBits> DerPrivateKey for RsaPrivKey<B> {}
+
+impl<B: RsaKeyBits> DerKey for RsaPrivKey<B> {
+    type Boring = RsaKey<B>;
+    fn boring(&self) -> &RsaKey<B> {
+        &self.inner
+    }
+    fn from_boring(inner: RsaKey<B>) -> RsaPrivKey<B> {
+        RsaPrivKey { inner }
+    }
+}
+
+impl<B: RsaKeyBits> PrivateKey for RsaPrivKey<B> {
+    type Public = RsaPubKey<B>;
+
+    fn public(&self) -> RsaPubKey<B> {
+        RsaPubKey {
+            inner: self.inner.clone(),
+        }
+    }
+}
+
+impl<B: RsaKeyBits> Debug for RsaPrivKey<B> {
+    fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
+        write!(f, "RsaPrivKey")
+    }
+}
+
+/// An RSA public key whose bit length is unknown at compile time.
+///
+/// An `RsaPubKeyAnyBits` is an enum of [`RsaPubKey`]s over all supported bit
+/// lengths. It is returned from [`parse_public_key_der_any_bits`].
+#[allow(missing_docs)]
+#[derive(Debug)]
+pub enum RsaPubKeyAnyBits {
+    B2048(RsaPubKey<B2048>),
+    B3072(RsaPubKey<B3072>),
+    B4096(RsaPubKey<B4096>),
+    B6144(RsaPubKey<B6144>),
+    B8192(RsaPubKey<B8192>),
+}
+
+impl RsaPubKeyAnyBits {
+    /// Parses a public key in DER format with any bit length.
+    ///
+    /// `parse_from_der` is like [`DerPublicKey::parse_from_der`], but it
+    /// accepts any bit length rather than a particular bit length.
+    ///
+    /// Since [`RsaPubKey`] requires a static [`RsaKeyBits`] type parameter, the
+    /// `parse_from_der` function on `RsaPubKey`'s `DerPublicKey` implementation
+    /// can only be called when the bit length is known ahead of time. This
+    /// function, on the other hand, accepts any bit length.
+    ///
+    /// [`DerPublicKey::parse_from_der`]: ::public::DerPublicKey::parse_from_der
+    /// [`PublicKey`]: ::public::PublicKey
+    #[must_use]
+    pub fn parse_from_der(bytes: &[u8]) -> Result<RsaPubKeyAnyBits, Error> {
+        CStackWrapper::cbs_with_temp_buffer(bytes, |cbs| {
+            let mut evp_pkey = CHeapWrapper::evp_parse_public_key(cbs)?;
+            let key = evp_pkey.evp_pkey_get1_rsa()?;
+            if cbs.cbs_len() > 0 {
+                return Err(Error::new(
+                    "excess data provided after valid DER input".to_string(),
+                ));
+            }
+
+            Ok(match key.rsa_bits().try_into().unwrap() {
+                B2048::BITS => RsaPubKeyAnyBits::B2048(RsaPubKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B3072::BITS => RsaPubKeyAnyBits::B3072(RsaPubKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B4096::BITS => RsaPubKeyAnyBits::B4096(RsaPubKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B6144::BITS => RsaPubKeyAnyBits::B6144(RsaPubKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B8192::BITS => RsaPubKeyAnyBits::B8192(RsaPubKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                bits => return Err(Error::new(format!("unsupported bit length: {}", bits))),
+            })
+        })
+    }
+}
+
+/// An RSA private key whose bit length is unknown at compile time.
+///
+/// An `RsaPrivKeyAnyBits` is an enum of [`RsaPrivKey`]s over all supported bit
+/// lengths. It is returned from [`parse_private_key_der_any_bits`].
+#[allow(missing_docs)]
+#[derive(Debug)]
+pub enum RsaPrivKeyAnyBits {
+    B2048(RsaPrivKey<B2048>),
+    B3072(RsaPrivKey<B3072>),
+    B4096(RsaPrivKey<B4096>),
+    B6144(RsaPrivKey<B6144>),
+    B8192(RsaPrivKey<B8192>),
+}
+
+impl RsaPrivKeyAnyBits {
+    /// Gets the public key corresponding to this private key.
+    #[must_use]
+    pub fn public(&self) -> RsaPubKeyAnyBits {
+        match self {
+            RsaPrivKeyAnyBits::B2048(key) => RsaPubKeyAnyBits::B2048(key.public()),
+            RsaPrivKeyAnyBits::B3072(key) => RsaPubKeyAnyBits::B3072(key.public()),
+            RsaPrivKeyAnyBits::B4096(key) => RsaPubKeyAnyBits::B4096(key.public()),
+            RsaPrivKeyAnyBits::B6144(key) => RsaPubKeyAnyBits::B6144(key.public()),
+            RsaPrivKeyAnyBits::B8192(key) => RsaPubKeyAnyBits::B8192(key.public()),
+        }
+    }
+
+    /// Parses a private key in DER format with any bit length.
+    ///
+    /// `parse_from_der` is like [`DerPrivateKey::parse_from_der`], but it
+    /// accepts any bit length rather that a particular bit length.
+    ///
+    /// Since [`RsaPrivKey`] requires a static [`RsaKeyBits`] type parameter,
+    /// the `parse_from_der` function on `RsaPrivKey`'s `DerPrivateKey`
+    /// implementation can only be called when the bit length is known ahead of
+    /// time. This function, on the other hand, accepts any bit length.
+    ///
+    /// [`DerPrivateKey::parse_from_der`]: ::public::DerPrivateKey::parse_from_der
+    /// [`PrivateKey`]: ::public::PrivateKey
+    #[must_use]
+    pub fn parse_from_der(bytes: &[u8]) -> Result<RsaPrivKeyAnyBits, Error> {
+        CStackWrapper::cbs_with_temp_buffer(bytes, |cbs| {
+            let key = CHeapWrapper::rsa_parse_private_key(cbs)?;
+            if cbs.cbs_len() > 0 {
+                return Err(Error::new(
+                    "excess data provided after valid DER input".to_string(),
+                ));
+            }
+
+            Ok(match key.rsa_bits().try_into().unwrap() {
+                B2048::BITS => RsaPrivKeyAnyBits::B2048(RsaPrivKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B3072::BITS => RsaPrivKeyAnyBits::B3072(RsaPrivKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B4096::BITS => RsaPrivKeyAnyBits::B4096(RsaPrivKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B6144::BITS => RsaPrivKeyAnyBits::B6144(RsaPrivKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                B8192::BITS => RsaPrivKeyAnyBits::B8192(RsaPrivKey {
+                    inner: RsaKey::from_RSA(key.clone())?,
+                }),
+                bits => return Err(Error::new(format!("unsupported bit length: {}", bits))),
+            })
+        })
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use util::should_fail;
+
+    #[test]
+    fn test_generate() {
+        RsaPrivKey::<B2048>::generate().unwrap();
+        RsaPrivKey::<B3072>::generate().unwrap();
+        RsaPrivKey::<B4096>::generate().unwrap();
+        RsaPrivKey::<B6144>::generate().unwrap();
+        RsaPrivKey::<B8192>::generate().unwrap();
+    }
+
+    #[test]
+    fn test_marshal_parse() {
+        // Test various combinations of parsing and serializing keys.
+        //
+        // Since we need to test dynamic parsing (the
+        // parse_private_key_der_any_bits and parse_public_key_der_any_bits
+        // functions), we need a way of unwrapping their return values into a
+        // static key type. Unfortunately, there's no way (on stable Rust) to do
+        // that generically, so the caller must pass a function which will do
+        // it.
+        fn test<
+            B: RsaKeyBits,
+            F: Fn(RsaPrivKeyAnyBits) -> RsaPrivKey<B>,
+            G: Fn(RsaPubKeyAnyBits) -> RsaPubKey<B>,
+        >(
+            unwrap_priv_any: F,
+            unwrap_pub_any: G,
+        ) {
+            let key = RsaPrivKey::<B>::generate().unwrap();
+
+            let _ = RsaPrivKey::<B>::parse_from_der(&key.marshal_to_der()).unwrap();
+            let _ =
+                unwrap_priv_any(RsaPrivKeyAnyBits::parse_from_der(&key.marshal_to_der()).unwrap());
+            let pubkey = key.public();
+            let _ = RsaPubKey::<B>::parse_from_der(&pubkey.marshal_to_der()).unwrap();
+            let _ =
+                unwrap_pub_any(RsaPubKeyAnyBits::parse_from_der(&pubkey.marshal_to_der()).unwrap());
+
+            let _ = RsaPubKey::<B>::marshal_to_der;
+            let _ = RsaPubKey::<B>::parse_from_der;
+        }
+
+        macro_rules! unwrap_any_bits {
+            ($name:ident, $any_type:ty, $key_type:ty, $bits_variant:path) => {
+                fn $name(key: $any_type) -> $key_type {
+                    match key {
+                        $bits_variant(key) => key,
+                        _ => panic!("unexpected bits"),
+                    }
+                }
+            };
+        }
+
+        unwrap_any_bits!(
+            unwrap_priv_key_any_2048,
+            RsaPrivKeyAnyBits,
+            RsaPrivKey<B2048>,
+            RsaPrivKeyAnyBits::B2048
+        );
+        unwrap_any_bits!(
+            unwrap_priv_key_any_3072,
+            RsaPrivKeyAnyBits,
+            RsaPrivKey<B3072>,
+            RsaPrivKeyAnyBits::B3072
+        );
+        unwrap_any_bits!(
+            unwrap_priv_key_any_4096,
+            RsaPrivKeyAnyBits,
+            RsaPrivKey<B4096>,
+            RsaPrivKeyAnyBits::B4096
+        );
+        unwrap_any_bits!(
+            unwrap_priv_key_any_6144,
+            RsaPrivKeyAnyBits,
+            RsaPrivKey<B6144>,
+            RsaPrivKeyAnyBits::B6144
+        );
+        unwrap_any_bits!(
+            unwrap_priv_key_any_8192,
+            RsaPrivKeyAnyBits,
+            RsaPrivKey<B8192>,
+            RsaPrivKeyAnyBits::B8192
+        );
+        unwrap_any_bits!(
+            unwrap_pub_key_any_2048,
+            RsaPubKeyAnyBits,
+            RsaPubKey<B2048>,
+            RsaPubKeyAnyBits::B2048
+        );
+        unwrap_any_bits!(
+            unwrap_pub_key_any_3072,
+            RsaPubKeyAnyBits,
+            RsaPubKey<B3072>,
+            RsaPubKeyAnyBits::B3072
+        );
+        unwrap_any_bits!(
+            unwrap_pub_key_any_4096,
+            RsaPubKeyAnyBits,
+            RsaPubKey<B4096>,
+            RsaPubKeyAnyBits::B4096
+        );
+        unwrap_any_bits!(
+            unwrap_pub_key_any_6144,
+            RsaPubKeyAnyBits,
+            RsaPubKey<B6144>,
+            RsaPubKeyAnyBits::B6144
+        );
+        unwrap_any_bits!(
+            unwrap_pub_key_any_8192,
+            RsaPubKeyAnyBits,
+            RsaPubKey<B8192>,
+            RsaPubKeyAnyBits::B8192
+        );
+
+        test::<B2048, _, _>(unwrap_priv_key_any_2048, unwrap_pub_key_any_2048);
+        test::<B3072, _, _>(unwrap_priv_key_any_3072, unwrap_pub_key_any_3072);
+        test::<B4096, _, _>(unwrap_priv_key_any_4096, unwrap_pub_key_any_4096);
+        test::<B6144, _, _>(unwrap_priv_key_any_6144, unwrap_pub_key_any_6144);
+        test::<B8192, _, _>(unwrap_priv_key_any_8192, unwrap_pub_key_any_8192);
+    }
+
+    #[test]
+    fn test_parse_fail() {
+        // Test that invalid input is rejected.
+        fn test_parse_invalid<B: RsaKeyBits>() {
+            should_fail(
+                RsaPrivKey::<B>::parse_from_der(&[]),
+                "RsaPrivKey::parse_from_der",
+                "RSA routines:OPENSSL_internal:BAD_ENCODING",
+            );
+            should_fail(
+                RsaPubKey::<B>::parse_from_der(&[]),
+                "RsaPubKey::parse_from_der",
+                "public key routines:OPENSSL_internal:DECODE_ERROR",
+            );
+            should_fail(
+                RsaPrivKeyAnyBits::parse_from_der(&[]),
+                "RsaPrivKeyAnyBits::parse_from_der",
+                "RSA routines:OPENSSL_internal:BAD_ENCODING",
+            );
+            should_fail(
+                RsaPubKeyAnyBits::parse_from_der(&[]),
+                "RsaPubKeyAnyBits::parse_from_der",
+                "public key routines:OPENSSL_internal:DECODE_ERROR",
+            );
+        }
+
+        test_parse_invalid::<B2048>();
+        test_parse_invalid::<B3072>();
+        test_parse_invalid::<B4096>();
+        test_parse_invalid::<B6144>();
+        test_parse_invalid::<B8192>();
+
+        // Test that, when a particular bit size is expected, other bit sizes are
+        // rejected.
+        fn test_parse_wrong_bit_size<B1: RsaKeyBits, B2: RsaKeyBits>() {
+            let privkey = RsaPrivKey::<B1>::generate().unwrap();
+            let key_der = privkey.marshal_to_der();
+            should_fail(
+                RsaPrivKey::<B2>::parse_from_der(&key_der),
+                "RsaPrivKey::parse_from_der",
+                "unexpected RSA key bit length:",
+            );
+            let key_der = privkey.public().marshal_to_der();
+            should_fail(
+                RsaPubKey::<B2>::parse_from_der(&key_der),
+                "RsaPubKey::parse_from_der",
+                "unexpected RSA key bit length:",
+            );
+        }
+
+        // All pairs of bit sizes, (X, Y), such that X != Y.
+        test_parse_wrong_bit_size::<B2048, B3072>();
+        test_parse_wrong_bit_size::<B2048, B4096>();
+        test_parse_wrong_bit_size::<B2048, B6144>();
+        test_parse_wrong_bit_size::<B2048, B8192>();
+        test_parse_wrong_bit_size::<B3072, B2048>();
+        test_parse_wrong_bit_size::<B3072, B4096>();
+        test_parse_wrong_bit_size::<B3072, B6144>();
+        test_parse_wrong_bit_size::<B3072, B8192>();
+        test_parse_wrong_bit_size::<B4096, B2048>();
+        test_parse_wrong_bit_size::<B4096, B3072>();
+        test_parse_wrong_bit_size::<B4096, B6144>();
+        test_parse_wrong_bit_size::<B4096, B8192>();
+        test_parse_wrong_bit_size::<B6144, B2048>();
+        test_parse_wrong_bit_size::<B6144, B3072>();
+        test_parse_wrong_bit_size::<B6144, B4096>();
+        test_parse_wrong_bit_size::<B6144, B8192>();
+        test_parse_wrong_bit_size::<B8192, B2048>();
+        test_parse_wrong_bit_size::<B8192, B3072>();
+        test_parse_wrong_bit_size::<B8192, B4096>();
+        test_parse_wrong_bit_size::<B8192, B6144>();
+    }
+}