Repository: randombit/botan-rs (language: rust)

Issue:
Impossible to generate a DSA or ElGamal key with a randomly generated DL group
AFAICS there is no parameter string to pass to `Privkey::create` that selects a randomly generated DL group.


Discussion / hints:
Oh interesting - we expose this from FFI but *not* in the Rust binding.

Fix patch:
diff --git a/botan/src/pubkey.rs b/botan/src/pubkey.rs
index 39c3cb5..48d738f 100644
--- a/botan/src/pubkey.rs
+++ b/botan/src/pubkey.rs
@@ -45,6 +45,28 @@ impl Privkey {
         Ok(Privkey { obj })
     }
 
+    /// Create a new ElGamal private key with a random group
+    pub fn create_elgamal(
+        p_bits: usize,
+        q_bits: usize,
+        rng: &mut RandomNumberGenerator,
+    ) -> Result<Self> {
+        let obj = botan_init!(botan_privkey_create_elgamal, rng.handle(), p_bits, q_bits)?;
+
+        Ok(Self { obj })
+    }
+
+    /// Create a new DSA private key with a random group
+    pub fn create_dsa(
+        p_bits: usize,
+        q_bits: usize,
+        rng: &mut RandomNumberGenerator,
+    ) -> Result<Self> {
+        let obj = botan_init!(botan_privkey_create_dsa, rng.handle(), p_bits, q_bits)?;
+
+        Ok(Self { obj })
+    }
+
     /// Load an RSA private key (p,q,e)
     ///
     /// # Examples
diff --git a/todo.txt b/todo.txt
index adf8922..67fcc52 100644
--- a/todo.txt
+++ b/todo.txt
@@ -14,7 +14,6 @@ Missing Functions
 These are functions currently exposed by Botan's C API which are not
 exposed by the high level Rust wrapper:
 
-botan_privkey_create_dsa
 botan_privkey_load_sm2
 botan_pubkey_load_sm2
 


Test patch:
diff --git a/botan/tests/tests.rs b/botan/tests/tests.rs
index 1f17c41..7287247 100644
--- a/botan/tests/tests.rs
+++ b/botan/tests/tests.rs
@@ -1028,6 +1028,81 @@ fn test_totp() -> Result<(), botan::Error> {
     Ok(())
 }
 
+#[test]
+fn test_elgamal() -> Result<(), botan::Error> {
+    let mut rng = botan::RandomNumberGenerator::new()?;
+
+    let p_bits = 1024;
+    let q_bits = 256;
+
+    let elg = botan::Privkey::create_elgamal(p_bits, q_bits, &mut rng)?;
+
+    // extract the elements:
+    let p = elg.get_field("p")?;
+    let q = elg.get_field("q")?;
+    let g = elg.get_field("g")?;
+    let x = elg.get_field("x")?;
+    let y = elg.get_field("y")?;
+
+    // check the lengths:
+    assert_eq!(p.bit_count()?, p_bits);
+    assert_eq!(q.bit_count()?, q_bits);
+    assert!(x.bit_count()? <= q_bits);
+    assert!(y.bit_count()? <= p_bits);
+
+    // create a public key:
+    let elgp = botan::Pubkey::load_elgamal(&p, &g, &y)?;
+
+    // encrypt a message:
+    let padding = "PKCS1v15";
+    let ptext = rng.read(16)?;
+    let ctext = elgp.encrypt(&ptext, padding, &mut rng)?;
+
+    // decrypt with the private key:
+    let recovered = elg.decrypt(&ctext, padding)?;
+    assert_eq!(recovered, ptext);
+
+    Ok(())
+}
+
+#[test]
+fn test_dsa() -> Result<(), botan::Error> {
+    let mut rng = botan::RandomNumberGenerator::new()?;
+
+    let p_bits = 1024;
+    let q_bits = 256;
+
+    let dsa = botan::Privkey::create_dsa(p_bits, q_bits, &mut rng)?;
+
+    // extract the elements:
+    let p = dsa.get_field("p")?;
+    let q = dsa.get_field("q")?;
+    let g = dsa.get_field("g")?;
+    let x = dsa.get_field("x")?;
+    let y = dsa.get_field("y")?;
+
+    // check the lengths:
+    assert_eq!(p.bit_count()?, p_bits);
+    assert_eq!(q.bit_count()?, q_bits);
+    assert!(x.bit_count()? <= q_bits);
+    assert!(y.bit_count()? <= p_bits);
+
+    // create a public key:
+    let dsap = botan::Pubkey::load_dsa(&p, &q, &g, &y)?;
+
+    // sign a message:
+    let padding = "EMSA1(SHA-256)";
+    let message = rng.read(16)?;
+
+    let signature = dsa.sign(&message, &padding, &mut rng)?;
+
+    // verify the signature:
+
+    assert!(dsap.verify(&message, &signature, &padding)?);
+
+    Ok(())
+}
+
 #[cfg(feature = "botan3")]
 #[test]
 fn test_zfec() -> Result<(), botan::Error> {