Browse Source

Added the key_len method to Scheme.

Matthew Carr 2 years ago
parent
commit
09778da5a5
3 changed files with 164 additions and 117 deletions
  1. 90 64
      crates/btnode/src/crypto/mod.rs
  2. 70 48
      crates/btnode/src/crypto/tpm.rs
  3. 4 5
      crates/btnode/src/test_helpers.rs

+ 90 - 64
crates/btnode/src/crypto/mod.rs

@@ -10,7 +10,7 @@ use openssl::{
     pkey::{PKey, HasPublic, HasPrivate},
     symm::{Cipher, encrypt as openssl_encrypt, decrypt as openssl_decrypt},
     rand::rand_bytes,
-    rsa::{Rsa, Padding as OpensslPadding},
+    rsa::{Rsa as OsslRsa, Padding as OpensslPadding},
     hash::{hash, MessageDigest},
     sign::{Signer as OsslSigner, Verifier as OsslVerifier}
 };
@@ -193,33 +193,31 @@ impl Display for Hash {
     }
 }
 
-pub(crate) const RSA_KEY_BYTES: usize = 384;
-
 /// A cryptographic signature.
-#[derive(Debug, PartialEq, Serialize, Deserialize, Clone, EnumDiscriminants)]
-#[strum_discriminants(name(SignatureKind))]
-pub(crate) enum Signature {
-    #[serde(with = "BigArray")]
-    Rsa([u8; RSA_KEY_BYTES]),
+#[derive(Debug, PartialEq, Serialize, Deserialize, Clone)]
+pub struct Signature {
+    kind: Sign,
+    data: Vec<u8>,
 }
 
 impl Signature {
-    fn new(id: SignatureKind) -> Signature {
-        match id {
-            SignatureKind::Rsa => Signature::Rsa([0; RSA_KEY_BYTES])
-        }
+    pub fn empty(kind: Sign) -> Signature {
+        let data = vec![0; kind.key_len() as usize];
+        Signature { kind, data }
     }
 
-    fn as_slice(&self) -> &[u8] {
-        match self {
-            Signature::Rsa(buf) => buf.as_slice()
-        }
+    pub fn copy_from(kind: Sign, from: &[u8]) -> Signature {
+        let mut data = vec![0; kind.key_len() as usize];
+        data.as_mut_slice().copy_from_slice(from);
+        Signature { kind, data }
     }
 
-    fn as_mut_slice(&mut self) -> &mut [u8] {
-        match self {
-            Signature::Rsa(buf) => buf.as_mut_slice()
-        }
+    pub fn as_slice(&self) -> &[u8] {
+        self.data.as_slice()
+    }
+
+    pub fn as_mut_slice(&mut self) -> &mut [u8] {
+        self.data.as_mut_slice()
     }
 }
 
@@ -235,12 +233,6 @@ impl AsMut<[u8]> for Signature {
     }
 }
 
-impl Default for Signature {
-    fn default() -> Self {
-        Signature::Rsa([0; RSA_KEY_BYTES])
-    }
-}
-
 #[derive(Debug, PartialEq, Serialize, Deserialize, Clone, EnumDiscriminants)]
 #[strum_discriminants(name(SymKeyKind))]
 pub(crate) enum SymKey {
@@ -306,6 +298,22 @@ impl Decrypter for SymKey {
     }
 }
 
+#[repr(u32)]
+#[derive(Debug, Display, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
+pub enum KeyLen {
+    Bits256 = 32,
+    Bits512 = 64,
+    Bits2048 = 256,
+    Bits3072 = 384,
+    Bits4096 = 512,
+}
+
+impl KeyLen {
+    const fn bits(self) -> u32 {
+        8 * self as u32
+    }
+}
+
 pub trait Scheme: for<'de> Deserialize<'de> + Serialize
     + Copy + std::fmt::Debug + PartialEq + Into<Self::Kind>
 {
@@ -316,6 +324,7 @@ pub trait Scheme: for<'de> Deserialize<'de> + Serialize
     fn public_from_der(self, der: &[u8]) -> Result<PKey<Public>>;
     fn private_from_der(self, der: &[u8]) -> Result<PKey<Private>>;
     fn generate(self) -> Result<AsymKeyPair<Self::Kind>>;
+    fn key_len(self) -> KeyLen;
 }
 
 pub enum SchemeKind {
@@ -364,11 +373,22 @@ impl Scheme for Encrypt {
             Encrypt::RsaEsOaep(inner) => inner.generate(),
         }
     }
+
+    fn key_len(self) -> KeyLen {
+        match self {
+            Encrypt::RsaEsOaep(inner) => inner.key_len(),
+        }
+    }
 }
 
 impl Encrypt {
+    pub const RSA_OAEP_2048_SHA_256: Encrypt = Encrypt::RsaEsOaep(RsaEsOaep {
+        key_len: KeyLen::Bits2048,
+        hash_kind: HashKind::Sha2_256,
+    });
+
     pub const RSA_OAEP_3072_SHA_256: Encrypt = Encrypt::RsaEsOaep(RsaEsOaep {
-        key_bytes: 384,
+        key_len: KeyLen::Bits3072,
         hash_kind: HashKind::Sha2_256,
     });
 }
@@ -414,38 +434,53 @@ impl Scheme for Sign {
             Sign::RsaSsaPss(inner) => inner.generate(),
         }
     }
+
+    fn key_len(self) -> KeyLen {
+        self.key_len_const()
+    }
 }
 
 impl Sign {
+    pub const RSA_PSS_2048_SHA_256: Sign = Sign::RsaSsaPss(RsaSsaPss {
+        key_bits: KeyLen::Bits2048,
+        hash_kind: HashKind::Sha2_256,
+    });
+
     pub const RSA_PSS_3072_SHA_256: Sign = Sign::RsaSsaPss(RsaSsaPss {
-        key_bytes: 384,
+        key_bits: KeyLen::Bits3072,
         hash_kind: HashKind::Sha2_256,
     });
 
-    fn sig_buf(&self) -> Signature {
+    const fn key_len_const(self) -> KeyLen {
         match self {
-            Sign::RsaSsaPss(_) => Signature::Rsa([0u8; RSA_KEY_BYTES]),
+            Sign::RsaSsaPss(inner) => inner.key_bits,
         }
     }
 }
 
+enum Rsa {}
+
+impl Rsa {
+    /// The default public exponent to use for generated RSA keys.
+    const EXP: u32 = 65537; // 2**16 + 1
+
+    fn generate<S: Scheme>(scheme: S) -> Result<AsymKeyPair<S>> {
+        let key = OsslRsa::generate(scheme.key_len().bits())?;
+        // TODO: Separating the keys this way seems inefficient. Investigate alternatives.
+        let public_der = key.public_key_to_der()?;
+        let private_der = key.private_key_to_der()?;
+        let public = AsymKey::<Public, S>::new(scheme, &public_der)?;
+        let private = AsymKey::<Private, S>::new(scheme, &private_der)?;
+        Ok(AsymKeyPair { public, private })
+    }
+}
+
 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Copy)]
 pub struct RsaEsOaep {
-    key_bytes: usize,
+    key_len: KeyLen,
     hash_kind: HashKind,
 }
 
