Support bookworm openssl in Key
This commit is contained in:
parent
988a3b0fe7
commit
c41ad54c37
4 changed files with 272 additions and 63 deletions
|
|
@ -19,3 +19,4 @@ nonzero_ext = "0.3.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3"
|
tempfile = "3"
|
||||||
|
mockall = "0.11.4"
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
#[cfg(test)]
|
||||||
|
use mockall::mock;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::ffi::OsStr;
|
use std::ffi::OsStr;
|
||||||
use std::io::Result as IoResult;
|
use std::io::Result as IoResult;
|
||||||
|
|
@ -33,30 +35,57 @@ pub fn get_output(output: Output) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
pub trait CommandRunner {
|
pub trait CommandRunner {
|
||||||
async fn run(&self, program: &str, args: &[&OsStr], stdin: &[u8]) -> IoResult<Output>;
|
async fn run<'a>(&self, program: &str, args: &'a [&'a OsStr], input: &[u8]) -> IoResult<Output>;
|
||||||
|
|
||||||
async fn run_with_args(&self, program: &str, args: &[&OsStr]) -> IoResult<Output> {
|
async fn run_with_args<'a>(&self, program: &str, args: &'a [&'a OsStr]) -> IoResult<Output> {
|
||||||
self.run(program, args, b"").await
|
self.run(program, args, b"").await
|
||||||
}
|
}
|
||||||
async fn get_output(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
async fn get_output<'a>(
|
||||||
|
&self,
|
||||||
|
program: &str,
|
||||||
|
args: &'a [&'a OsStr],
|
||||||
|
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
let output = self.run_with_args(program, args).await?;
|
let output = self.run_with_args(program, args).await?;
|
||||||
get_output(output)
|
get_output(output)
|
||||||
}
|
}
|
||||||
async fn run_successfully(&self, program: &str, args: &[&OsStr]) -> Result<(), Box<dyn Error>> {
|
async fn run_successfully<'a>(
|
||||||
|
&self,
|
||||||
|
program: &str,
|
||||||
|
args: &'a [&'a OsStr],
|
||||||
|
) -> Result<(), Box<dyn Error>> {
|
||||||
is_success(self.run(program, args, b"").await)?;
|
is_success(self.run(program, args, b"").await)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
async fn get_stderr(&self, program: &str, args: &[&OsStr]) -> Result<Vec<u8>, Box<dyn Error>> {
|
async fn get_stderr<'a>(
|
||||||
|
&self,
|
||||||
|
program: &str,
|
||||||
|
args: &'a [&'a OsStr],
|
||||||
|
) -> Result<Vec<u8>, Box<dyn Error>> {
|
||||||
Ok(is_success(self.run_with_args(program, args).await)?.stderr)
|
Ok(is_success(self.run_with_args(program, args).await)?.stderr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mock! {
|
||||||
|
pub CommandRunner {
|
||||||
|
}
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl CommandRunner for CommandRunner {
|
||||||
|
async fn run<'a>(&self, program: &str, args: &'a [&'a OsStr], input: &[u8]) -> IoResult<Output>;
|
||||||
|
|
||||||
|
async fn run_with_args<'a>(&self, program: &str, args: &'a [&'a OsStr]) -> IoResult<Output>;
|
||||||
|
async fn get_output<'a>(&self, program: &str, args: &'a [&'a OsStr]) -> Result<Vec<u8>, Box<dyn Error>>;
|
||||||
|
async fn run_successfully<'a>(&self, program: &str, args: &'a [&'a OsStr]) -> Result<(), Box<dyn Error>>;
|
||||||
|
async fn get_stderr<'a>(&self, program: &str, args: &'a [&'a OsStr]) -> Result<Vec<u8>, Box<dyn Error>>;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct StdCommandRunner;
|
pub struct StdCommandRunner;
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl CommandRunner for StdCommandRunner {
|
impl CommandRunner for StdCommandRunner {
|
||||||
async fn run(&self, program: &str, args: &[&OsStr], input: &[u8]) -> IoResult<Output> {
|
async fn run<'a>(&self, program: &str, args: &'a [&'a OsStr], input: &[u8]) -> IoResult<Output> {
|
||||||
//println!("{} {:?}", program, args);
|
//println!("{} {:?}", program, args);
|
||||||
let mut child = Command::new(program)
|
let mut child = Command::new(program)
|
||||||
.args(args)
|
.args(args)
|
||||||
|
|
@ -114,7 +143,7 @@ impl Drop for TempSetEnv<'_> {
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl<U: AsRef<str>> CommandRunner for SetuidCommandRunner<U> {
|
impl<U: AsRef<str>> CommandRunner for SetuidCommandRunner<U> {
|
||||||
async fn run(&self, program: &str, args: &[&OsStr], input: &[u8]) -> IoResult<Output> {
|
async fn run<'a>(&self, program: &str, args: &'a [&'a OsStr], input: &[u8]) -> IoResult<Output> {
|
||||||
let uid = get_user_by_name(self.user_name.as_ref())
|
let uid = get_user_by_name(self.user_name.as_ref())
|
||||||
.expect("User does not exist")
|
.expect("User does not exist")
|
||||||
.uid();
|
.uid();
|
||||||
|
|
|
||||||
|
|
@ -92,63 +92,30 @@ impl<_C: CommandRunner, C: Borrow<_C>, P: AsRef<Path>, S: AsRef<str>, B: AsRef<s
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::Checkout;
|
use super::{Checkout, Symbol};
|
||||||
use crate::async_utils::run;
|
use crate::async_utils::run;
|
||||||
use crate::async_utils::sleep;
|
use crate::command_runner::MockCommandRunner;
|
||||||
use crate::command_runner::CommandRunner;
|
use mockall::Sequence;
|
||||||
use crate::symbols::Symbol;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::ffi::{OsStr, OsString};
|
|
||||||
use std::io::Result as IoResult;
|
|
||||||
use std::os::unix::process::ExitStatusExt;
|
|
||||||
use std::process::{ExitStatus, Output};
|
|
||||||
use std::time::{Duration, Instant};
|
|
||||||
|
|
||||||
struct DummyCommandRunner {
|
|
||||||
pub args: RefCell<Vec<Vec<OsString>>>,
|
|
||||||
}
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl CommandRunner for DummyCommandRunner {
|
|
||||||
async fn run(&self, program: &str, args: &[&OsStr], stdin: &[u8]) -> IoResult<Output> {
|
|
||||||
assert_eq!(program, "git");
|
|
||||||
assert_eq!(stdin, b"");
|
|
||||||
sleep(Duration::from_millis(50)).await;
|
|
||||||
self
|
|
||||||
.args
|
|
||||||
.borrow_mut()
|
|
||||||
.push(args.iter().map(|a| a.to_os_string()).collect());
|
|
||||||
Ok(Output {
|
|
||||||
status: ExitStatus::from_raw(0),
|
|
||||||
stdout: vec![],
|
|
||||||
stderr: vec![],
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test() {
|
fn test() {
|
||||||
let c = DummyCommandRunner {
|
let mut seq = Sequence::new();
|
||||||
args: RefCell::new(vec![]),
|
let mut c = MockCommandRunner::new();
|
||||||
};
|
c.expect_get_output()
|
||||||
let checkout: Checkout<DummyCommandRunner, _, _, _, _> =
|
.times(1)
|
||||||
Checkout::new(".", "source", "branch", &c);
|
.withf(|cmd, args| cmd == "git" && args == ["-C", ".", "rev-list", "-1", "HEAD"])
|
||||||
let start = Instant::now();
|
.returning(|_, _| Ok("".into()));
|
||||||
|
c.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.in_sequence(&mut seq)
|
||||||
|
.withf(|cmd, args| cmd == "git" && args == ["-C", ".", "fetch", "source", "branch"])
|
||||||
|
.returning(|_, _| Ok("".into()));
|
||||||
|
c.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.in_sequence(&mut seq)
|
||||||
|
.withf(|cmd, args| cmd == "git" && args == ["-C", ".", "rev-list", "-1", "FETCH_HEAD"])
|
||||||
|
.returning(|_, _| Ok("".into()));
|
||||||
|
let checkout = Checkout::new(".", "source", "branch", c);
|
||||||
assert!(run(checkout.target_reached()).unwrap());
|
assert!(run(checkout.target_reached()).unwrap());
|
||||||
let end = Instant::now();
|
|
||||||
let mut args = c.args.into_inner();
|
|
||||||
let first_two_args = &mut args[0..2];
|
|
||||||
first_two_args.sort_unstable();
|
|
||||||
assert_eq!(
|
|
||||||
first_two_args,
|
|
||||||
[
|
|
||||||
["-C", ".", "fetch", "source", "branch"],
|
|
||||||
["-C", ".", "rev-list", "-1", "HEAD"],
|
|
||||||
]
|
|
||||||
);
|
|
||||||
assert_eq!(args[2], ["-C", ".", "rev-list", "-1", "FETCH_HEAD"]);
|
|
||||||
|
|
||||||
assert!((end - start).as_millis() >= 100);
|
|
||||||
assert!((end - start).as_millis() < 150);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
let stdout = self
|
let mut stdout = self
|
||||||
.command_runner
|
.command_runner
|
||||||
.get_output(
|
.get_output(
|
||||||
"openssl",
|
"openssl",
|
||||||
|
|
@ -44,9 +44,12 @@ impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
if stdout.starts_with("RSA ".as_ref()) {
|
||||||
|
stdout = stdout.split_off(4);
|
||||||
|
}
|
||||||
Ok(
|
Ok(
|
||||||
stdout.ends_with(b"RSA key ok\n")
|
stdout.ends_with(b"RSA key ok\n")
|
||||||
&& stdout.starts_with(format!("RSA Private-Key: ({} bit, 2 primes)\n", self.bits).as_ref()),
|
&& stdout.starts_with(format!("Private-Key: ({} bit, 2 primes)\n", self.bits).as_ref()),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -67,4 +70,213 @@ impl<C: CommandRunner, P: AsRef<Path>> Symbol for Key<C, P> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {}
|
mod test {
|
||||||
|
use super::{Key, Symbol};
|
||||||
|
use crate::async_utils::run;
|
||||||
|
use crate::command_runner::MockCommandRunner;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bookworm_success() {
|
||||||
|
let mut command_runner = MockCommandRunner::new();
|
||||||
|
command_runner
|
||||||
|
.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.returning(|_, _| {
|
||||||
|
Ok(
|
||||||
|
"Private-Key: (4096 bit, 2 primes)
|
||||||
|
modulus:
|
||||||
|
00:...
|
||||||
|
publicExponent: 65537 (0x10001)
|
||||||
|
privateExponent:
|
||||||
|
57:...
|
||||||
|
prime1:
|
||||||
|
00:...
|
||||||
|
prime2:
|
||||||
|
00:...
|
||||||
|
exponent1:
|
||||||
|
2b:...
|
||||||
|
exponent2:
|
||||||
|
0e:...
|
||||||
|
coefficient:
|
||||||
|
43:...
|
||||||
|
RSA key ok
|
||||||
|
"
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let symbol = Key::new(command_runner, "/"); // FIXME: Has to be an existing file
|
||||||
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bookworm_short_key() {
|
||||||
|
let mut command_runner = MockCommandRunner::new();
|
||||||
|
command_runner
|
||||||
|
.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.returning(|_, _| {
|
||||||
|
Ok(
|
||||||
|
"Private-Key: (2048 bit, 2 primes)
|
||||||
|
modulus:
|
||||||
|
00:...
|
||||||
|
publicExponent: 65537 (0x10001)
|
||||||
|
privateExponent:
|
||||||
|
57:...
|
||||||
|
prime1:
|
||||||
|
00:...
|
||||||
|
prime2:
|
||||||
|
00:...
|
||||||
|
exponent1:
|
||||||
|
2b:...
|
||||||
|
exponent2:
|
||||||
|
0e:...
|
||||||
|
coefficient:
|
||||||
|
43:...
|
||||||
|
RSA key ok
|
||||||
|
"
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let symbol = Key::new(command_runner, "/"); // FIXME: Has to be an existing file
|
||||||
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bookworm_broken_key() {
|
||||||
|
let mut command_runner = MockCommandRunner::new();
|
||||||
|
command_runner.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.returning(|_, _| Ok("Private-Key: (4096 bit, 2 primes)
|
||||||
|
modulus:
|
||||||
|
00:...
|
||||||
|
publicExponent: 65537 (0x10001)
|
||||||
|
privateExponent:
|
||||||
|
57:...
|
||||||
|
prime1:
|
||||||
|
00:...
|
||||||
|
prime2:
|
||||||
|
00:...
|
||||||
|
exponent1:
|
||||||
|
2b:...
|
||||||
|
exponent2:
|
||||||
|
0e:...
|
||||||
|
coefficient:
|
||||||
|
43:...
|
||||||
|
RSA key not ok
|
||||||
|
40C782E5E77F0000:error:0200007E:rsa routines:rsa_validate_keypair_multiprime:iqmp not inverse of q:../crypto/rsa/rsa_chk.c:196:
|
||||||
|
".into()));
|
||||||
|
let symbol = Key::new(command_runner, "/"); // FIXME: Has to be an existing file
|
||||||
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bullseye_success() {
|
||||||
|
let mut command_runner = MockCommandRunner::new();
|
||||||
|
command_runner
|
||||||
|
.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.returning(|_, _| {
|
||||||
|
Ok(
|
||||||
|
"RSA Private-Key: (4096 bit, 2 primes)
|
||||||
|
modulus:
|
||||||
|
00:...
|
||||||
|
publicExponent: 65537 (0x10001)
|
||||||
|
privateExponent:
|
||||||
|
57:...
|
||||||
|
prime1:
|
||||||
|
00:...
|
||||||
|
prime2:
|
||||||
|
00:...
|
||||||
|
exponent1:
|
||||||
|
2b:...
|
||||||
|
exponent2:
|
||||||
|
0e:...
|
||||||
|
coefficient:
|
||||||
|
43:...
|
||||||
|
RSA key ok
|
||||||
|
"
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let symbol = Key::new(command_runner, "/"); // FIXME: Has to be an existing file
|
||||||
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bullseye_short_key() {
|
||||||
|
let mut command_runner = MockCommandRunner::new();
|
||||||
|
command_runner
|
||||||
|
.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.returning(|_, _| {
|
||||||
|
Ok(
|
||||||
|
"RSA Private-Key: (2048 bit, 2 primes)
|
||||||
|
modulus:
|
||||||
|
00:...
|
||||||
|
publicExponent: 65537 (0x10001)
|
||||||
|
privateExponent:
|
||||||
|
57:...
|
||||||
|
prime1:
|
||||||
|
00:...
|
||||||
|
prime2:
|
||||||
|
00:...
|
||||||
|
exponent1:
|
||||||
|
2b:...
|
||||||
|
exponent2:
|
||||||
|
0e:...
|
||||||
|
coefficient:
|
||||||
|
43:...
|
||||||
|
RSA key ok
|
||||||
|
"
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let symbol = Key::new(command_runner, "/"); // FIXME: Has to be an existing file
|
||||||
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bullseye_broken_key() {
|
||||||
|
let mut command_runner = MockCommandRunner::new();
|
||||||
|
command_runner
|
||||||
|
.expect_get_output()
|
||||||
|
.times(1)
|
||||||
|
.returning(|_, _| {
|
||||||
|
Ok(
|
||||||
|
"RSA Private-Key: (4096 bit, 2 primes)
|
||||||
|
modulus:
|
||||||
|
00:...
|
||||||
|
publicExponent: 65537 (0x10001)
|
||||||
|
privateExponent:
|
||||||
|
57:...
|
||||||
|
prime1:
|
||||||
|
00:...
|
||||||
|
prime2:
|
||||||
|
00:...
|
||||||
|
exponent1:
|
||||||
|
2b:...
|
||||||
|
exponent2:
|
||||||
|
0e:...
|
||||||
|
coefficient:
|
||||||
|
43:...
|
||||||
|
RSA key error: iqmp not inverse of q
|
||||||
|
"
|
||||||
|
.into(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let symbol = Key::new(command_runner, "/"); // FIXME: Has to be an existing file
|
||||||
|
run(async {
|
||||||
|
assert_eq!(symbol.target_reached().await.unwrap(), false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue