Add getters for RSA public exponent and modulus

Closes: #28

Change-Id: I3effea784346fe6c0712780087a6929a2ee5c027
Reviewed-on: https://fuchsia-review.googlesource.com/c/mundane/+/486717
Reviewed-by: Joshua Liebow-Feeser <joshlf@google.com>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b99852..5020343 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,9 @@
 
 ## Unreleased
 
+### Added
+- Exposed getters for RSA public exponent and modulus
+
 ## [0.4.3] - 2020-03-26
 
 ### Added
diff --git a/boringssl/bindgen.sh b/boringssl/bindgen.sh
index 227faa2..dbe740f 100755
--- a/boringssl/bindgen.sh
+++ b/boringssl/bindgen.sh
@@ -41,8 +41,10 @@
 # former for a consistency check of the postprocessing step which adds the
 # #[link_name...] attributes. Any change of the allowlist must be made to the
 # appropriate sub-list.
-ALLOWLIST_FUNCS="BN_free|\
+ALLOWLIST_FUNCS="BN_bn2bin_padded|\
+BN_free|\
 BN_init|\
+BN_num_bytes|\
 BN_set_u64|\
 CBS_init|\
 CBS_len|\
@@ -103,6 +105,8 @@
 RSA_bits|\
 RSA_free|\
 RSA_generate_key_ex|\
+RSA_get0_e|\
+RSA_get0_n|\
 RSA_marshal_private_key|\
 RSA_new|\
 RSA_parse_private_key|\
diff --git a/boringssl/boringssl.rs b/boringssl/boringssl.rs
index a847438..f4167a2 100644
--- a/boringssl/boringssl.rs
+++ b/boringssl/boringssl.rs
@@ -211,9 +211,18 @@
     pub fn BN_free(bn: *mut BIGNUM);
 }
 extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_4_3_BN_num_bytes"]
+    pub fn BN_num_bytes(bn: *const BIGNUM) -> ::std::os::raw::c_uint;
+}
+extern "C" {
     #[link_name = "__RUST_MUNDANE_0_4_3_BN_set_u64"]
     pub fn BN_set_u64(bn: *mut BIGNUM, value: u64) -> ::std::os::raw::c_int;
 }
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_4_3_BN_bn2bin_padded"]
+    pub fn BN_bn2bin_padded(out: *mut u8, len: size_t, in_: *const BIGNUM)
+        -> ::std::os::raw::c_int;
+}
 #[repr(C)]
 #[derive(Debug, Copy, Clone)]
 pub struct bn_gencb_st {
@@ -1107,6 +1116,14 @@
     pub fn RSA_bits(rsa: *const RSA) -> ::std::os::raw::c_uint;
 }
 extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_4_3_RSA_get0_n"]
+    pub fn RSA_get0_n(rsa: *const RSA) -> *const BIGNUM;
+}
+extern "C" {
+    #[link_name = "__RUST_MUNDANE_0_4_3_RSA_get0_e"]
+    pub fn RSA_get0_e(rsa: *const RSA) -> *const BIGNUM;
+}
+extern "C" {
     #[link_name = "__RUST_MUNDANE_0_4_3_RSA_generate_key_ex"]
     pub fn RSA_generate_key_ex(
         rsa: *mut RSA,
diff --git a/src/boringssl/mod.rs b/src/boringssl/mod.rs
index 10e17cc..18e69b9 100644
--- a/src/boringssl/mod.rs
+++ b/src/boringssl/mod.rs
@@ -107,15 +107,15 @@
 
 use boringssl::abort::UnwrapAbort;
 use boringssl::raw::{
-    size_t, 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,
+    size_t, BN_bn2bin_padded, BN_num_bytes, 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_copy, HMAC_CTX_init, HMAC_Final, HMAC_Init_ex, HMAC_Update,
     HMAC_size, IntoSizeT, IntoUsize, RAND_bytes, RC4_set_key, RSA_bits, RSA_generate_key_ex,
-    RSA_marshal_private_key, RSA_parse_private_key, RSA_sign_pss_mgf1, RSA_size,
+    RSA_get0_e, RSA_get0_n, RSA_marshal_private_key, RSA_parse_private_key, RSA_sign_pss_mgf1, RSA_size,
     RSA_verify_pss_mgf1, SHA384_Init, RC4,
 };
 #[cfg(feature = "rsa-pkcs1v15")]
@@ -129,6 +129,33 @@
     }
 }
 
+impl CRef<'_, BIGNUM> {
+    /// The `BN_bn2bin_padded` function.
+    ///
+    /// `bn_bn2bin_padded` can only fail if `out` is shorter than the minimum
+    /// number of bytes required by [`bn_num_bytes`].
+    ///
+    /// [`bn_num_bytes`]: CRef::bn_num_bytes
+    #[must_use]
+    pub fn bn_bn2bin_padded(&self, out: &mut [u8]) -> Result<(), BoringError> {
+        unsafe {
+            BN_bn2bin_padded(
+                out.as_mut_ptr(), out.len().into_size_t(), self.as_const())
+        }
+    }
+
+    /// The `BN_num_bytes` function.
+    ///
+    /// `bn_num_bytes` returns minimum number of bytes required to serialize
+    /// using [`bn_bn2bin_padded`].
+    ///
+    /// [`bn_bn2bin_padded`]: CRef::bn_bn2bin_padded
+    #[must_use]
+    pub fn bn_num_bytes(&self) -> usize {
+        unsafe { BN_num_bytes(self.as_const()).into_usize() }
+    }
+}
+
 impl CStackWrapper<CBB> {
     /// Creates a new `CBB` and initializes it with `CBB_init`.
     ///
@@ -648,6 +675,23 @@
         unsafe { RSA_bits(self.as_const() as *mut _).into_usize() }
     }
 
+    /// The `RSA_get0_e` function.
+    ///
+    /// Returns a constant reference to the key public exponent.
+    #[must_use]
+    #[allow(clippy::needless_lifetimes)] // to be more explicit
+    pub fn rsa_get0_e<'a>(&'a self) -> Result<CRef<'a, BIGNUM>, BoringError> {
+            unsafe { Ok(CRef::new(RSA_get0_e(self.as_const())?)) }
+    }
+
+    /// The `RSA_get0_n` function.
+    ///
+    /// Returns a constant reference to the key public modulus.
+    #[allow(clippy::needless_lifetimes)] // to be more explicit
+    pub fn rsa_get0_n<'a>(&'a self) -> Result<CRef<'a, BIGNUM>, BoringError> {
+            unsafe { Ok(CRef::new(RSA_get0_n(self.as_const())?)) }
+    }
+
     /// The `RSA_generate_key_ex` function.
     #[must_use]
     pub fn rsa_generate_key_ex(
diff --git a/src/boringssl/raw.rs b/src/boringssl/raw.rs
index dc7bd0f..2561206 100644
--- a/src/boringssl/raw.rs
+++ b/src/boringssl/raw.rs
@@ -21,7 +21,7 @@
 
 // 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,
+    size_t, BN_num_bytes, 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,
     RC4_set_key, RSA_bits, RC4,
 };
@@ -62,6 +62,13 @@
 
 #[allow(non_snake_case)]
 #[must_use]
+pub unsafe fn BN_bn2bin_padded(out: *mut u8, len: size_t, bn: *const BIGNUM)
+                        -> Result<(), BoringError> {
+    one_or_err("BN_bn2bin_padded", ffi::BN_bn2bin_padded(out, len, bn))
+}
+
+#[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))
 }
@@ -418,6 +425,18 @@
     ptr_or_err("RSA_parse_private_key", ffi::RSA_parse_private_key(cbs))
 }
 
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn RSA_get0_n(rsa: *const RSA) -> Result<NonNull<BIGNUM>, BoringError> {
+    ptr_or_err("RSA_get0_n", ffi::RSA_get0_n(rsa) as *mut BIGNUM)
+}
+
+#[allow(non_snake_case)]
+#[must_use]
+pub unsafe fn RSA_get0_e(rsa: *const RSA) -> Result<NonNull<BIGNUM>, BoringError> {
+    ptr_or_err("RSA_get0_e", ffi::RSA_get0_e(rsa) as *mut BIGNUM)
+}
+
 #[cfg(feature = "rsa-pkcs1v15")]
 #[allow(non_snake_case)]
 #[must_use]
diff --git a/src/public/rsa/mod.rs b/src/public/rsa/mod.rs
index 9195e89..cf5e85c 100644
--- a/src/public/rsa/mod.rs
+++ b/src/public/rsa/mod.rs
@@ -82,6 +82,36 @@
         pub fn get_key(&self) -> &CHeapWrapper<boringssl::RSA> {
             &self.key
         }
+
+        /// Returns the key's public exponent as a big-endian byte vector.
+        pub fn public_exponent_be(&self) -> Vec<u8> {
+            // Guaranteed to succeed because RSA_get0_e can only fail if
+            // the "e" parameter is not initialized, but one of the invariants
+            // on RsaKey is that it is initialized.
+            let bignum = self.key.rsa_get0_e().unwrap();
+            let num_bytes = bignum.bn_num_bytes();
+            let mut exponent = vec![0; num_bytes];
+            // Guaranteed to succeed because the size of `exponent` is
+            // [`bn_num_bytes`].
+            bignum.bn_bn2bin_padded(exponent.as_mut_slice()).unwrap();
+
+            exponent
+        }
+
+        /// Returns the key's public modulus as a big-endian byte vector.
+        pub fn public_modulus_be(&self) -> Vec<u8> {
+            // Guaranteed to succeed because RSA_get0_n can only fail if
+            // the "n" parameter is not initialized, but one of the invariants
+            // on RsaKey is that it is initialized.
+            let bignum = self.key.rsa_get0_n().unwrap();
+            let num_bytes = bignum.bn_num_bytes();
+            let mut modulus = vec![0; num_bytes];
+            // Guaranteed to succeed because the size of `modulus` is
+            // [`bn_num_bytes`].
+            bignum.bn_bn2bin_padded(modulus.as_mut_slice()).unwrap();
+
+            modulus
+        }
     }
 
     impl<B: RsaKeyBits> BoringDerKey for RsaKey<B> {
@@ -215,6 +245,18 @@
     pub fn generate() -> Result<RsaPrivKey<B>, Error> {
         Ok(RsaPrivKey { inner: RsaKey::generate()? })
     }
+
+    #[must_use]
+    /// Returns the key's public exponent as a big-endian byte vector.
+    pub fn public_exponent_be(&self) -> Vec<u8> {
+        self.inner.public_exponent_be()
+    }
+
+    #[must_use]
+    /// Returns the key's public modulus as a big-endian byte vector.
+    pub fn public_modulus_be(&self) -> Vec<u8> {
+        self.inner.public_modulus_be()
+    }
 }
 
 impl<B: RsaKeyBits> Sealed for RsaPrivKey<B> {}