diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7f75341..dafa333 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -26,10 +26,10 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: ${{ matrix.toolchain }} + - name: Rust Toolchain + run: | + rustup toolchain install ${{ matrix.toolchain }} --profile minimal --no-self-update + rustup default ${{ matrix.toolchain }} - name: Install vcpkg packages if: matrix.os == 'windows-latest' @@ -41,6 +41,20 @@ jobs: manifest-dir: ${{ github.workspace }}/.github/manifest github-binarycache: true + - name: Install lair-keystore-0.4.4 for fwd compat test Windows + if: matrix.os == 'windows-latest' + run: |- + $env:SODIUM_LIB_DIR="$(pwd)\vcpkg\packages\libsodium_x64-windows-release\lib" + cargo install lair_keystore@0.4.4 --debug + + - name: Install lair-keystore-0.4.4 for fwd compat test + if: matrix.os != 'windows-latest' + run: cargo install lair_keystore@0.4.4 --debug + + - name: Rename lair-keystore to include version + shell: bash + run: mv $(which lair-keystore){,-0.4.4} + - name: Make Test Windows if: matrix.os == 'windows-latest' run: |- diff --git a/crates/lair_keystore/tests/integration.rs b/crates/lair_keystore/tests/integration.rs new file mode 100644 index 0000000..8cb1237 --- /dev/null +++ b/crates/lair_keystore/tests/integration.rs @@ -0,0 +1,6 @@ +mod tests { + mod common; + mod fwd_compat_0_4_4; + mod migrate_unencrypted; + mod server_test; +} diff --git a/crates/lair_keystore/tests/common.rs b/crates/lair_keystore/tests/tests/common.rs similarity index 100% rename from crates/lair_keystore/tests/common.rs rename to crates/lair_keystore/tests/tests/common.rs diff --git a/crates/lair_keystore/tests/tests/fwd_compat_0_4_4.rs b/crates/lair_keystore/tests/tests/fwd_compat_0_4_4.rs new file mode 100644 index 0000000..3075a86 --- /dev/null +++ b/crates/lair_keystore/tests/tests/fwd_compat_0_4_4.rs @@ -0,0 +1,230 @@ +use lair_keystore::dependencies::*; +use lair_keystore_api::prelude::*; +use std::sync::Arc; +use tokio::io::{AsyncBufReadExt, AsyncWriteExt}; + +#[cfg(not(windows))] +const NAME: &str = "lair-keystore-0.4.4"; + +#[cfg(windows)] +const NAME: &str = "lair-keystore-0.4.4.exe"; + +const PASSPHRASE: &[u8] = b"passphrase"; + +const TAG1: &str = "TAG1"; +const TAG2: &str = "TAG2"; + +#[tokio::test(flavor = "multi_thread")] +async fn fwd_compat_0_4_4() { + let tmpdir = tempdir::TempDir::new("lair_fwd_044").unwrap(); + + println!("{tmpdir:?}"); + + // -- make sure we have the correct 0.4.4 version avaliable -- // + + let mut cmd = tokio::process::Command::new(NAME); + + cmd.kill_on_drop(true).arg("--version"); + + eprintln!("{cmd:?}"); + + let ver = cmd + .output() + .await + .expect("please ensure above command is on the PATH"); + + assert!(ver.status.success()); + assert_eq!(b"lair_keystore 0.4.4\n", ver.stdout.as_slice()); + + // -- initialize the 0.4.4 keystore -- // + + let mut cmd = tokio::process::Command::new(NAME); + + cmd.kill_on_drop(true) + .arg("--lair-root") + .arg(tmpdir.path()) + .arg("init") + .arg("--piped") + .stdin(std::process::Stdio::piped()); + + eprintln!("{cmd:?}"); + + let mut init = cmd.spawn().unwrap(); + let mut stdin = init.stdin.take().unwrap(); + stdin.write_all(PASSPHRASE).await.unwrap(); + stdin.shutdown().await.unwrap(); + drop(stdin); + + let init = init.wait_with_output().await.unwrap(); + + assert!(init.status.success()); + println!("{}", String::from_utf8_lossy(init.stdout.as_slice())); + + // -- fetch the connection string -- // + + let mut cmd = tokio::process::Command::new(NAME); + + cmd.kill_on_drop(true) + .arg("--lair-root") + .arg(tmpdir.path()) + .arg("url"); + + eprintln!("{cmd:?}"); + + let s_url = cmd + .output() + .await + .expect("please ensure above command is on the PATH"); + + assert!(s_url.status.success()); + let s_url = String::from_utf8_lossy(s_url.stdout.as_slice()).to_string(); + let s_url = url::Url::parse(&s_url).unwrap(); + + println!("s_url: {s_url}"); + + // -- run the actual server -- // + + let mut cmd = tokio::process::Command::new(NAME); + + cmd.kill_on_drop(true) + .arg("--lair-root") + .arg(tmpdir.path()) + .arg("server") + .arg("--piped") + .stdin(std::process::Stdio::piped()) + .stdout(std::process::Stdio::piped()); + + eprintln!("{cmd:?}"); + + let mut server = cmd.spawn().unwrap(); + let mut stdin = server.stdin.take().unwrap(); + stdin.write_all(PASSPHRASE).await.unwrap(); + stdin.shutdown().await.unwrap(); + drop(stdin); + + let mut server_lines = + tokio::io::BufReader::new(server.stdout.take().unwrap()).lines(); + + tokio::time::timeout(std::time::Duration::from_secs(10), async { + loop { + let line = server_lines.next_line().await.unwrap().unwrap(); + println!("-:=:- {line}"); + if line.contains("lair-keystore running") { + break; + } + } + }) + .await + .unwrap(); + + // -- connect a client and insert data into the store -- // + + let client044 = lair_keystore_api::ipc_keystore::ipc_keystore_connect( + s_url.clone(), + PASSPHRASE, + ) + .await + .unwrap(); + + let _seed_info_ref = + client044.new_seed(TAG1.into(), None, true).await.unwrap(); + + let _wka_cert = client044.new_wka_tls_cert(TAG2.into()).await.unwrap(); + + // -- shut down the 044 client and server -- // + + client044.shutdown().await.unwrap(); + drop(client044); + + // give windows a chance to sync to disk + tokio::time::sleep(std::time::Duration::from_secs(5)).await; + + server.kill().await.unwrap(); + drop(server); + + // -- run the new server using the 044 store -- // + + let mut config_path = tmpdir.path().to_owned(); + config_path.push("lair-keystore-config.yaml"); + let config = tokio::fs::read(&config_path).await.unwrap(); + + println!("{}", String::from_utf8_lossy(&config)); + + let config = LairServerConfigInner::from_bytes(&config).unwrap(); + + lair_keystore::server::StandaloneServer::new(Arc::new(config)) + .await + .unwrap() + .run(PASSPHRASE) + .await + .unwrap(); + + // -- connect a client to the new server and check functionality -- // + + let client = lair_keystore_api::ipc_keystore::ipc_keystore_connect( + s_url.clone(), + PASSPHRASE, + ) + .await + .unwrap(); + + let entry_list = client.list_entries().await.unwrap(); + + assert_eq!(2, entry_list.len()); + + for entry in entry_list { + match entry { + LairEntryInfo::Seed { tag, .. } => { + assert_eq!(TAG1, &*tag); + } + LairEntryInfo::WkaTlsCert { tag, .. } => { + assert_eq!(TAG2, &*tag); + } + oth => panic!("unexpected: {:?}", oth), + } + } + + let entry = match client.get_entry(TAG1.into()).await.unwrap() { + LairEntryInfo::Seed { seed_info, .. } => seed_info, + _ => panic!(), + }; + + let sig = client + .sign_by_pub_key( + entry.ed25519_pub_key.clone(), + None, + b"hello".to_vec().into(), + ) + .await + .unwrap(); + assert!(entry + .ed25519_pub_key + .verify_detached(sig, &b"hello"[..]) + .await + .unwrap()); + + // secretbox encrypt some data + let (nonce, cipher) = client + .secretbox_xsalsa_by_tag(TAG1.into(), None, b"hello".to_vec().into()) + .await + .unwrap(); + + // make sure we can decrypt our own message + let msg = client + .secretbox_xsalsa_open_by_tag(TAG1.into(), None, nonce, cipher) + .await + .unwrap(); + + assert_eq!(b"hello", &*msg); + + // try exporting the seed (just to ourselves) + let _ = client + .export_seed_by_tag( + TAG1.into(), + entry.x25519_pub_key.clone(), + entry.x25519_pub_key.clone(), + None, + ) + .await + .unwrap(); +} diff --git a/crates/lair_keystore/tests/migrate_unencrypted.rs b/crates/lair_keystore/tests/tests/migrate_unencrypted.rs similarity index 95% rename from crates/lair_keystore/tests/migrate_unencrypted.rs rename to crates/lair_keystore/tests/tests/migrate_unencrypted.rs index 6735f65..7ed5040 100644 --- a/crates/lair_keystore/tests/migrate_unencrypted.rs +++ b/crates/lair_keystore/tests/tests/migrate_unencrypted.rs @@ -1,8 +1,6 @@ -use common::{connect_with_config, create_config}; +use super::common::{connect_with_config, create_config}; use lair_keystore_api::dependencies::{sodoken, tokio}; -mod common; - #[cfg(not(windows))] // No encryption on Windows, ignore this test #[tokio::test(flavor = "multi_thread")] async fn migrate_unencrypted() { diff --git a/crates/lair_keystore/tests/server_test.rs b/crates/lair_keystore/tests/tests/server_test.rs similarity index 99% rename from crates/lair_keystore/tests/server_test.rs rename to crates/lair_keystore/tests/tests/server_test.rs index bae44f9..ff8c9d9 100644 --- a/crates/lair_keystore/tests/server_test.rs +++ b/crates/lair_keystore/tests/tests/server_test.rs @@ -1,9 +1,7 @@ -use common::connect; +use super::common::connect; use lair_keystore::dependencies::*; use lair_keystore_api::prelude::*; -mod common; - #[tokio::test(flavor = "multi_thread")] async fn server_test_happy_path() { let tmpdir = tempdir::TempDir::new("lair keystore test").unwrap();