-fn generate_rsa<S: Scheme>(scheme: S) -> Result<AsymKeyPair<S>> {
-    let key_bits = 8 * u32::try_from(RSA_KEY_BYTES)?;
-    let key = Rsa::generate(key_bits)?;
-    // TODO: Separating the keys this way seems inefficient. Investigate alternatives.
-    let public_der = key.public_key_to_der()?;
-    let private_der = key.private_key_to_der()?;
-    let public = AsymKey::<Public, S>::new(scheme, &public_der)?;
-    let private = AsymKey::<Private, S>::new(scheme, &private_der)?;
-    Ok(AsymKeyPair { public, private })
-}
-
 impl Scheme for RsaEsOaep {
     type Kind = Encrypt;
 
@@ -470,7 +505,11 @@ impl Scheme for RsaEsOaep {
     }
 
     fn generate(self) -> Result<AsymKeyPair<Self::Kind>> {
-        generate_rsa(self.into())
+        Rsa::generate(self.into())
+    }
+
+    fn key_len(self) -> KeyLen {
+        self.key_len
     }
 }
 
@@ -482,7 +521,7 @@ impl From<RsaEsOaep> for Encrypt {
 
 #[derive(Deserialize, Serialize, Clone, Debug, PartialEq, Copy)]
 pub struct RsaSsaPss {
-    key_bytes: usize,
+    key_bits: KeyLen,
     hash_kind: HashKind,
 }
 
@@ -510,7 +549,11 @@ impl Scheme for RsaSsaPss {
     }
 
     fn generate(self) -> Result<AsymKeyPair<Self::Kind>> {
-        generate_rsa(self.into())
+        Rsa::generate(self.into())
+    }
+
+    fn key_len(self) -> KeyLen {
+        self.key_bits
     }
 }
 
@@ -590,12 +633,6 @@ impl<S: Scheme> AsymKey<Private, S> {
     }
 }
 
-impl<P: KeyPrivacy> AsymKey<P, Sign> {
-    fn signature_buf(&self) -> Signature {
-        self.scheme.sig_buf()
-    }
-}
-
 impl<'de, S: Scheme> Deserialize<'de> for AsymKey<Public, S> {
     fn deserialize<D: Deserializer<'de>>(d: D) -> std::result::Result<Self, D::Error> {
         const FIELDS: &[&str] = &["scheme", "pkey"];
@@ -698,7 +735,7 @@ impl Signer for AsymKey<Private, Sign> {
         for part in parts {
             signer.update(part)?;
         }
-        let mut signature = self.scheme.sig_buf();
+        let mut signature = Signature::empty(self.scheme);
         signer.sign(signature.as_mut_slice())?;
         Ok(signature)
     }
@@ -1133,7 +1170,7 @@ mod tests {
     #[test]
     fn verify_writecap_invalid_signature() -> Result<()> {
         let mut writecap = make_writecap(vec!["apps", "verse"])?;
-        writecap.signature = Signature::default();
+        writecap.signature = Signature::empty(Sign::RSA_PSS_3072_SHA_256);
         let result = verify_writecap(&writecap, &writecap.path);
         if let Err(Error::InvalidSignature) = result {
             Ok(())
@@ -1244,16 +1281,5 @@ mod tests {
             assert_eq!(44, public_len);
             assert_eq!(48, private_len);
         }
-
-        #[test]
-        fn rsa_signature_len() -> Result<()> {
-            let key = make_key_pair();
-            let signature = key.sign([NODE_PUBLIC_KEY.as_slice()].into_iter())?;
-            let length = match signature {
-                Signature::Rsa(data) => data.len(),
-            };
-            assert_eq!(RSA_KEY_BYTES, length);
-            Ok(())
-        }
     }
 }

+ 70 - 48
crates/btnode/src/crypto/tpm.rs

@@ -50,7 +50,6 @@ use tss_esapi::{
         SymmetricDefinitionObject,
         PublicKeyRsa,
         RsaScheme,
-        RsaExponent,
         HashcheckTicket,
         Ticket,
         SignatureScheme,
@@ -248,21 +247,21 @@ impl DigestExt for Digest {
     }
 }
 
-const COOKIE_LEN: usize = RSA_KEY_BYTES;
-
 #[derive(Serialize, Deserialize, Clone)]
 struct Cookie(
     #[serde(with = "BigArray")]
-    [u8; COOKIE_LEN]
+    [u8; Self::LEN]
 );
 
 impl Cookie {
+    const LEN: usize = TpmCredStore::SIGN_SCHEME.key_len_const() as usize;
+
     fn random() -> Result<Cookie> {
         Ok(Cookie(rand_array()?))
     }
 
     fn empty() -> Cookie {
-        Cookie([0; COOKIE_LEN])
+        Cookie([0; Self::LEN])
     }
 
     fn as_slice(&self) -> &[u8] {
@@ -413,29 +412,61 @@ impl TryInto<RsaScheme> for SchemeKind {
     }
 }
 
+impl TryFrom<KeyLen> for RsaKeyBits {
+    type Error = Error;
+
+    fn try_from(len: KeyLen) -> Result<RsaKeyBits> {
+        match len {
+            KeyLen::Bits2048 => Ok(RsaKeyBits::Rsa2048),
+            KeyLen::Bits3072 => Ok(RsaKeyBits::Rsa3072),
+            KeyLen::Bits4096 => Ok(RsaKeyBits::Rsa4096),
+            _ => Err(Error::custom(format!("unsupported key len: {}", len)))
+        }
+    }
+}
+
 struct KeyBuilder<'a, S: Scheme> {
     scheme: S,
     allow_dup: bool,
     unique: &'a [u8],
-    auth: Option<Auth>,
+    auth_value: Option<Auth>,
+    name_hash: HashingAlgorithm,
+    policy_digest: Digest,
 }
 
 impl<'a, S: Scheme> KeyBuilder<'a, S> {
-    /// The public exponent to use for generated RSA keys.
-    const RSA_EXPONENT: u32 = 65537; // 2**16 + 1
-
-    const RSA_KEY_BITS: RsaKeyBits = RsaKeyBits::Rsa3072;
-
     fn new(scheme: S, unique: &'a [u8]) -> KeyBuilder<'a, S> {
         KeyBuilder {
             scheme,
             allow_dup: false,
             unique,
-            auth: None,
+            auth_value: None,
+            name_hash: HashingAlgorithm::Sha256,
+            policy_digest: Digest::empty(),
         }
     }
 
-    fn template(&self) -> Result<Public> {
+    fn with_allow_dup(mut self, allow_dup: bool) -> Self {
+        self.allow_dup = allow_dup;
+        self
+    }
+
+    fn with_auth(mut self, auth: Auth) -> Self {
+        self.auth_value = Some(auth);
+        self
+    }
+
+    fn with_name_hash(mut self, name_hash: HashingAlgorithm) -> Self {
+        self.name_hash = name_hash;
+        self
+    }
+
+    fn with_policy_digest(mut self, policy_digest: Digest) -> Self {
+        self.policy_digest = policy_digest;
+        self
+    }
+
+    fn rsa_template(&self) -> Result<Public> {
         let scheme_enum = self.scheme.as_enum();
         let decrypt = match scheme_enum {
             SchemeKind::Sign(_) => false,
@@ -450,33 +481,33 @@ impl<'a, S: Scheme> KeyBuilder<'a, S> {
             .with_sign_encrypt(!decrypt)
             .with_restricted(false)
             .build()?;
-        let name_hashing_algorithm = HashingAlgorithm::Sha256;
-        let auth_policy = Digest::empty();
+        let name_hashing_algorithm = self.name_hash;
         let parameters = PublicRsaParameters::new(
             SymmetricDefinitionObject::Null,
             scheme_enum.try_into()?,
-            Self::RSA_KEY_BITS,
-            RsaExponent::try_from(Self::RSA_EXPONENT)?, 
+            self.scheme.key_len().try_into()?,
+            Rsa::EXP.try_into()?, 
         );
         let unique = PublicKeyRsa::try_from(self.unique)?;
         let public = Public::Rsa {
             object_attributes,
             name_hashing_algorithm,
-            auth_policy,
+            auth_policy: self.policy_digest.clone(),
             parameters,
             unique,
         };
         Ok(public)
     }
 
-    fn with_allow_dup(mut self, allow_dup: bool) -> Self {
-        self.allow_dup = allow_dup;
-        self
-    }
-
-    fn with_auth(mut self, auth: Auth) -> Self {
-        self.auth = Some(auth);
-        self
+    fn template(&self) -> Result<Public> {
+        match self.scheme.as_enum() {
+            SchemeKind::Encrypt(encrypt) => match encrypt {
+                Encrypt::RsaEsOaep(_) => self.rsa_template(),
+            }
+            SchemeKind::Sign(sign) => match sign {
+                Sign::RsaSsaPss(_) => self.rsa_template(),
+            }
+        }
     }
 }
 
@@ -537,6 +568,9 @@ pub(crate) struct TpmCredStore {
 }
 
 impl TpmCredStore {
+    const SIGN_SCHEME: Sign = Sign::RSA_PSS_3072_SHA_256;
+    const ENCRYPT_SCHEME: Encrypt = Encrypt::RSA_OAEP_3072_SHA_256;
+
     pub(crate) fn new<P: AsRef<Path>>(mut context: Context, state_path: P) -> Result<TpmCredStore> {
         let storage = Storage::load_or_init(state_path.as_ref())?;
         let session = context.start_default_auth_session()?;
@@ -564,7 +598,7 @@ impl TpmCredStore {
             guard.context.create_primary(
                 Hierarchy::Owner,
                 params.template()?,
-                params.auth,
+                params.auth_value,
                 None,
                 None,
                 None,
@@ -575,14 +609,14 @@ impl TpmCredStore {
     }
 
     fn gen_node_sign_key(&self) -> Result<KeyPair<Sign>> {
-        let params = KeyBuilder::new(Sign::RSA_PSS_3072_SHA_256, self.cookie.as_slice())
+        let params = KeyBuilder::new(Self::SIGN_SCHEME, self.cookie.as_slice())
             .with_allow_dup(false)
             .with_auth(self.cookie.auth());
         self.gen_key(params)
     }
 
     fn gen_node_enc_key(&self) -> Result<KeyPair<Encrypt>> {
-        let params = KeyBuilder::new(Encrypt::RSA_OAEP_3072_SHA_256, self.cookie.as_slice())
+        let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, self.cookie.as_slice())
             .with_allow_dup(false)
             .with_auth(self.cookie.auth());
         self.gen_key(params)
@@ -630,16 +664,16 @@ impl TpmCredStore {
     }
 
     fn gen_root_sign_key(&self, password: &str) -> Result<KeyPair<Sign>> {
-        let unique: [u8; COOKIE_LEN] = rand_array()?;
-        let params = KeyBuilder::new(Sign::RSA_PSS_3072_SHA_256, unique.as_slice())
+        let unique: [u8; Cookie::LEN] = rand_array()?;
+        let params = KeyBuilder::new(Self::SIGN_SCHEME, unique.as_slice())
             .with_allow_dup(true)
             .with_auth(Auth::try_from(password.as_bytes())?);
         self.gen_key(params)
     }
 
     fn gen_root_enc_key(&self, password: &str) -> Result<KeyPair<Encrypt>> {
-        let unique: [u8; COOKIE_LEN] = rand_array()?;
-        let params = KeyBuilder::new(Encrypt::RSA_OAEP_3072_SHA_256, unique.as_slice())
+        let unique: [u8; Cookie::LEN] = rand_array()?;
+        let params = KeyBuilder::new(Self::ENCRYPT_SCHEME, unique.as_slice())
             .with_allow_dup(true)
             .with_auth(Auth::try_from(password.as_bytes())?);
         self.gen_key(params)
@@ -696,7 +730,7 @@ impl<S: Scheme> AsymKeyPub<S> {
                 let exponent_value = parameters.exponent().value();
                 let exponent = BigNum::from_u32(exponent_value)?;
                 let modulus = BigNum::from_slice(unique.as_slice())?;
-                let rsa = Rsa::from_public_components(modulus, exponent)?;
+                let rsa = OsslRsa::from_public_components(modulus, exponent)?;
                 let pkey = PKey::from_rsa(rsa)?.conv_pub();
                 Ok(AsymKey { pkey, scheme })
             },
@@ -841,7 +875,7 @@ impl Signer for TpmCreds {
                 Error::custom(format!("Unexpected signature type: {:?}", sig.algorithm()))),
         };
 
-        let mut buf = self.sign.public.scheme.sig_buf();
+        let mut buf = Signature::empty(self.sign.public.scheme);
         buf.as_mut_slice().write_all(slice)?;
         Ok(buf)
     }
@@ -1203,7 +1237,7 @@ active_pcr_banks = sha256
     }
 
     fn encrypt_decrypt_test(creds: &TpmCreds) -> Result<()> {
-        let expected: [u8; RSA_KEY_BYTES / 2] = rand_array()?;
+        let expected: [u8; Cookie::LEN / 2] = rand_array()?;
         let ct = creds.encrypt(expected.as_slice())?;
         let actual = creds.decrypt(&ct)?;
         if expected.as_slice() == actual {
@@ -1227,18 +1261,6 @@ active_pcr_banks = sha256
         HashcheckTicket::null();
     }
 
-    /// Checks that the value of `TpmCredStore::RSA_KEY_BITS` matches the value of `RSA_KEY_BYTES`.
-    #[test]
-    fn rsa_key_bits_and_key_bytes_compatible() {
-        let bytes = match KeyBuilder::<Sign>::RSA_KEY_BITS {
-            RsaKeyBits::Rsa1024 => 128,
-            RsaKeyBits::Rsa2048 => 256,
-            RsaKeyBits::Rsa3072 => 384,
-            RsaKeyBits::Rsa4096 => 512,
-        };
-        assert_eq!(RSA_KEY_BYTES, bytes)
-    }
-
     #[test]
     fn persistent_handles() -> Result<()> {
         let harness = SwtpmHarness::new()?;

+ 4 - 5
crates/btnode/src/test_helpers.rs

@@ -404,7 +404,7 @@ pub(crate) fn make_writecap_trusted_by<C: Creds>(
         path: make_path_with_owner(next.path.owner.clone(), path_components),
         expires: hour_hence,
         signing_key: trusting_creds.public_sign().clone(),
-        signature: Signature::default(),
+        signature: Signature::empty(Sign::RSA_PSS_3072_SHA_256),
         next: Some(Box::from(next)),
     };
     crypto::sign_writecap(&mut writecap, &trusting_creds)
@@ -436,7 +436,7 @@ pub(crate) fn make_self_signed_writecap_with<C: Creds>(key: &C) -> Result<Writec
         path: make_path_with_owner(root_principal, vec![]),
         expires: hour_hence,
         signing_key: key.public_sign().clone(),
-        signature: Signature::default(),
+        signature: Signature::empty(Sign::RSA_PSS_3072_SHA_256),
         next: None,
     };
     crypto::sign_writecap(&mut writecap, key)
@@ -466,7 +466,7 @@ pub(crate) fn make_block_with(readcap: Readcap) -> Result<Block> {
         readcaps,
         writecap,
         body: Cryptotext::Plain(Vec::from(PAYLOAD)),
-        signature: Signature::Rsa(SIGNATURE)
+        signature: Signature::copy_from(Sign::RSA_PSS_3072_SHA_256, &SIGNATURE)
     })
 }
 
@@ -491,8 +491,7 @@ fn write_rsa_keys_to_file(path: &str) -> Result<()> {
     use openssl::{
         rsa::Rsa,
     };
-    let key_bits = 8 * u32::try_from(RSA_KEY_BYTES).map_err(|e| Error::Message(e.to_string()))?;
-    let rsa = Rsa::generate(key_bits).map_err(convert_err)?;
+    let rsa = Rsa::generate(3072).map_err(convert_err)?;
     let public_der = rsa.public_key_to_der().map_err(convert_err)?;
     let private_der = rsa.private_key_to_der().map_err(convert_err)?;
     let slices = [